"""빗썸 캔들 역방향 페이지네이션 다운로더."""

from __future__ import annotations

import logging
from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import Any

from deepcoin.api.bithumb import BithumbCandleClient, parse_kst_datetime
from deepcoin.config import Settings
from deepcoin.data.candle_store import CandleStore, candle_to_row
from deepcoin.data.intervals import interval_label

logger = logging.getLogger(__name__)


@dataclass
class DownloadResult:
    """단일 인터벌 다운로드 결과."""

    interval_min: int
    requests: int
    saved_rows: int
    oldest_kst: datetime | None
    newest_kst: datetime | None
    reached_target: bool


class CandleDownloader:
    """지정 기간 캔들을 수집해 SQLite에 저장한다."""

    def __init__(self, settings: Settings, client: BithumbCandleClient | None = None) -> None:
        """다운로더를 초기화한다.

        Args:
            settings: 애플리케이션 설정.
            client: API 클라이언트. None이면 기본 생성.
        """
        self.settings = settings
        self.client = client or BithumbCandleClient(
            base_url=settings.api_url,
            count=settings.candle_count,
            sleep_sec=settings.request_sleep_sec,
            retries=settings.request_retries,
        )

    def download_interval(
        self,
        store: CandleStore,
        interval_min: int,
        days: int | None = None,
    ) -> DownloadResult:
        """한 인터벌의 캔들을 역방향으로 수집한다.

        Args:
            store: SQLite 저장소.
            interval_min: 분 단위 (1440=일봉).
            days: 수집 일수. None이면 settings.download_days.

        Returns:
            DownloadResult.
        """
        lookback_days = days if days is not None else self.settings.download_days
        target_start = datetime.now() - timedelta(days=lookback_days)
        table = store.ensure_table(self.settings.symbol, interval_min)

        to_kst: datetime | None = None
        requests = 0
        saved_rows = 0
        oldest_kst: datetime | None = None
        newest_kst: datetime | None = None
        reached_target = False
        seen_oldest: set[str] = set()

        logger.info(
            "수집 시작: %s %s, 목표=%s 이후",
            self.settings.market,
            interval_label(interval_min),
            target_start.strftime("%Y-%m-%d %H:%M:%S"),
        )

        while True:
            batch = self.client.fetch_candles(
                market=self.settings.market,
                interval_min=interval_min,
                to_kst=to_kst,
            )
            requests += 1

            if not batch:
                logger.info("%s: 더 이상 데이터 없음", interval_label(interval_min))
                break

            rows = [
                candle_to_row(c, self.settings.symbol, self.settings.coin_name)
                for c in batch
            ]
            saved_rows += store.upsert_rows(table, rows)

            batch_oldest = parse_kst_datetime(batch[-1]["candle_date_time_kst"])
            batch_newest = parse_kst_datetime(batch[0]["candle_date_time_kst"])

            if newest_kst is None or batch_newest > newest_kst:
                newest_kst = batch_newest
            if oldest_kst is None or batch_oldest < oldest_kst:
                oldest_kst = batch_oldest

            if batch_oldest <= target_start:
                reached_target = True
                logger.info(
                    "%s: 목표 기간 도달 (oldest=%s)",
                    interval_label(interval_min),
                    batch_oldest.strftime("%Y-%m-%d %H:%M:%S"),
                )
                break

            oldest_key = batch[-1]["candle_date_time_kst"]
            if oldest_key in seen_oldest:
                logger.warning("%s: 페이지네이션 정체 — 중단", interval_label(interval_min))
                break
            seen_oldest.add(oldest_key)

            to_kst = batch_oldest

            if requests % 20 == 0:
                logger.info(
                    "%s: 진행 중 requests=%s saved=%s oldest=%s",
                    interval_label(interval_min),
                    requests,
                    saved_rows,
                    batch_oldest.strftime("%Y-%m-%d %H:%M:%S"),
                )

        return DownloadResult(
            interval_min=interval_min,
            requests=requests,
            saved_rows=saved_rows,
            oldest_kst=oldest_kst,
            newest_kst=newest_kst,
            reached_target=reached_target,
        )

    def download_all(self, store: CandleStore, days: int | None = None) -> list[DownloadResult]:
        """설정된 모든 인터벌을 수집한다.

        Args:
            store: SQLite 저장소.
            days: 수집 일수.

        Returns:
            인터벌별 DownloadResult 리스트.
        """
        results: list[DownloadResult] = []
        for interval in self.settings.download_intervals:
            if interval == 1:
                logger.warning("1분봉은 장기 수집 시 요청량이 매우 큽니다 — 건너뜁니다.")
                continue
            result = self.download_interval(store, interval, days=days)
            results.append(result)
        return results
