import logging
import sys
import os

from dotenv import load_dotenv
from flask import Flask, request, jsonify

from api.admin_api import admin_api
from api.auth_api import auth_api
from api.child_api import child_api
from api.child_override_api import child_override_api
from api.chore_api import chore_api
from api.chore_schedule_api import chore_schedule_api
from api.image_api import image_api
from api.kindness_api import kindness_api
from api.penalty_api import penalty_api
from api.reward_api import reward_api
from api.routine_api import routine_api
from api.routine_item_api import routine_item_api
from api.child_routine_api import child_routine_api
from api.routine_schedule_api import routine_schedule_api
from api.task_api import task_api
from api.tracking_api import tracking_api
from api.user_api import user_api
from api.push_subscription_api import push_subscription_api
from api.digest_action_api import digest_action_api
from config.version import get_full_version

from db.default import initializeImages, createDefaultTasks, createDefaultRewards
from db.db import ensure_mongodb_indexes
from events.broadcaster import Broadcaster
from events.sse import sse_response_for_user, send_to_user
from api.utils import get_current_user_id
from utils.account_deletion_scheduler import start_deletion_scheduler
from utils.chore_expiry_notification_scheduler import start_chore_expiry_notification_scheduler
from utils.digest_scheduler import start_digest_scheduler
from utils.state_expiry_scheduler import start_state_expiry_scheduler


# Load environment variables
load_dotenv()

# Ensure MongoDB indexes exist when running against MongoDB.
ensure_mongodb_indexes()

# Configure logging once at application startup
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S',
    stream=sys.stdout,
    force=True  # Override any existing config
)
logger = logging.getLogger(__name__)

logging.getLogger("werkzeug").setLevel(logging.WARNING)
logging.getLogger("events.sse").setLevel(logging.WARNING)

app = Flask(__name__)
#CORS(app, resources={r"/api/*": {"origins": ["http://localhost:3000", "http://localhost:5173"]}})
#Todo - add prefix to all these routes instead of in each blueprint
app.register_blueprint(admin_api)
app.register_blueprint(child_api)
app.register_blueprint(child_override_api)
app.register_blueprint(chore_api)
app.register_blueprint(chore_schedule_api)
app.register_blueprint(kindness_api)
app.register_blueprint(penalty_api)
app.register_blueprint(reward_api)
app.register_blueprint(routine_api)
app.register_blueprint(routine_item_api)
app.register_blueprint(child_routine_api)
app.register_blueprint(routine_schedule_api)
app.register_blueprint(task_api)
app.register_blueprint(image_api)
app.register_blueprint(auth_api, url_prefix='/auth')
app.register_blueprint(user_api)
app.register_blueprint(tracking_api)
app.register_blueprint(push_subscription_api)
app.register_blueprint(digest_action_api)

app.config.update(
    MAIL_SERVER='smtp.gmail.com',
    MAIL_PORT=587,
    MAIL_USE_TLS=True,
    MAIL_USERNAME='ryan.kegel@gmail.com',
    MAIL_PASSWORD='ruyj hxjf nmrz buar',
    MAIL_DEFAULT_SENDER='ryan.kegel@gmail.com',
    FRONTEND_URL=os.environ.get('FRONTEND_URL', 'https://localhost:5173'),  # Dynamic via env var, defaults to localhost
    VAPID_CLAIMS_EMAIL=os.environ.get('VAPID_CLAIMS_EMAIL', 'admin@reward-app.local'),
)

# Security: require SECRET_KEY and REFRESH_TOKEN_EXPIRY_DAYS from environment
_secret_key = os.environ.get('SECRET_KEY')
if not _secret_key:
    raise RuntimeError(
        'SECRET_KEY environment variable is required. '
        'Set it to a random string (e.g. python -c "import secrets; print(secrets.token_urlsafe(64))")')
app.config['SECRET_KEY'] = _secret_key

_refresh_expiry = os.environ.get('REFRESH_TOKEN_EXPIRY_DAYS')
if not _refresh_expiry:
    raise RuntimeError('REFRESH_TOKEN_EXPIRY_DAYS environment variable is required (e.g. 90).')
try:
    app.config['REFRESH_TOKEN_EXPIRY_DAYS'] = int(_refresh_expiry)
except ValueError:
    raise RuntimeError('REFRESH_TOKEN_EXPIRY_DAYS must be an integer.')

_digest_token_secret = os.environ.get('DIGEST_TOKEN_SECRET')
if not _digest_token_secret:
    raise RuntimeError(
        'DIGEST_TOKEN_SECRET environment variable is required. '
        'Set it to a random string (e.g. python -c "import secrets; print(secrets.token_urlsafe(64))")')
app.config['DIGEST_TOKEN_SECRET'] = _digest_token_secret

_vapid_public_key = os.environ.get('VAPID_PUBLIC_KEY')
_vapid_private_key = os.environ.get('VAPID_PRIVATE_KEY')
if not _vapid_public_key or not _vapid_private_key:
    raise RuntimeError(
        'VAPID_PUBLIC_KEY and VAPID_PRIVATE_KEY environment variables are required. '
        'Generate a key pair with: python -c "from py_vapid import Vapid; v=Vapid(); '
        'v.generate_keys(); print(v.public_key.public_bytes_raw().hex(), v.private_key.private_bytes_raw().hex())"'
    )
app.config['VAPID_PUBLIC_KEY'] = _vapid_public_key
app.config['VAPID_PRIVATE_KEY'] = _vapid_private_key

@app.route("/version")
def api_version():
    return jsonify({"version": get_full_version()})

@app.route("/events")
def events():
    user_id = get_current_user_id()
    if not user_id:
        return {"error": "Authentication required"}, 401

    return sse_response_for_user(user_id)


@app.route("/notify/<user_id>")
def notify_user(user_id):
    # Example trigger
    send_to_user(user_id, {
        "type": "notification",
        "message": f"Hello {user_id}, this is a private message!"
    })

    return {"status": "sent"}

def start_background_threads():
    broadcaster = Broadcaster()
    broadcaster.daemon = True
    broadcaster.start()

initializeImages()
createDefaultTasks()
createDefaultRewards()
start_background_threads()
start_deletion_scheduler()
start_digest_scheduler(app)
start_state_expiry_scheduler(app)
start_chore_expiry_notification_scheduler(app)

if __name__ == '__main__':
    app.run(debug=False, host='0.0.0.0', port=5000, threaded=True)