from __future__ import annotations

import json
import urllib.error
import urllib.parse
import urllib.request
from datetime import datetime
from zoneinfo import ZoneInfo

from backend.data.contracts import (
    DataSource,
    DataUsage,
    ObservationMetadata,
    ProviderResult,
    SnapshotState,
)
from backend.data.providers.base import ProviderError

SHANGHAI = ZoneInfo("Asia/Shanghai")
INDEX_CODES = {"000001.SH": "1.000001", "399001.SZ": "0.399001", "399006.SZ": "0.399006"}


class EastmoneyProvider:
    source = DataSource.EASTMONEY
    url = "https://push2delay.eastmoney.com/api/qt/stock/trends2/get"

    def __init__(self, timeout: int = 6) -> None:
        self._timeout = timeout

    @property
    def configured(self) -> bool:
        return True

    def calendar(self, start_date: str, end_date: str) -> ProviderResult:
        raise ProviderError("The display provider is not a calendar authority")

    def entities(self) -> ProviderResult:
        raise ProviderError("The display provider is not an entity authority")

    def daily(self, entity_type: str, identifier: str, end_date: str) -> ProviderResult:
        raise ProviderError("The display provider does not supply canonical daily bars")

    def minute(self, entity_type: str, identifier: str, trade_date: str) -> ProviderResult:
        secid = self._secid(entity_type, identifier)
        params = urllib.parse.urlencode(
            {
                "secid": secid,
                "fields1": "f1,f2,f3,f4,f5,f6,f7,f8,f9,f10,f11,f12,f13",
                "fields2": "f51,f52,f53,f54,f55,f56,f57,f58",
                "iscr": "0",
                "ndays": "1",
            }
        )
        request = urllib.request.Request(
            f"{self.url}?{params}",
            headers={"Accept": "application/json", "User-Agent": "XiaobaiReview/2"},
        )
        try:
            with urllib.request.urlopen(request, timeout=self._timeout) as response:
                payload = json.loads(response.read().decode("utf-8"))
        except (urllib.error.URLError, TimeoutError, OSError, json.JSONDecodeError) as exc:
            raise ProviderError("展示行情请求失败") from exc
        data = payload.get("data") or {}
        rows = []
        for raw in data.get("trends") or []:
            fields = str(raw).split(",")
            if len(fields) < 8 or " " not in fields[0]:
                continue
            date, time = fields[0].split(" ", 1)
            if date != trade_date or not "09:30" <= time[:5] <= "15:00":
                continue
            rows.append(
                {
                    "time": fields[0],
                    "open": fields[1],
                    "close": fields[2],
                    "high": fields[3],
                    "low": fields[4],
                    "volume": fields[5],
                    "amount": fields[6],
                    "avgPrice": fields[7],
                    "preClose": data.get("preClose"),
                }
            )
        metadata = ObservationMetadata(
            source=self.source,
            observed_at=datetime.now(SHANGHAI),
            unit="yuan/share",
            adjustment="unadjusted",
            freshness_seconds=0,
            coverage=1 if rows else 0,
            state=SnapshotState.REALTIME,
            usage=DataUsage.DISPLAY,
        )
        return ProviderResult(tuple(rows), metadata)

    def snapshot_inputs(
        self, trade_date: str, previous_trade_date: str
    ) -> dict[str, ProviderResult | dict[str, object]]:
        raise ProviderError("The display provider cannot build market snapshots")

    def realtime_market_inputs(
        self,
        trade_date: str,
        previous_trade_date: str,
        identifiers: tuple[str, ...],
    ) -> dict[str, ProviderResult | dict[str, object]]:
        raise ProviderError("The display provider cannot build realtime market snapshots")

    def sector_members(self, representative: str, trade_date: str) -> ProviderResult:
        raise ProviderError("The display provider is not the constituent authority")

    def heaven_inputs(
        self, representative: str, trade_date: str
    ) -> dict[str, ProviderResult | None]:
        raise ProviderError("The display provider cannot supply deterministic Heaven inputs")

    def heaven_realtime_inputs(
        self, representative: str, trade_date: str, previous_trade_date: str
    ) -> dict[str, ProviderResult | None]:
        raise ProviderError("The display provider cannot supply deterministic Heaven inputs")

    def market_insight(
        self,
        kind: str,
        trade_date: str,
        previous_trade_date: str = "",
        identifier: str = "",
    ) -> dict[str, ProviderResult | None]:
        raise ProviderError("The display provider cannot supply market insight archives")

    def realtime_snapshots(
        self, identifiers: tuple[str, ...], start_time: str, end_time: str
    ) -> ProviderResult:
        raise ProviderError("The display provider cannot supply calculation snapshots")

    def event_reasons(self, trade_date: str) -> ProviderResult:
        raise ProviderError("The display provider cannot supply event reasons")

    def screener_inputs(self, trade_dates: tuple[str, ...]) -> dict[str, ProviderResult | None]:
        raise ProviderError("The display provider cannot supply screener factors")

    @staticmethod
    def _secid(entity_type: str, identifier: str) -> str:
        if entity_type == "index" and identifier in INDEX_CODES:
            return INDEX_CODES[identifier]
        code = identifier.split(".")[0]
        if entity_type == "stock" and len(code) == 6 and code.isdigit():
            market = "1" if code.startswith(("5", "6", "9")) else "0"
            return f"{market}.{code}"
        raise ProviderError("该标的暂无展示分时数据")
