"""빗썸 Public REST API — 캔들 조회."""

from __future__ import annotations

import logging
import time
from datetime import datetime
from typing import Any

import requests

from deepcoin.data.intervals import INTERVAL_DAILY, INTERVAL_MONTHLY, INTERVAL_WEEKLY

logger = logging.getLogger(__name__)

# 인터벌(분) → 빗썸 candles API 경로 세그먼트
_CALENDAR_PATHS: dict[int, str] = {
    INTERVAL_DAILY: "days",
    INTERVAL_WEEKLY: "weeks",
    INTERVAL_MONTHLY: "months",
}

KST_FMT = "%Y-%m-%d %H:%M:%S"
KST_FMT_T = "%Y-%m-%dT%H:%M:%S"


def parse_kst_datetime(value: str) -> datetime:
    """KST 캔들 시각 문자열을 datetime으로 변환한다.

    Args:
        value: `yyyy-MM-dd HH:mm:ss` 또는 ISO 형식.

    Returns:
        naive datetime (KST 기준).
    """
    normalized = value.strip().replace("T", " ")
    return datetime.strptime(normalized, KST_FMT)


def format_kst_datetime(dt: datetime) -> str:
    """datetime을 빗썸 `to` 파라미터 형식으로 포맷한다.

    Args:
        dt: KST 기준 시각.

    Returns:
        `yyyy-MM-dd HH:mm:ss` 문자열.
    """
    return dt.strftime(KST_FMT)


class BithumbCandleClient:
    """빗썸 캔들 API 클라이언트."""

    def __init__(
        self,
        base_url: str = "https://api.bithumb.com",
        count: int = 200,
        sleep_sec: float = 0.35,
        retries: int = 3,
    ) -> None:
        """클라이언트를 초기화한다.

        Args:
            base_url: API 베이스 URL.
            count: 요청당 캔들 개수 (최대 200).
            sleep_sec: 연속 요청 간 대기(초).
            retries: 실패 시 재시도 횟수.
        """
        self.base_url = base_url.rstrip("/")
        self.count = min(max(count, 1), 200)
        self.sleep_sec = sleep_sec
        self.retries = retries
        self._session = requests.Session()
        self._session.headers.update({"accept": "application/json"})

    def fetch_candles(
        self,
        market: str,
        interval_min: int,
        to_kst: datetime | None = None,
    ) -> list[dict[str, Any]]:
        """캔들 배치를 조회한다 (최신순).

        Args:
            market: 거래 페어 (예: KRW-WLD).
            interval_min: 분 단위. 1440이면 일봉 API 사용.
            to_kst: 조회 기준 시각(KST). 해당 시각 캔들은 제외.

        Returns:
            캔들 dict 리스트. API 오류 시 빈 리스트.

        Raises:
            requests.RequestException: 재시도 후에도 네트워크 실패.
        """
        if interval_min in _CALENDAR_PATHS:
            segment = _CALENDAR_PATHS[interval_min]
            url = f"{self.base_url}/v1/candles/{segment}"
            params: dict[str, Any] = {"market": market, "count": self.count}
        else:
            url = f"{self.base_url}/v1/candles/minutes/{interval_min}"
            params = {"market": market, "count": self.count}

        if to_kst is not None:
            params["to"] = format_kst_datetime(to_kst)

        last_error: Exception | None = None
        for attempt in range(1, self.retries + 1):
            try:
                response = self._session.get(url, params=params, timeout=30)
                if response.status_code == 429:
                    wait = self.sleep_sec * attempt * 3
                    logger.warning("Rate limit 429 — %ss 대기 후 재시도", wait)
                    time.sleep(wait)
                    continue
                response.raise_for_status()
                payload = response.json()
                if isinstance(payload, dict) and "error" in payload:
                    logger.error("API error: %s", payload["error"])
                    return []
                if not isinstance(payload, list):
                    logger.error("Unexpected response type: %s", type(payload))
                    return []
                time.sleep(self.sleep_sec)
                return payload
            except requests.RequestException as exc:
                last_error = exc
                wait = self.sleep_sec * attempt * 2
                logger.warning("Request failed (%s/%s): %s", attempt, self.retries, exc)
                time.sleep(wait)

        if last_error is not None:
            raise last_error
        return []
