from __future__ import annotations

from typing import Any


def build_theme_library(
    trade_date: str,
    directory_rows: tuple[dict[str, Any], ...],
    daily_rows: tuple[dict[str, Any], ...],
    hot_rows: tuple[dict[str, Any], ...],
) -> dict[str, Any]:
    daily = {str(row.get("ts_code") or ""): row for row in daily_rows}
    hot = {
        str(row.get("ts_code") or ""): int(_number(row.get("rank"), 9999))
        for row in hot_rows
        if str(row.get("data_type") or "") == "概念板块"
    }
    items = []
    for row in directory_rows:
        if str(row.get("type") or "").upper() != "N":
            continue
        if str(row.get("exchange") or "").upper() != "A":
            continue
        code = str(row.get("ts_code") or "")
        name = str(row.get("name") or "").strip()
        if not code or not name:
            continue
        quote = daily.get(code)
        items.append(
            {
                "code": code,
                "name": name,
                "member_count": int(_number(row.get("count"))),
                "change": round(_number((quote or {}).get("pct_change")), 2)
                if quote
                else None,
                "close": round(_number((quote or {}).get("close")), 3) if quote else None,
                "turnover_rate": round(_number((quote or {}).get("turnover_rate")), 2)
                if quote
                else None,
                "hot_rank": hot.get(code),
                "has_quote": bool(quote),
            }
        )
    items.sort(
        key=lambda item: (
            bool(item["has_quote"]),
            item["hot_rank"] is not None,
            -(item["hot_rank"] or 9999),
            _number(item["change"], -999),
        ),
        reverse=True,
    )
    quoted = [item for item in items if item["has_quote"]]
    return {
        "trade_date": trade_date,
        "summary": {
            "theme_count": len(items),
            "quoted_count": len(quoted),
            "up_count": sum(_number(item["change"]) > 0 for item in quoted),
            "down_count": sum(_number(item["change"]) < 0 for item in quoted),
            "hot_count": len(hot),
        },
        "items": items,
    }


def build_theme_detail(
    trade_date: str,
    theme: dict[str, Any],
    member_rows: tuple[dict[str, Any], ...],
    daily_rows: tuple[dict[str, Any], ...],
) -> dict[str, Any]:
    daily = {str(row.get("ts_code") or ""): row for row in daily_rows}
    members = []
    seen = set()
    for row in member_rows:
        identifier = str(row.get("con_code") or "")
        if not identifier or identifier in seen:
            continue
        seen.add(identifier)
        quote = daily.get(identifier)
        members.append(
            {
                "identifier": identifier,
                "code": identifier.split(".")[0],
                "name": str(row.get("con_name") or ""),
                "change": round(_number((quote or {}).get("pct_chg")), 2)
                if quote
                else None,
                "close": round(_number((quote or {}).get("close")), 2) if quote else None,
                "amount": _number((quote or {}).get("amount")) * 1000 if quote else None,
                "quoted": bool(quote),
            }
        )
    members.sort(
        key=lambda item: (
            bool(item["quoted"]),
            _number(item["change"], -999),
            _number(item["amount"]),
        ),
        reverse=True,
    )
    quoted = [item for item in members if item["quoted"]]
    return {
        "trade_date": trade_date,
        "theme": theme,
        "summary": {
            "member_count": len(members),
            "quoted_count": len(quoted),
            "up_count": sum(_number(item["change"]) > 0 for item in quoted),
            "down_count": sum(_number(item["change"]) < 0 for item in quoted),
            "turnover_rate": theme.get("turnover_rate"),
        },
        "members": members,
    }


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
