# src/web/api/v2/schemas/calibration_data.py

from __future__ import annotations

from datetime import datetime
from typing import Optional, Dict, Literal

from pydantic import BaseModel, Field

# ==================================================
# Download (Exchange -> DB)  (modo "sync" / síncrono)
# ==================================================

class CalibrationDataDownloadRequest(BaseModel):
    symbol: str = Field(..., examples=["BTC/USDT"])
    timeframe: str = Field(..., examples=["1h"])

    # En esta primera versión, start/end sirven para:
    # - validar coherencia del input
    # - informar en logs/UI
    # pero el sync incremental REAL lo hace sync_ohlcv() desde last_ts hasta expected_last.
    start_date: Optional[datetime] = None
    end_date: Optional[datetime] = None

    dry_run: bool = Field(
        False,
        description="Si True: no descarga; solo valida y devuelve resumen BEFORE/AFTER (sin cambios).",
    )


class CalibrationDataDownloadResponse(BaseModel):
    symbol: str
    timeframe: str

    start_date: Optional[datetime]
    end_date: Optional[datetime]

    started: bool
    dry_run: bool

    inserted_new_rows: int

    first_available_after: Optional[datetime]
    last_available_after: Optional[datetime]
    candles_count_after: int

    message: str


# ==================================================
# Download (Exchange -> DB)  (modo JOB / asíncrono con progreso)
# ==================================================

JobStatus = Literal[
    "created",
    "downloading",
    "processing",
    "saving",
    "done",
    "cancelled",
    "failed",
]

class CalibrationDataDownloadJobStartRequest(BaseModel):
    """
    Request para iniciar un job asíncrono.
    Recomendación: aquí SÍ pedimos start/end para poder estimar velas y limitar descarga.
    """
    symbol: str = Field(..., examples=["BTC/USDT"])
    timeframe: str = Field(..., examples=["1h"])
    start_date: Optional[datetime] = None
    end_date: Optional[datetime] = None

    dry_run: bool = Field(
        False,
        description="Si True: no ejecuta descarga; solo calcula/valida y crea un job 'done' inmediatamente.",
    )


class CalibrationDataDownloadJobStartResponse(BaseModel):
    job_id: str
    status: JobStatus
    message: str


class CalibrationDataDownloadJobStatusResponse(BaseModel):
    job_id: str

    symbol: Optional[str] = None
    timeframe: Optional[str] = None
    start_date: Optional[datetime] = None
    end_date: Optional[datetime] = None

    status: JobStatus
    message: str

    # progreso visual
    progress: int = Field(0, ge=0, le=100)

    # progreso por bloques
    blocks_done: int = 0
    blocks_total: Optional[int] = None

    # métricas útiles para UI
    candles_downloaded: int = 0
    inserted_new_rows: int = 0

    # control
    cancelled: bool = False

    # timestamps opcionales (útil para debug/UX)
    created_at: Optional[datetime] = None
    updated_at: Optional[datetime] = None


class CalibrationDataDownloadJobCancelResponse(BaseModel):
    job_id: str
    status: JobStatus
    message: str

# =========================
# Inspect (DB)
# =========================

class CalibrationDataRequest(BaseModel):
    symbol: str
    timeframe: str
    start_date: Optional[datetime] = None
    end_date: Optional[datetime] = None
    mode: Literal["crypto", "session"] = "crypto"

QualityStatus = Literal["ok", "warning", "fail"]


class DataQualityGaps(BaseModel):
    count: int
    max_gap: Optional[str] = None


class DataQualityCoverage(BaseModel):
    expected: int
    actual: int
    ratio: float


class DataQualityChecks(BaseModel):
    continuity: QualityStatus
    gaps: DataQualityGaps
    coverage: DataQualityCoverage
    volume: QualityStatus


class DataQualityResult(BaseModel):
    status: QualityStatus
    checks: DataQualityChecks
    message: str


class CalibrationDataResponse(BaseModel):
    symbol: str
    timeframe: str
    first_available: Optional[datetime]
    last_available: Optional[datetime]
    candles_count: int
    valid: bool
    data_quality: Optional[DataQualityResult] = None
