from __future__ import annotations

from datetime import datetime
from typing import Any

from backend.data.gateway import DataGateway, MarketDataUnavailable
from backend.data.providers.base import ProviderError
from backend.data.quality import DataQualityError
from backend.features.market.insights import MarketInsightService
from backend.features.market.insights.service import MarketInsightError
from backend.features.market.sync import MarketSnapshotService, SnapshotSyncError
from backend.http.errors import AppError


class MarketService:
    def __init__(
        self,
        gateway: DataGateway,
        snapshots: MarketSnapshotService,
        insights: MarketInsightService,
    ) -> None:
        self._gateway = gateway
        self._snapshots = snapshots
        self._insights = insights

    def context(self, requested_date: str | None = None) -> dict[str, Any]:
        context = self._call(self._gateway.trade_context, requested_date)
        return _context(context)

    def summary(self, requested_date: str | None = None) -> dict[str, Any]:
        result = self._call(self._gateway.summary, requested_date)
        return {"context": _context(result["context"]), "values": result["values"]}

    def search(self, query: str) -> dict[str, Any]:
        normalized = " ".join(query.split())
        items = self._gateway.search(normalized) if normalized else ()
        labels = {"stock": "股票", "sector": "板块", "theme": "题材", "index": "指数"}
        groups = []
        for entity_type in ("stock", "sector", "theme", "index"):
            groups.append(
                {
                    "entity_type": entity_type,
                    "label": labels[entity_type],
                    "items": [
                        {
                            "entity_type": item.entity_type,
                            "identifier": item.identifier,
                            "code": item.code,
                            "name": item.name,
                            "sector": item.sector,
                        }
                        for item in items
                        if item.entity_type == entity_type
                    ],
                }
            )
        return {"query": normalized, "groups": groups}

    def chart(self, entity_type: str, identifier: str, interval: str) -> dict[str, Any]:
        series = self._call(self._gateway.chart, entity_type, identifier, interval)
        return {
            "entity_type": series.entity.entity_type,
            "identifier": series.entity.identifier,
            "code": series.entity.code,
            "name": series.entity.name,
            "interval": series.interval,
            "trade_date": series.trade_date,
            "observed_at": series.metadata.observed_at,
            "previous_close": series.previous_close,
            "range_start": "09:30" if interval == "minute" else None,
            "range_end": "15:00" if interval == "minute" else None,
            "points": [
                {
                    "time": point.time,
                    "open": point.open,
                    "high": point.high,
                    "low": point.low,
                    "close": point.close,
                    "volume": point.volume,
                    "amount": point.amount,
                    "average": point.average,
                }
                for point in series.points
            ],
        }

    def entity_detail(
        self, entity_type: str, identifier: str, requested_date: str | None = None
    ) -> dict[str, Any]:
        return self._call(
            self._snapshots.entity_detail, entity_type, identifier, requested_date
        )

    def refresh_reference(self) -> dict[str, int | str]:
        return self._call(self._gateway.refresh_reference)

    def sync_snapshot(self, requested_date: str | None = None) -> dict[str, Any]:
        return self._call(self._snapshots.sync, requested_date)

    def sync_realtime(
        self, requested_date: str | None = None, now: datetime | None = None
    ) -> dict[str, Any]:
        return self._call(self._snapshots.sync_realtime, requested_date, now)

    def workspace(self, key: str, requested_date: str | None = None) -> dict[str, Any]:
        return self._call(self._snapshots.workspace, key, requested_date)

    def rotation_members(
        self, sector_name: str, requested_date: str | None = None
    ) -> dict[str, Any]:
        trade_date, representative = self._call(
            self._snapshots.rotation_member_target, requested_date, sector_name
        )
        return self._call(
            self._gateway.sector_members, trade_date, sector_name, representative
        )

    def insight(
        self, key: str, requested_date: str | None, user_id: int
    ) -> dict[str, Any]:
        return self._call(self._insights.workspace, key, requested_date, user_id=user_id)

    def sync_insight(
        self, key: str, requested_date: str | None, user_id: int
    ) -> dict[str, Any]:
        return self._call(
            self._insights.workspace, key, requested_date, user_id=user_id, force=True
        )

    def theme_detail(self, identifier: str, requested_date: str | None = None) -> dict[str, Any]:
        return self._call(self._insights.theme_detail, identifier, requested_date)

    def save_seat_alias(
        self, seat_name: str, alias_name: str, user_id: int
    ) -> dict[str, str]:
        return self._call(self._insights.save_seat_alias, seat_name, alias_name, user_id)

    @staticmethod
    def _call(function, *args, **kwargs):
        try:
            return function(*args, **kwargs)
        except (
            MarketDataUnavailable,
            ProviderError,
            DataQualityError,
            SnapshotSyncError,
            MarketInsightError,
        ) as exc:
            raise AppError("market_data_unavailable", str(exc), 503) from exc


def _context(context) -> dict[str, Any]:
    return {
        "requested_date": context.requested_date,
        "actual_date": context.actual_date,
        "previous_date": context.previous_date,
        "observed_at": context.observed_at,
        "state": context.state.value if context.state else None,
        "carried_forward": context.carried_forward,
        "message": context.message,
    }
