from __future__ import annotations

from typing import Any

from backend.data.numbers import finite_number


AMOUNT_THOUSAND_YUAN = 1000.0
AMOUNT_WAN_YUAN = 10000.0
VOLUME_LOT = 100.0

API_TO_DATASET = {
    "trade_cal": "calendar",
    "stock_basic": "stocks",
    "daily": "daily",
    "daily_basic": "valuation",
    "index_daily": "index_daily",
    "moneyflow": "moneyflow",
    "stk_auction": "auction",
}

SCALE_FIELDS = {
    "daily": {"vol": VOLUME_LOT, "amount": AMOUNT_THOUSAND_YUAN},
    "index_daily": {"vol": VOLUME_LOT, "amount": AMOUNT_THOUSAND_YUAN},
    "valuation": {"total_mv": AMOUNT_WAN_YUAN, "circ_mv": AMOUNT_WAN_YUAN},
    "moneyflow": {
        "buy_sm_amount": AMOUNT_WAN_YUAN,
        "sell_sm_amount": AMOUNT_WAN_YUAN,
        "buy_md_amount": AMOUNT_WAN_YUAN,
        "sell_md_amount": AMOUNT_WAN_YUAN,
        "buy_lg_amount": AMOUNT_WAN_YUAN,
        "sell_lg_amount": AMOUNT_WAN_YUAN,
        "buy_elg_amount": AMOUNT_WAN_YUAN,
        "sell_elg_amount": AMOUNT_WAN_YUAN,
        "net_mf_amount": AMOUNT_WAN_YUAN,
    },
    "auction": {"vol": VOLUME_LOT, "float_share": AMOUNT_WAN_YUAN},
}


def yyyymmdd(value: Any) -> str:
    return str(value or "").replace("-", "")[:8]


def to_native_rows(dataset: str, rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
    return [to_native_row(dataset, row) for row in rows]


def to_native_row(dataset: str, row: dict[str, Any]) -> dict[str, Any]:
    if dataset == "calendar":
        is_open = row.get("is_open")
        return {
            "exchange": str(row.get("exchange") or "SSE"),
            "cal_date": yyyymmdd(row.get("cal_date")),
            "is_open": 1 if is_open in (True, 1, "1", "Y", "y") else 0,
            "pretrade_date": yyyymmdd(row.get("pretrade_date")) or None,
        }
    converted = dict(row)
    converted.pop("batch_id", None)
    if "volume" in converted and "vol" not in converted:
        converted["vol"] = converted.pop("volume")
    elif "volume" in converted:
        converted.pop("volume", None)
    scales = SCALE_FIELDS.get(dataset) or {}
    for field, factor in scales.items():
        if field in converted:
            converted[field] = _unscale(converted.get(field), factor)
    if dataset == "stocks":
        converted.pop("updated_at", None)
    return converted


def to_canonical_row(dataset: str, row: dict[str, Any]) -> dict[str, Any]:
    if dataset == "calendar":
        is_open = row.get("is_open")
        return {
            "exchange": str(row.get("exchange") or "SSE"),
            "cal_date": yyyymmdd(row.get("cal_date")),
            "is_open": 1 if is_open in (True, 1, "1", "Y", "y") else 0,
            "pretrade_date": yyyymmdd(row.get("pretrade_date")) or None,
        }
    converted = dict(row)
    if "volume" in converted and "vol" not in converted:
        converted["vol"] = converted.pop("volume")
    scales = SCALE_FIELDS.get(dataset) or {}
    for field, factor in scales.items():
        if field in converted:
            converted[field] = _scale(converted.get(field), factor)
    return converted


def row_key(dataset: str, row: dict[str, Any]) -> tuple[str, ...]:
    if dataset == "calendar":
        return (yyyymmdd(row.get("cal_date")),)
    if dataset == "stocks":
        return (str(row.get("ts_code") or "").upper(),)
    if dataset == "status":
        return (str(row.get("dataset") or ""), yyyymmdd(row.get("trade_date")))
    return (str(row.get("ts_code") or "").upper(), yyyymmdd(row.get("trade_date")))


def project_fields(rows: list[dict[str, Any]], fields: str) -> list[dict[str, Any]]:
    keys = [item.strip() for item in str(fields or "").split(",") if item.strip()]
    if not keys:
        return rows
    return [{key: row.get(key) for key in keys} for row in rows]


def filter_stock_rows(rows: list[dict[str, Any]], params: dict[str, Any] | None) -> list[dict[str, Any]]:
    payload = params or {}
    ts_code = str(payload.get("ts_code") or "").strip().upper()
    status = str(payload.get("list_status") or "").strip()
    name = str(payload.get("name") or "").strip()
    filtered = rows
    if ts_code:
        filtered = [row for row in filtered if str(row.get("ts_code") or "").upper() == ts_code]
    if status:
        filtered = [row for row in filtered if str(row.get("list_status") or status) == status]
    if name:
        filtered = [row for row in filtered if name.casefold() in str(row.get("name") or "").casefold()]
    return filtered


def filter_calendar_rows(rows: list[dict[str, Any]], params: dict[str, Any] | None) -> list[dict[str, Any]]:
    payload = params or {}
    if payload.get("is_open") in (1, "1", True):
        return [row for row in rows if int(row.get("is_open") or 0) == 1]
    if payload.get("is_open") in (0, "0", False):
        return [row for row in rows if int(row.get("is_open") or 0) == 0]
    return rows


def _scale(value: Any, factor: float) -> float | None:
    number = _optional_number(value)
    if number is None:
        return None
    return number * factor


def _unscale(value: Any, factor: float) -> float | None:
    number = _optional_number(value)
    if number is None or factor == 0:
        return None
    return number / factor


def _optional_number(value: Any) -> float | None:
    if value in (None, ""):
        return None
    number = finite_number(value, default=float("nan"))
    if number != number:
        return None
    return number
