"""Client for weather data endpoints (observations, climate, climate_gaps)."""

from __future__ import annotations

from pathlib import Path
from typing import Any

from therminal._http import HttpSession, normalize_date, to_dataframe
from therminal.config import TherminalConfig
from therminal.models import Climate, Observation


class WeatherHistory:
    """Client for historical weather data (observations, climate, climate_gaps)."""

    def __init__(
        self,
        config: TherminalConfig | None = None,
        _session: HttpSession | None = None,
    ) -> None:
        self._config = config or TherminalConfig()
        self._http = _session or HttpSession(self._config)
        self._owns_session = _session is None

    def close(self) -> None:
        if self._owns_session:
            self._http.close()

    def __enter__(self) -> WeatherHistory:
        return self

    def __exit__(self, *a: Any) -> None:
        self.close()

    # ── Observations ─────────────────────────────────────────

    def observations(
        self,
        station: str | None = None,
        from_date: str | None = None,
        to_date: str | None = None,
        obs_type: str | None = None,
        resolution: str | None = None,
        units: str | None = None,
        tz: str | None = None,
        format: str | None = None,
        columns: list[str] | None = None,
        limit: int = 1000,
        offset: int = 0,
        as_dataframe: bool = False,
        save_path: str | Path | None = None,
    ) -> list[Observation] | Path | Any:
        """Get weather observations (METAR/SPECI or 1-minute OMO).

        Args:
            station: Station code (e.g., NYC, LAX).
            from_date: Start date (RFC3339 or YYYY-MM-DD).
            to_date: End date (RFC3339 or YYYY-MM-DD).
            obs_type: Filter by type (METAR, SPECI). Ignored when resolution=1min.
            resolution: Observation resolution. None (default) = hourly METAR,
                "1min" = 1-minute ASOS OMO data (integer C).
            units: Unit system (raw, metric, imperial).
            tz: Timezone (UTC, station, or IANA name).
            format: Output format: None/json (default), csv, parquet.
            columns: List of columns to include.
            as_dataframe: Return as Pandas DataFrame.
            save_path: File path for csv/parquet output.

        Returns:
            list[Observation] for JSON, Path for csv/parquet, DataFrame if as_dataframe.
        """
        resolved = self._config.resolve(units=units, tz=tz, station=station)
        params: dict[str, Any] = {
            "station": resolved["station"],
            "from": normalize_date(from_date),
            "to": normalize_date(to_date),
            "type": obs_type,
            "resolution": resolution,
            "units": resolved["units"],
            "tz": resolved["tz"],
            "offset": offset,
        }

        if format in ("csv", "parquet"):
            params["limit"] = limit
            return self._http.handle_format(
                "/api/v1/observations", params, format, columns,
                as_dataframe, "observed_at", "observations", save_path,
            )

        # DataFrame users: use parquet for full dataset (no JSON 50K cap)
        if as_dataframe and format is None and limit == 1000:
            return self._http.handle_format(
                "/api/v1/observations", {**params}, "parquet", columns,
                True, "observed_at", "observations", save_path,
            )

        # JSON path — auto-paginate for full result set
        if limit != 1000:
            params["limit"] = limit
        if columns:
            params["columns"] = ",".join(columns)
        data = self._http.get_all("/api/v1/observations", params)

        if as_dataframe:
            return to_dataframe(data, index_col="observed_at")

        return [Observation.from_api_dict(d) for d in data]

    # ── Climate ──────────────────────────────────────────────

    def climate(
        self,
        station: str | None = None,
        from_date: str | None = None,
        to_date: str | None = None,
        units: str | None = None,
        format: str | None = None,
        columns: list[str] | None = None,
        limit: int = 1000,
        offset: int = 0,
        as_dataframe: bool = False,
        save_path: str | Path | None = None,
    ) -> list[Climate] | Path | Any:
        """Get daily climate reports (NWS CLI products).

        Args:
            station: Station code (e.g., NYC).
            from_date: Start date (YYYY-MM-DD).
            to_date: End date (YYYY-MM-DD).
            units: Unit system (raw, metric, imperial).
            format: Output format: None/json (default), csv, parquet.
            columns: List of columns to include.
            as_dataframe: Return as Pandas DataFrame.
            save_path: File path for csv/parquet output.

        Returns:
            list[Climate] for JSON, Path for csv/parquet, DataFrame if as_dataframe.
        """
        resolved = self._config.resolve(units=units, station=station)
        params: dict[str, Any] = {
            "station": resolved["station"],
            "from": from_date,
            "to": to_date,
            "units": resolved["units"],
            "offset": offset,
        }

        if format in ("csv", "parquet"):
            params["limit"] = limit
            return self._http.handle_format(
                "/api/v1/climate", params, format, columns,
                as_dataframe, "observation_date", "climate", save_path,
            )

        # DataFrame users: use parquet for full dataset
        if as_dataframe and format is None and limit == 1000:
            return self._http.handle_format(
                "/api/v1/climate", {**params}, "parquet", columns,
                True, "observation_date", "climate", save_path,
            )

        # JSON path
        if limit != 1000:
            params["limit"] = limit
        if columns:
            params["columns"] = ",".join(columns)
        data = self._http.get_all("/api/v1/climate", params)

        if as_dataframe:
            return to_dataframe(data, index_col="observation_date")

        return [Climate.from_api_dict(d) for d in data]

    # ── Analysis ─────────────────────────────────────────────

    def climate_gaps(
        self,
        station: str | None = None,
        from_date: str | None = None,
        to_date: str | None = None,
        as_dataframe: bool = False,
    ) -> list[dict[str, Any]] | Any:
        """Find gaps in climate report history for a station.

        Args:
            station: Station code (required).
            from_date: Start date (YYYY-MM-DD). Defaults to 5 years ago.
            to_date: End date (YYYY-MM-DD). Defaults to today.
            as_dataframe: Return as Pandas DataFrame.

        Returns:
            list[dict] or DataFrame if as_dataframe.
        """
        resolved = self._config.resolve(station=station)
        params: dict[str, Any] = {
            "station": resolved["station"],
            "from": from_date,
            "to": to_date,
        }
        data = self._http.get("/api/v1/analysis/climate-gaps", params)
        return to_dataframe(data) if as_dataframe else data


# Backward compat alias (v1.0.0-v1.0.2 used WeatherClient)
WeatherClient = WeatherHistory
