from __future__ import annotations

from typing import Any, Dict, Optional

import pandas as pd

from xfintech.data.common.cache import Cache
from xfintech.data.common.coolant import Coolant
from xfintech.data.common.params import Params
from xfintech.data.common.retry import Retry
from xfintech.data.job import JobHouse
from xfintech.data.source.baostock.job import BaostockJob
from xfintech.data.source.baostock.session.session import Session
from xfintech.data.source.baostock.stock.minuteline.constant import (
    KEY,
    NAME,
    PAGINATE,
    SOURCE,
    TARGET,
)


@JobHouse.register(KEY, alias=KEY)
class Minuteline(BaostockJob):
    """
    描述:
    - 获取A股分钟线行情数据
    - 支持5分钟、15分钟、30分钟、60分钟K线数据
    - 支持前复权、后复权、不复权三种复权方式
    - API文档: http://www.baostock.com/mainContent?file=stockKData.md
    - SCALE: Individual
    - TYPE: Partitioned
    - PAGINATE: 10000 rows / 100 pages

    属性:
    - name: str, 作业名称 'minuteline'。
    - key: str, 作业键 '/baostock/minuteline'。
    - session: Session, Baostock会话对象。
    - source: TableInfo, 源表信息（Baostock原始格式）。
    - target: TableInfo, 目标表信息（转换后格式）。
    - params: Params, 查询参数。
        - code: str, 必需, 股票代码（如 "sh.600000" 或 "sz.000001"）
        - start_date: str, 可选, 开始日期（YYYY-MM-DD 或 YYYYMMDD）
        - end_date: str, 可选, 结束日期（YYYY-MM-DD 或 YYYYMMDD）
        - frequency: str, 可选, 频率(5/15/30/60)，默认5
        - adjustflag: str, 可选, 复权类型(1=后复权/2=前复权/3=不复权)，默认3
    - coolant: Coolant, 请求冷却控制。
    - paginate: Paginate, 分页控制（pagesize=10000, pagelimit=100）。
    - retry: Retry, 重试策略。
    - cache: Cache, 缓存管理。

    方法:
    - run(): 执行作业，返回分钟线行情DataFrame。
    - _run(): 内部执行逻辑，处理日期参数并调用API。
    - transform(data): 转换数据格式，将源格式转为目标格式。

    例子:
    ```python
        from xfintech.data.source.baostock.session import Session
        from xfintech.data.source.baostock.stock.minuteline import Minuteline

        session = Session()
        job = Minuteline(
            session=session,
            params={
                "code": "sh.600000",
                "start_date": "20260101",
                "end_date": "20260131",
                "frequency": "5",
                "adjustflag": "3"
            }
        )
        df = job.run()
    ```
    """

    def __init__(
        self,
        session: Session,
        params: Optional[Params | Dict[str, Any]] = None,
        coolant: Optional[Coolant | Dict[str, Any]] = None,
        retry: Optional[Retry | Dict[str, Any]] = None,
        cache: Optional[Cache | Dict[str, str] | bool] = None,
    ) -> None:
        super().__init__(
            name=NAME,
            key=KEY,
            session=session,
            source=SOURCE,
            target=TARGET,
            params=params,
            coolant=coolant,
            paginate=PAGINATE,
            retry=retry,
            cache=cache,
        )

    def _run(self) -> pd.DataFrame:
        cached = self._load_cache()
        if cached is not None:
            return cached

        payload = self.params.to_dict()
        payload = self._parse_string_params(
            payload,
            keys=[
                "code",
                "frequency",
                "adjustflag",
            ],
        )
        payload = self._parse_date_params(
            payload,
            keys=["start_date", "end_date"],
        )
        fields = self.source.list_column_names()
        payload["fields"] = ",".join(fields)

        # Fetch data from API
        data = self._fetchall(
            api=self.connection.query_history_k_data_plus,
            **payload,
        )
        result = self.transform(data)
        self._save_cache(result)
        return result

    # Transform logic
    def transform(
        self,
        data: pd.DataFrame,
    ) -> pd.DataFrame:
        cols = self.target.list_column_names()
        if data is None or data.empty:
            return pd.DataFrame(columns=cols)

        out = data.copy()
        out["code"] = out["code"].astype(str)
        out["date"] = out["date"].astype(str)
        out["time"] = out["time"].astype(str)
        out["adjustflag"] = out["adjustflag"].astype(str)

        # Convert numeric fields
        numeric_fields = [
            "open",
            "high",
            "low",
            "close",
            "volume",
            "amount",
        ]
        for field in numeric_fields:
            out[field] = pd.to_numeric(
                out[field],
                errors="coerce",
            )

        # Finalize output
        out = out[cols].drop_duplicates()
        out = out.sort_values(by=["code", "date", "time"])
        out = out.reset_index(drop=True)
        self.markpoint("transform[OK]")
        return out
