"""工作台列表与表单选项：策略分页、版本下拉（V2-02/03）、静态枚举（V2-04）。"""

from __future__ import annotations

from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple

from core.infra.project_context import DiscoveryManager
from core.modules.data_manager import DataManager
from core.modules.market_profile.constants import MARKETS_CONFIG_DIR
from core.modules.strategy.engines.shared.data_classes.discovered_strategy import DiscoveredStrategy
from core.modules.strategy.engines.shared.data_classes.strategy_settings.sampling_settings import (
    KNOWN_STRATEGIES,
)
from core.modules.strategy.engines.shared.data_classes.strategy_settings.simulation_settings import (
    KNOWN_SIMULATION_TEMPLATES,
)
from core.modules.strategy.engines.simulator.capital_allocation.data_classes.settings import (
    _VALID_MODES,
)
from core.modules.strategy.services.discovery import StrategyDiscoveryHelper

# 与 ``API.md`` V2-03：固定至多 10 条，不分页。
DROPDOWN_LIMIT = 10


def _summary(ds: DiscoveredStrategy) -> Dict[str, Any]:
    desc = ""
    try:
        desc = str(ds.settings.meta.description or "").strip()
    except Exception:
        desc = ""
    return {
        "name": ds.name,
        "is_enabled": bool(ds.is_enabled),
        "worker_class_name": ds.worker_class_name,
        "folder": str(ds.folder),
        "description": desc,
    }


def fetch_discovered_strategies_page(page: int, limit: int) -> Tuple[List[Dict[str, Any]], int]:
    """
    分页返回 userspace 发现到的策略摘要；``page`` 为 1-based，按 ``name`` 排序。
    """
    discovered = StrategyDiscoveryHelper.discover_strategies()
    ordered = sorted(discovered.values(), key=lambda d: d.name)
    total = len(ordered)
    if total == 0:
        return [], 0
    page = max(1, int(page))
    limit = max(1, int(limit))
    start = (page - 1) * limit
    chunk = ordered[start : start + limit]
    return [_summary(ds) for ds in chunk], total


def _iso(dt: Any) -> Optional[str]:
    if dt is None:
        return None
    if isinstance(dt, datetime):
        return dt.isoformat(sep=" ", timespec="seconds")
    return str(dt)


def fetch_strategy_versions_dropdown(strategy_name: str) -> List[Dict[str, Any]]:
    """
    某策略工作台快照版本（最多 ``DROPDOWN_LIMIT`` 条，从新到旧）；无表或无行时 ``[]``。
    """
    name = str(strategy_name or "").strip()
    if not name:
        return []

    model = DataManager().get_table("sys_strategy_workbench_snapshot")
    if model is None:
        return []

    rows = model.list_by_strategy(name, limit=DROPDOWN_LIMIT)
    items: List[Dict[str, Any]] = []
    for row in rows:
        sid = int(row.get("snapshot_id") or row.get("version") or 0)
        if sid <= 0:
            continue
        items.append(
            {
                "version_id": f"v{sid}",
                "snapshot_id": sid,
                "updated_at": _iso(row.get("updated_at")),
                "created_at": _iso(row.get("created_at")),
            }
        )
    return items


# --- V2-04 静态选项（与校验层合法取值一致） ---

_CAPITAL_ALLOCATION_META: Dict[str, tuple] = {
    "equal_capital": (
        "等额资金",
        "每个新开仓机会分配相近的现金额度（总资金 ÷ 最大持股数），股数随价格浮动。",
    ),
    "equal_shares": (
        "等额股数",
        "每个机会买入相同手数（由「每次买入手数」与市场最小交易单位决定）。",
    ),
    "kelly": (
        "凯莉公式",
        "按凯莉公式估算建议仓位，再乘以「凯莉折扣系数」做保守缩放；需策略提供胜率/赔率等输入。",
    ),
    "custom": (
        "自定义",
        "使用策略或引擎扩展的自定义分配逻辑（高级用法）。",
    ),
}

_SAMPLING_STRATEGY_META: Dict[str, tuple] = {
    "continuous": ("连续采样", "从某一位置起连续顺序抽样。比如，从第1个开始连续抽取5个股票"),
    "uniform": ("均匀采样", "对所有集合进行等间距抽样。比如一共100个股票，抽取10个，那么就是每隔10个抽取一个。"),
    "stratified": ("分层采样", "按分层规则抽样（如行业、市值桶），需配置随机种子以便复现。比如，深市沪市各抽取5个。"),
    "random": ("随机采样", "在集合中随机抽样；可设种子固定随机结果。"),
    "pool": ("股票池", "在指定的列表内回测。"),
    "blacklist": ("黑名单", "抽取所有的集合，再排除黑名单列表后的样本。"),
}

_SIMULATION_TEMPLATE_META: Dict[str, tuple] = {
    "deterministic": (
        "确定性",
        "收盘确认信号，次日开盘买入、收盘卖出；盯盘用收盘价。系统默认预设，偏乐观。",
    ),
    "extreme": (
        "极值压力",
        "盯盘与成交均按当日最高/最低价等极值近似，用于压力测试，结果通常更保守。",
    ),
    "custom": (
        "自定义",
        "逐项指定盯盘价、买卖价、滑点与涨跌停等规则；仅在此模式下可改细项。",
    ),
}


def items_capital_allocation_strategies() -> List[Dict[str, Any]]:
    """``capital_simulator.allocation.mode`` 可选值。"""
    ordered = ("equal_capital", "equal_shares", "kelly", "custom")
    modes = [m for m in ordered if m in _VALID_MODES]
    rest = sorted(m for m in _VALID_MODES if m not in modes)
    out: List[Dict[str, Any]] = []
    for m in modes + rest:
        meta = _CAPITAL_ALLOCATION_META.get(m)
        if meta:
            label, tooltip = meta
            out.append({"value": m, "label": label, "tooltip": tooltip})
        else:
            out.append({"value": m, "label": m})
    return out


def items_sampling_strategies() -> List[Dict[str, Any]]:
    """根级 ``sampling.strategy`` 可选值。"""
    ordered = ("continuous", "uniform", "stratified", "random", "pool", "blacklist")
    keys = [k for k in ordered if k in KNOWN_STRATEGIES]
    rest = sorted(k for k in KNOWN_STRATEGIES if k not in keys)
    out: List[Dict[str, Any]] = []
    for k in keys + rest:
        meta = _SAMPLING_STRATEGY_META.get(k)
        if meta:
            label, tooltip = meta
            out.append({"value": k, "label": label, "tooltip": tooltip})
        else:
            out.append({"value": k, "label": k})
    return out


def items_simulation_templates() -> List[Dict[str, Any]]:
    """根级 ``simulation.template`` 可选值（label 中文，value 与 settings 英文枚举一致）。"""
    ordered = ("deterministic", "extreme", "custom")
    keys = [k for k in ordered if k in KNOWN_SIMULATION_TEMPLATES]
    rest = sorted(k for k in KNOWN_SIMULATION_TEMPLATES if k not in keys)
    out: List[Dict[str, Any]] = []
    for k in keys + rest:
        meta = _SIMULATION_TEMPLATE_META.get(k)
        if meta:
            label, tooltip = meta
            out.append({"value": k, "label": label, "tooltip": tooltip})
        else:
            out.append({"value": k, "label": k})
    return out


def items_market_profiles() -> List[Dict[str, Any]]:
    """根级 ``market_profile`` 可选值（扫描 markets 配置目录）。"""
    known = DiscoveryManager.discover_configs(MARKETS_CONFIG_DIR)
    out: List[Dict[str, Any]] = []
    for pid in known:
        label = pid
        try:
            raw = DiscoveryManager.load_overridable_config(MARKETS_CONFIG_DIR, pid)
            if isinstance(raw, dict):
                desc = str(raw.get("description") or "").strip()
                if desc:
                    label = desc
        except Exception:
            pass
        out.append({"value": pid, "label": label})
    return out
