# src/data/downloader.py

from datetime import datetime, timezone
import pandas as pd
from typing import Optional

from src.data.fetcher import DataFetcher
from src.data.processor import DataProcessor
from src.data import indicators
from src.utils.logger import log


class OHLCVDownloader:
    """
    Adapter que convierte DataFetcher en un downloader
    compatible con StorageManager y con soporte de progreso.
    """

    def __init__(self, exchange_name: str, api_key=None, api_secret=None):
        self.fetcher = DataFetcher(
            exchange_name=exchange_name,
            api_key=api_key,
            api_secret=api_secret,
        )
        self.processor = DataProcessor()

    # --------------------------------------------------
    # Post-procesado (SIN CAMBIOS de lógica)
    # --------------------------------------------------

    def _post_process(
        self,
        df_new: pd.DataFrame,
        *,
        storage,
        symbol: str,
        timeframe: str,
        adx_length: int = 14,
    ) -> pd.DataFrame:

        if df_new is None or df_new.empty:
            return df_new

        df_new = self.processor.clean_data(df_new)
        df_new = self.processor.calculate_returns(df_new)

        # --------------------------------------------------
        # ADX con contexto histórico (warm-up)
        # --------------------------------------------------
        warmup = adx_length * 5
        start_ts = df_new.index.min()

        df_tail = storage.load_ohlcv_tail(
            symbol=symbol,
            timeframe=timeframe,
            end_ts=start_ts,
            limit=warmup,
        )

        if df_tail is not None and not df_tail.empty:
            df_full = pd.concat([df_tail, df_new]).sort_index()
        else:
            df_full = df_new.copy()

        df_full = indicators.add_adx(
            df_full,
            length=adx_length,
            column_name="adx",
        )

        # devolver SOLO filas nuevas
        return df_full.loc[df_new.index]

    # --------------------------------------------------
    # Descarga por rango (YA compatible con storage)
    # --------------------------------------------------

    def download_range(
        self,
        *,
        symbol: str,
        timeframe: str,
        start: datetime,
        end: datetime,
        storage,
        job: Optional[object] = None,   # 🔧 CAMBIO
    ) -> pd.DataFrame:

        log.info(
            f"⬇️ Descargando OHLCV {symbol} {timeframe} "
            f"{start} → {end}"
        )

        def progress_cb(iteration, df_block):
            if job:
                job.update(
                    status="downloading",
                    message=f"Descargando bloque {iteration}",
                    blocks_done=iteration,
                    progress=min(90, iteration * 3),
                )

        def should_cancel():
            return bool(job and job.cancelled)

        df = self.fetcher.fetch_historical(
            symbol=symbol,
            timeframe=timeframe,
            since=start,
            until=end,
            progress_cb=progress_cb,
            should_cancel=should_cancel,
        )

        if job and job.cancelled:
            log.warning("Descarga cancelada antes del post-procesado")
            return pd.DataFrame()

        if job:
            job.update(
                status="processing",
                message="Procesando indicadores",
                progress=92,
            )

        return self._post_process(
            df_new=df,
            storage=storage,
            symbol=symbol,
            timeframe=timeframe,
        )

    # --------------------------------------------------
    # Descarga completa (CAMBIO CLAVE)
    # --------------------------------------------------

    def download_full(
        self,
        *,
        symbol: str,
        timeframe: str,
        storage,                        # 🔧 CAMBIO: ahora es obligatorio
        job: Optional[object] = None,   # 🔧 CAMBIO
    ) -> pd.DataFrame:

        log.warning(
            f"DB vacía → descarga completa {symbol} {timeframe}"
        )

        start = datetime(2017, 1, 1, tzinfo=timezone.utc)
        end = datetime.now(timezone.utc)

        def progress_cb(iteration, df_block):
            if job:
                job.update(
                    status="downloading",
                    message=f"Descargando bloque {iteration}",
                    blocks_done=iteration,
                    progress=min(90, iteration * 2),
                )

        def should_cancel():
            return bool(job and job.cancelled)

        df = self.fetcher.fetch_historical(
            symbol=symbol,
            timeframe=timeframe,
            since=start,
            until=end,
            progress_cb=progress_cb,
            should_cancel=should_cancel,
        )

        if job and job.cancelled:
            log.warning("Descarga cancelada antes del post-procesado")
            return pd.DataFrame()

        if job:
            job.update(
                status="processing",
                message="Procesando indicadores",
                progress=92,
            )

        return self._post_process(
            df_new=df,
            storage=storage,
            symbol=symbol,
            timeframe=timeframe,
        )
