from __future__ import annotations

from statistics import mean
from typing import Any

from backend.data.contracts import ProviderResult


def build_snapshot(
    trade_date: str,
    previous_trade_date: str,
    inputs: dict[str, ProviderResult | dict[str, Any]],
) -> dict[str, Any]:
    daily_rows = _rows(inputs, "daily")
    daily = {str(row.get("ts_code") or ""): row for row in daily_rows}
    limits = _pool(_rows(inputs, "limit_up"), "涨停")
    broken = _pool(_rows(inputs, "broken"), "炸板")
    down_limits = _pool(_rows(inputs, "limit_down"), "跌停")
    previous_limits = _pool(_rows(inputs, "previous_limit_up"), "涨停")
    price_limits = {str(row.get("ts_code") or ""): row for row in _rows(inputs, "price_limits")}
    for row in broken:
        up_limit = _number(price_limits.get(row["identifier"], {}).get("up_limit"))
        row["distance_to_limit"] = (
            round((up_limit - row["price"]) / up_limit * 100, 2) if up_limit else None
        )

    yesterday = _yesterday(
        previous_limits,
        daily,
        limits,
        broken,
        down_limits,
    )
    up_count = sum(_number(row.get("pct_chg")) > 0 for row in daily_rows)
    down_count = sum(_number(row.get("pct_chg")) < 0 for row in daily_rows)
    flat_count = len(daily_rows) - up_count - down_count
    amount = sum(_amount_yuan(row) for row in daily_rows)
    seal_rate = len(limits) / max(len(limits) + len(broken), 1) * 100
    sectors = _sectors(limits)
    previous_sectors = _sectors(previous_limits)
    overview = {
        "up_count": up_count,
        "down_count": down_count,
        "flat_count": flat_count,
        "limit_up": len(limits),
        "limit_down": len(down_limits),
        "broken": len(broken),
        "seal_rate": round(seal_rate, 1),
        "amount": round(amount, 2),
    }
    return {
        "trade_date": trade_date,
        "previous_trade_date": previous_trade_date,
        "overview": overview,
        "limits": limits,
        "broken": broken,
        "down_limits": down_limits,
        "yesterday_limits": yesterday,
        "limit_performance": _performance(yesterday),
        "ladders": _ladders(limits),
        "sectors": sectors,
        "sector_rotation": _rotation(sectors, previous_sectors),
    }


def build_realtime_inputs(
    inputs: dict[str, ProviderResult | dict[str, Any]],
    directory: dict[str, dict[str, Any]],
) -> dict[str, ProviderResult | dict[str, Any]]:
    daily_result = inputs.get("daily")
    limits_result = inputs.get("price_limits")
    previous_result = inputs.get("previous_limit_up")
    if not isinstance(daily_result, ProviderResult) or not isinstance(
        limits_result, ProviderResult
    ):
        return inputs
    prices = {str(row.get("ts_code") or ""): row for row in limits_result.rows}
    previous = {
        str(row.get("ts_code") or ""): row
        for row in previous_result.rows
    } if isinstance(previous_result, ProviderResult) else {}
    pools: dict[str, list[dict[str, Any]]] = {
        "limit_up": [],
        "limit_down": [],
        "broken": [],
    }
    for quote in daily_result.rows:
        identifier = str(quote.get("ts_code") or "")
        price = prices.get(identifier) or {}
        current = _number(quote.get("close"))
        high = _number(quote.get("high"))
        up_limit = _number(price.get("up_limit"))
        down_limit = _number(price.get("down_limit"))
        event_type = ""
        if up_limit > 0 and current >= up_limit - 0.001:
            event_type = "limit_up"
        elif up_limit > 0 and high >= up_limit - 0.001:
            event_type = "broken"
        elif down_limit > 0 and current <= down_limit + 0.001:
            event_type = "limit_down"
        if not event_type:
            continue
        identity = directory.get(identifier) or {}
        prior_streak = int(_number(previous.get(identifier, {}).get("limit_times")))
        pools[event_type].append(
            {
                **quote,
                "name": str(quote.get("name") or identity.get("name") or ""),
                "industry": str(identity.get("sector") or ""),
                "first_time": "",
                "last_time": "",
                "open_times": 0,
                "limit_times": prior_streak + 1 if event_type == "limit_up" else 1,
                "fd_amount": 0,
            }
        )
    metadata = daily_result.metadata
    return {
        **inputs,
        "limit_up": ProviderResult(tuple(pools["limit_up"]), metadata),
        "limit_down": ProviderResult(tuple(pools["limit_down"]), metadata),
        "broken": ProviderResult(tuple(pools["broken"]), metadata),
    }


def _rows(
    inputs: dict[str, ProviderResult | dict[str, Any]], key: str
) -> tuple[dict[str, Any], ...]:
    value = inputs.get(key)
    return value.rows if isinstance(value, ProviderResult) else ()


def _pool(rows: tuple[dict[str, Any], ...], status: str) -> list[dict[str, Any]]:
    result = []
    for row in rows:
        identifier = str(row.get("ts_code") or "")
        amount = _amount_yuan(row)
        result.append(
            {
                "identifier": identifier,
                "code": identifier.split(".")[0],
                "name": str(row.get("name") or "").strip(),
                "price": _number(row.get("close")),
                "change": _number(row.get("pct_chg")),
                "sector": str(row.get("industry") or "").strip(),
                "reason": "",
                "first_time": _time(row.get("first_time")),
                "last_time": _time(row.get("last_time")),
                "open_times": int(_number(row.get("open_times"))),
                "streak": max(1, int(_number(row.get("limit_times"), 1))),
                "turnover_rate": _number(row.get("turnover_ratio")),
                "amount": amount,
                "seal_amount": _number(row.get("fd_amount")),
                "float_market_value": _number(row.get("float_mv")),
                "status": status,
            }
        )
    return result


def _yesterday(
    previous: list[dict[str, Any]],
    daily: dict[str, dict[str, Any]],
    current: list[dict[str, Any]],
    broken: list[dict[str, Any]],
    down: list[dict[str, Any]],
) -> list[dict[str, Any]]:
    current_map = {row["identifier"]: row for row in current}
    broken_codes = {row["identifier"] for row in broken}
    down_codes = {row["identifier"] for row in down}
    result = []
    for prior in previous:
        identifier = prior["identifier"]
        quote = daily.get(identifier, {})
        change = _number(quote.get("pct_chg"))
        if identifier in current_map:
            outcome = "晋级"
        elif identifier in broken_codes:
            outcome = "炸板"
        elif identifier in down_codes:
            outcome = "跌停"
        elif change > 0:
            outcome = "红盘"
        else:
            outcome = "断板"
        result.append(
            {
                "identifier": identifier,
                "code": prior["code"],
                "name": prior["name"],
                "prior_streak": prior["streak"],
                "current_streak": current_map.get(identifier, {}).get("streak", 0),
                "current_change": change,
                "current_price": _number(quote.get("close")),
                "sector": prior["sector"],
                "reason": prior["reason"],
                "outcome": outcome,
            }
        )
    return result


def _performance(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
    result = []
    for level in sorted({int(row["prior_streak"]) for row in rows}, reverse=True):
        group = [row for row in rows if int(row["prior_streak"]) == level]
        advanced = sum(row["outcome"] == "晋级" for row in group)
        positive = sum(_number(row["current_change"]) > 0 for row in group)
        outcomes = {
            outcome: sum(row["outcome"] == outcome for row in group)
            for outcome in ("晋级", "红盘", "断板", "炸板", "跌停")
        }
        result.append(
            {
                "level": level,
                "count": len(group),
                "advanced": advanced,
                "red": outcomes["红盘"],
                "broken": outcomes["断板"],
                "opened": outcomes["炸板"],
                "limit_down": outcomes["跌停"],
                "advance_rate": round(advanced / len(group) * 100, 1),
                "positive_rate": round(positive / len(group) * 100, 1),
                "average_change": round(mean(_number(row["current_change"]) for row in group), 2),
            }
        )
    return result


def _ladders(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
    levels = sorted({int(row["streak"]) for row in rows}, reverse=True)
    return [
        {
            "level": level,
            "label": "首板" if level == 1 else f"{level}板",
            "count": sum(int(row["streak"]) == level for row in rows),
            "stocks": sorted(
                (row for row in rows if int(row["streak"]) == level),
                key=lambda row: row.get("first_time") or "99:99",
            ),
        }
        for level in levels
    ]


def _sectors(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
    names = sorted({str(row.get("sector") or "").strip() for row in rows} - {""})
    result = []
    for name in names:
        stocks = [row for row in rows if str(row.get("sector") or "").strip() == name]
        leader = max(stocks, key=lambda row: (int(row["streak"]), _number(row["amount"])))
        max_streak = max(int(row["streak"]) for row in stocks)
        count = len(stocks)
        result.append(
            {
                "name": name,
                "count": count,
                "strength": min(100, 44 + count * 8 + max_streak * 5),
                "amount": round(sum(_number(row["amount"]) for row in stocks), 2),
                "leader": leader["name"],
                "representative": leader["identifier"],
                "change": round(mean(_number(row["change"]) for row in stocks), 2),
                "max_streak": max_streak,
            }
        )
    return sorted(
        result,
        key=lambda row: (int(row["count"]), int(row["max_streak"]), _number(row["amount"])),
        reverse=True,
    )[:20]


def _rotation(
    current: list[dict[str, Any]], previous: list[dict[str, Any]]
) -> list[dict[str, Any]]:
    previous_map = {str(row["name"]): row for row in previous}
    result = []
    for rank, sector in enumerate(current[:12], start=1):
        previous_count = int(previous_map.get(str(sector["name"]), {}).get("count") or 0)
        delta = int(sector["count"]) - previous_count
        result.append(
            {
                **sector,
                "rank": rank,
                "previous_count": previous_count,
                "delta": delta,
                "trend": "升温" if delta > 0 else "降温" if delta < 0 else "持平",
            }
        )
    return result


def _number(value: Any, default: float = 0.0) -> float:
    try:
        number = float(value)
        return number if number == number else default
    except (TypeError, ValueError):
        return default


def _amount_yuan(row: dict[str, Any]) -> float:
    amount = _number(row.get("amount"))
    return amount if row.get("amount_unit") == "yuan" else amount * 1000


def _time(value: Any) -> str:
    text = str(value or "").strip().replace(":", "")
    if len(text) < 4 or not text[:4].isdigit():
        return ""
    return f"{text[:2]}:{text[2:4]}"
