from __future__ import annotations

from datetime import datetime
from typing import Any

from backend.bootstrap.config import display_compact_date as _display_date
from backend.features.screener.backtest import BacktestRunner
from backend.features.screener.catalog import REGIMES
from backend.features.screener.factors import FactorBuilder
from backend.features.screener.formula import FormulaEvaluator
from database import ReviewDatabase


class SelectionRunner:
    def __init__(
        self,
        database: ReviewDatabase,
        factor_builder: FactorBuilder,
        formula_evaluator: FormulaEvaluator,
        backtest_runner: BacktestRunner,
    ) -> None:
        self.database = database
        self.factor_builder = factor_builder
        self.formula_evaluator = formula_evaluator
        self.backtest_runner = backtest_runner

    def validate_formula(self, formula: dict[str, Any]) -> dict[str, Any]:
        return self.formula_evaluator.validate_formula(formula)

    def build_factors(
        self,
        trade_date: str,
        realtime_snapshot: dict[str, Any] | None,
        history_days: int,
    ) -> tuple[list[dict[str, Any]], str]:
        return self.factor_builder.build_factors(
            trade_date, realtime_snapshot, history_days
        )

    def apply_formula(
        self, rows: list[dict[str, Any]], formula: dict[str, Any], regime: str
    ) -> list[dict[str, Any]]:
        return self.formula_evaluator.apply_formula(rows, formula, regime)

    def backtest(self, trade_date: str, formula: dict[str, Any]) -> dict[str, Any]:
        return self.backtest_runner.backtest(trade_date, formula)

    def screen(
        self, user_id: int, trade_date: str, formula: dict[str, Any], regime: str,
        strategy_name: str, run_backtest: bool = True,
        realtime_snapshot: dict[str, Any] | None = None,
        mode: str = "smart",
        prepared_factors: list[dict[str, Any]] | None = None,
        prepared_date: str = "",
    ) -> dict[str, Any]:
        mode = mode if mode in {"smart", "curated", "quant"} else "smart"
        formula = self.validate_formula(formula)
        if prepared_factors is None:
            history_days = int((formula.get("meta") or {}).get("history_days") or 80)
            factors, actual_date = self.build_factors(
                trade_date, realtime_snapshot, history_days
            )
        else:
            factors = prepared_factors
            actual_date = prepared_date or trade_date
        candidates = self.apply_formula(factors, formula, regime)
        backtest = self.backtest(actual_date, formula) if run_backtest else None
        required_fields = sorted({
            str(item.get("field") or "")
            for item in list(formula.get("filters") or []) + list(formula.get("score") or [])
            if item.get("field")
        })
        complete_rows = sum(
            1 for row in factors
            if all(row.get(field) is not None for field in required_fields)
        )
        coverage = round(complete_rows / len(factors) * 100, 1) if factors else 0.0
        health_status = "normal" if candidates else "no_signal"
        if backtest and backtest["samples"] >= 20:
            for candidate in candidates:
                estimate = backtest["win_rate"] * 0.65 + candidate["score"] * 100 * 0.35
                candidate["historical_probability"] = round(min(95, max(5, estimate)), 1)
                candidate["probability_samples"] = backtest["samples"]
        else:
            for candidate in candidates:
                candidate["historical_probability"] = None
                candidate["probability_samples"] = backtest["samples"] if backtest else 0
        result = {
            "meta": {
                "trade_date": _display_date(actual_date),
                "regime": regime,
                "regime_label": REGIMES.get(regime, regime),
                "strategy_name": strategy_name,
                "mode": mode,
                "library_version": int(
                    (formula.get("meta") or {}).get("library_version") or 0
                ),
                "universe_count": len(factors),
                "candidate_count": len(candidates),
                "updated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
                "health": {
                    "status": health_status,
                    "required_field_count": len(required_fields),
                    "complete_rows": complete_rows,
                    "universe_rows": len(factors),
                    "coverage": coverage,
                    "signal_count": len(candidates),
                },
                "selection_source": (
                    "tushare_rt_k+history" if realtime_snapshot else "historical_eod"
                ),
                "realtime": bool(realtime_snapshot),
                "history_cutoff": (
                    str(realtime_snapshot.get("previous_trade_date") or "")
                    if realtime_snapshot else actual_date
                ),
                "factor_freshness": {
                    "realtime": [
                        "价格", "涨跌幅", "成交量", "成交额", "换手率",
                        "均线位置", "5/10日动量", "板块强度", "开盘竞价",
                    ] if realtime_snapshot else [],
                    "historical": ["历史波动率", "流通市值", "资金流", "竞价因子", "回测"],
                },
            },
            "formula": formula,
            "candidates": candidates,
            "backtest": backtest,
            "disclaimer": (
                "候选仅由策略条件与当日数据计算；历史统计不代表未来收益。"
                if mode == "curated"
                else "概率为历史条件估计，不代表未来收益；退潮或样本不足时允许无候选。"
            ),
        }
        run_id = self.database.save_screener_run(
            user_id, actual_date, regime, strategy_name, formula, result, mode
        )
        result["meta"]["run_id"] = run_id
        return result
