import os
import threading

from flask import Flask, jsonify
from werkzeug.exceptions import RequestEntityTooLarge

from shared.config.config import (
    APP_HOST,
    APP_PORT,
    FLASK_SECRET_KEY,
    INSTANT_CLIENT_DIR,
    JOB_RETENTION_DAYS,
    MAX_UPLOAD_BYTES,
    MODEL_DEFAULT,
    logger,
)
from shared.db.oracle import oracle_thick_mode_requested
from shared.db.sqlite import cleanup_old_jobs, init_db, mark_running_jobs_interrupted
from shared.health import get_api_health_report, get_health_report
from modules.diagnostics.routes import diagnostics_bp
from modules.detection.file_routes import file_bp
from modules.detection.job_routes import job_bp
from modules.detection.upload_routes import upload_bp
from modules.dispatch.routes import dispatch_bp
from modules.face.routes import face_bp
from modules.training.routes import train_bp
from modules.score import score_bp
from modules.profile import profile_bp
from modules.dashboard import dashboard_bp
from modules.graph import graph_bp
from modules.dashboard.page_routes import dashboard_page_bp
from modules.profile.page_routes import profile_page_bp
from modules.graph.page_routes import graph_page_bp
from modules.ai_analyst import ai_analyst_bp
from modules.ai_analyst.page_routes import ai_analyst_page_bp
from modules.workbench.page_routes import workbench_page_bp
from modules.dashboard.services.alert_rule_engine import run_all_rules
from shared.inference.infer_service import get_model


def _seed_alerts_async() -> None:
    if os.getenv("WCNR_SEED_ALERTS_ON_START", "true").lower() not in {"1", "true", "yes"}:
        return
    try:
        result = run_all_rules()
        logger.info("Alert seed on start: %s", result)
    except Exception as exc:
        logger.warning("Alert seed on start failed: %s", exc)


def _start_alert_seed_thread() -> None:
    threading.Thread(target=_seed_alerts_async, name="alert-seed", daemon=True).start()


def create_app() -> Flask:
    init_db()
    _start_alert_seed_thread()
    app = Flask(__name__)
    app.secret_key = FLASK_SECRET_KEY
    app.config["MAX_CONTENT_LENGTH"] = MAX_UPLOAD_BYTES

    @app.get("/healthz")
    def healthz():
        report = get_health_report()
        return jsonify(report), (200 if report.get("ok") else 503)

    @app.get("/api/health")
    def api_health():
        return jsonify(get_api_health_report()), 200

    @app.get("/livez")
    def livez():
        return jsonify({"ok": True, "service": "multi-rider"}), 200

    @app.errorhandler(RequestEntityTooLarge)
    def handle_file_too_large(exc):
        limit_mb = max(1, MAX_UPLOAD_BYTES // (1024 * 1024))
        logger.warning("Request too large: %s", exc)
        return (
            jsonify(
                {
                    "ok": False,
                    "error": f"上传文件过大，当前上限约为 {limit_mb} MB，请压缩后重试或调大 MAX_UPLOAD_BYTES。",
                    "code": 413,
                }
            ),
            413,
        )

    app.register_blueprint(job_bp)
    app.register_blueprint(file_bp)
    app.register_blueprint(upload_bp)
    app.register_blueprint(face_bp)
    app.register_blueprint(train_bp)
    app.register_blueprint(dispatch_bp)
    app.register_blueprint(diagnostics_bp)
    app.register_blueprint(score_bp)
    app.register_blueprint(profile_bp)
    app.register_blueprint(dashboard_bp)
    app.register_blueprint(graph_bp)
    app.register_blueprint(dashboard_page_bp)
    app.register_blueprint(profile_page_bp)
    app.register_blueprint(graph_page_bp)
    app.register_blueprint(ai_analyst_bp)
    app.register_blueprint(ai_analyst_page_bp)
    app.register_blueprint(workbench_page_bp)
    if os.environ.get("WCNR_SCHEDULER_ENABLED", "1") == "1":
        from shared.scheduler import start_scheduler

        start_scheduler(app)
    return app


def bootstrap_app() -> None:
    init_db()
    if JOB_RETENTION_DAYS > 0:
        deleted = cleanup_old_jobs(JOB_RETENTION_DAYS)
        if deleted:
            logger.info("cleaned up %s old detection jobs older than %s days", deleted, JOB_RETENTION_DAYS)
    else:
        logger.info("job retention cleanup disabled; saved detection history is persistent")
    mark_running_jobs_interrupted()

    if oracle_thick_mode_requested() and not os.path.isdir(INSTANT_CLIENT_DIR):
        logger.warning("oracle thick mode requested but instantclient directory not found: %s", INSTANT_CLIENT_DIR)

    try:
        get_model(MODEL_DEFAULT)
        logger.info("Model preloaded: %s", MODEL_DEFAULT)
    except Exception as exc:
        logger.warning("Model preload failed for %s: %s", MODEL_DEFAULT, exc)


app = create_app()


def main() -> None:
    bootstrap_app()
    app.run(host=APP_HOST, port=APP_PORT, debug=False)


if __name__ == "__main__":
    main()
