"""OKX 期权复盘模块:Flask 路由注册(含对冲计划级复盘)."""
from __future__ import annotations

import os
from typing import Any

from flask import Flask, jsonify, request, send_file
from jinja2 import ChoiceLoader, FileSystemLoader
from werkzeug.utils import secure_filename

from lib.options.options_review_db import SOURCE_TYPES, init_options_review_tables
from lib.options.options_review_images_lib import (
    OPTIONS_REVIEW_UPLOAD_TFS,
    normalize_options_review_draft_id,
    options_review_image_paths,
    options_review_upload_dir,
    save_options_review_slot_file,
)
from lib.options.options_review_lib import (
    SOURCE_LABELS,
    compute_review_stats,
    count_review_trades,
    delete_review_entry,
    ensure_local_review_synced,
    get_review_trade,
    hide_review_trade,
    list_review_trades,
    save_review_entry,
    sync_all_review_sources,
)


def attach_options_review_templates(app: Flask, repo_root: str) -> None:
    tpl_dir = os.path.join(repo_root, "lib", "options", "templates")
    if not os.path.isdir(tpl_dir):
        return
    existing = app.jinja_loader
    loaders = [FileSystemLoader(tpl_dir)]
    if existing is not None:
        if isinstance(existing, ChoiceLoader):
            loaders = list(existing.loaders) + loaders
        else:
            loaders.insert(0, existing)
    app.jinja_loader = ChoiceLoader(loaders)


def install_options_review(app: Flask, repo_root: str, app_module: Any) -> None:
    attach_options_review_templates(app, repo_root)
    cfg = {
        "get_db": app_module.get_db,
        "login_required": app_module.login_required,
        "exchange_options": getattr(app_module, "exchange_options", None),
        "render_main_page": app_module.render_main_page,
        "upload_folder": getattr(app_module, "UPLOAD_FOLDER", None)
        or os.path.join(os.path.dirname(getattr(app_module, "BASE_DIR", repo_root)), "static", "images"),
        "options_enabled": bool(getattr(app_module, "OKX_OPTIONS_ENABLED", False)),
        "app_module": app_module,
    }
    app.extensions["options_review_cfg"] = cfg
    register_options_review_routes(app, cfg, repo_root)


def _require_ex(cfg: dict[str, Any]):
    from lib.exchange.okx_options_lib import options_api_ready

    if not cfg.get("options_enabled"):
        return None, "期权模块未启用"
    ex = cfg.get("exchange_options")
    ok, reason = options_api_ready(ex)
    if not ok:
        return None, reason or "期权 API 未配置"
    return ex, ""


def register_options_review_routes(app: Flask, cfg: dict[str, Any], repo_root: str) -> None:
    lr = cfg["login_required"]

    @app.route("/options/review")
    @lr
    def options_review_page():
        from lib.instance.instance_embed_lib import redirect_to_embed_shell_if_enabled

        redir = redirect_to_embed_shell_if_enabled("options_review")
        if redir is not None:
            return redir
        return cfg["render_main_page"]("options_review")

    @app.route("/static/options_review.js")
    @lr
    def static_options_review_js():
        path = os.path.join(repo_root, "lib", "common", "static", "options_review.js")
        if not os.path.isfile(path):
            return ("not found", 404)
        return send_file(path, mimetype="application/javascript; charset=utf-8")

    @app.route("/static/images/options_journal/<path:filename>")
    @lr
    def static_options_review_image(filename: str):
        folder = options_review_upload_dir(cfg["upload_folder"])
        safe = os.path.basename(filename or "")
        path = os.path.join(folder, safe)
        if not os.path.isfile(path):
            return ("not found", 404)
        return send_file(path)

    @app.route("/api/options/review/sync", methods=["POST"])
    @lr
    def api_options_review_sync():
        """刷新本地 options_trades + 已结束对冲计划(不访问交易所)."""
        conn = cfg["get_db"]()
        try:
            init_options_review_tables(conn)
            result = sync_all_review_sources(conn, from_exchange=False)
            conn.commit()
            return jsonify(result)
        finally:
            conn.close()

    @app.route("/api/options/review/trades")
    @lr
    def api_options_review_trades():
        conn = cfg["get_db"]()
        try:
            # 翻页可跳过同步,仅刷新当前卡片列表
            do_sync = (request.args.get("sync") or "1").strip().lower() not in (
                "0",
                "false",
                "no",
            )
            if do_sync:
                ensure_local_review_synced(conn)
                conn.commit()
            filt = dict(
                source_type=(request.args.get("source_type") or "").strip() or None,
                underlying=(request.args.get("underlying") or "").strip() or None,
                opt_type=(request.args.get("opt_type") or "").strip() or None,
                strategy_tag=(request.args.get("strategy_tag") or "").strip() or None,
                reviewed=(request.args.get("reviewed") or "").strip() or None,
                include_hedge_legs=(request.args.get("include_hedge_legs") or "")
                .strip()
                .lower()
                in ("1", "true", "yes"),
                closed_from=(request.args.get("closed_from") or "").strip() or None,
                closed_to=(request.args.get("closed_to") or "").strip() or None,
            )
            limit = min(500, max(1, int(request.args.get("limit") or 200)))
            offset = max(0, int(request.args.get("offset") or 0))
            total = count_review_trades(conn, **filt)
            items = list_review_trades(conn, **filt, limit=limit, offset=offset)
            pages = max(1, (total + limit - 1) // limit) if total else 1
            page = (offset // limit) + 1 if limit else 1
            return jsonify(
                {
                    "ok": True,
                    "trades": items,
                    "source_labels": SOURCE_LABELS,
                    "total": total,
                    "limit": limit,
                    "offset": offset,
                    "page": page,
                    "pages": pages,
                }
            )
        finally:
            conn.close()

    @app.route("/api/options/review/trades/<int:trade_id>")
    @lr
    def api_options_review_trade_detail(trade_id: int):
        conn = cfg["get_db"]()
        try:
            item = get_review_trade(conn, trade_id)
            if not item:
                return jsonify({"ok": False, "msg": "未找到"}), 404
            return jsonify({"ok": True, "trade": item})
        finally:
            conn.close()

    @app.route("/api/options/review/entry", methods=["POST"])
    @lr
    def api_options_review_entry_save():
        data = request.get_json(silent=True) or {}
        try:
            trade_id = int(data.get("trade_id"))
        except (TypeError, ValueError):
            return jsonify({"ok": False, "msg": "trade_id 无效"}), 400
        conn = cfg["get_db"]()
        try:
            out = save_review_entry(conn, trade_id, data)
            if out.get("ok"):
                conn.commit()
            return jsonify(out), (200 if out.get("ok") else 400)
        finally:
            conn.close()

    @app.route("/api/options/review/trades/<int:trade_id>", methods=["DELETE"])
    @lr
    def api_options_review_trade_hide(trade_id: int):
        """从复盘列表删除并持久隐藏(刷新本地源也不会再导入)."""
        conn = cfg["get_db"]()
        try:
            out = hide_review_trade(conn, trade_id)
            if out.get("ok"):
                entry = out.get("entry") or {}
                folder = options_review_upload_dir(cfg["upload_folder"])
                for path in options_review_image_paths(entry, folder):
                    try:
                        os.remove(path)
                    except OSError:
                        pass
                conn.commit()
            return jsonify(out), (200 if out.get("ok") else 400)
        finally:
            conn.close()

    @app.route("/api/options/review/entry/<int:trade_id>", methods=["DELETE"])
    @lr
    def api_options_review_entry_delete(trade_id: int):
        conn = cfg["get_db"]()
        try:
            out = delete_review_entry(conn, trade_id)
            if out.get("ok"):
                entry = out.get("entry") or {}
                folder = options_review_upload_dir(cfg["upload_folder"])
                for path in options_review_image_paths(entry, folder):
                    try:
                        os.remove(path)
                    except OSError:
                        pass
                conn.commit()
            return jsonify(out), (200 if out.get("ok") else 400)
        finally:
            conn.close()

    @app.route("/api/options/review/upload_slot", methods=["POST"])
    @lr
    def api_options_review_upload_slot():
        draft_id = normalize_options_review_draft_id(
            request.form.get("draft_id") if request.form else None
        )
        tf = str((request.form.get("tf") if request.form else None) or "").strip()
        if not draft_id:
            return jsonify({"ok": False, "error": "invalid draft_id"}), 400
        if tf not in OPTIONS_REVIEW_UPLOAD_TFS:
            return jsonify({"ok": False, "error": "invalid tf"}), 400
        f = request.files.get("file") if request.files else None
        if not f or not getattr(f, "filename", None):
            return jsonify({"ok": False, "error": "no file"}), 400
        folder = options_review_upload_dir(cfg["upload_folder"])
        item = save_options_review_slot_file(
            f, draft_id, tf, folder, secure_filename_fn=secure_filename
        )
        if not item:
            return jsonify({"ok": False, "error": "save failed"}), 500
        return jsonify({"ok": True, "tf": tf, "file": item["file"]})

    @app.route("/api/options/review/stats")
    @lr
    def api_options_review_stats():
        conn = cfg["get_db"]()
        try:
            ensure_local_review_synced(conn)
            conn.commit()
            stats = compute_review_stats(
                conn,
                source_type=(request.args.get("source_type") or "").strip() or None,
                underlying=(request.args.get("underlying") or "").strip() or None,
                include_hedge_legs=(request.args.get("include_hedge_legs") or "").strip().lower()
                in ("1", "true", "yes"),
                closed_from=(request.args.get("closed_from") or "").strip() or None,
                closed_to=(request.args.get("closed_to") or "").strip() or None,
                require_strategy=(request.args.get("require_strategy") or "").strip().lower()
                in ("1", "true", "yes"),
            )
            stats["source_types"] = list(SOURCE_TYPES)
            stats["source_labels"] = SOURCE_LABELS
            return jsonify(stats)
        finally:
            conn.close()
