"""REST Operations für TimeSeries-Endpoints."""

from __future__ import annotations

from collections.abc import Callable
from datetime import datetime

from loguru import logger

from ..models.rest import (
    Quotations,
    QuotationsPayload,
    QuotationValue,
    SetTimeSeriesDataInput,
    TimeSeriesData,
    TimeSeriesDataPayload,
)
from ..models.timeseries import (
    Aggregation,
    Interval,
    Quality,
    QuotationBehavior,
    TimeSeriesValue,
    TimeUnit,
)


class TimeSeriesOperations:
    """
    REST-Operationen für TimeSeries-Endpoints.

    Bietet Methoden zum Abrufen und Setzen von Zeitreihen-Daten
    sowie zum Abrufen von Quotierungen.
    """

    def __init__(self, rest_get_func: Callable, rest_post_func: Callable):
        """
        Initialisiert die TimeSeries-Operationen.

        Args:
            rest_get_func: Funktion für GET-Requests
            rest_post_func: Funktion für POST-Requests
        """
        self._get = rest_get_func
        self._post = rest_post_func
        logger.debug("TimeSeriesOperations initialized")

    def get_data(
        self,
        time_series_ids: list[int],
        from_time: datetime,
        to_time: datetime,
        time_unit: TimeUnit | None = None,
        multiplier: int | None = None,
        aggregation: Aggregation | None = None,
        unit: str | None = None,
        time_zone: str | None = None,
        quotation_time: datetime | None = None,
        quotation_exactly_at: bool = False,
        quotation_behavior: QuotationBehavior | None = None,
        exclude_qualities: list[Quality] | None = [Quality.MISSING],
    ) -> TimeSeriesDataPayload:
        """
        Ruft Zeitreihen-Daten für eine oder mehrere TimeSeries ab.

        Args:
            time_series_ids: IDs der abzufragenden TimeSeries
            from_time: Start des Zeitbereichs
            to_time: Ende des Zeitbereichs
            time_unit: Zeiteinheit für Intervall-Aggregation (optional)
            multiplier: Multiplikator für Intervall-Aggregation (optional)
            aggregation: Aggregationsart (optional, Standard: TimeSeries-Default)
            unit: Ziel-Einheit für Wert-Konvertierung (optional)
            time_zone: Zeitzone für Ausgabe (optional)
            quotation_time: Quotierungszeitpunkt (optional)
            quotation_exactly_at: Exakte Quotierung am angegebenen Zeitpunkt
            quotation_behavior: Quotierungs-Verhalten (optional)
            exclude_qualities: Auszuschließende Qualitäten (optional)

        Returns:
            TimeSeriesDataPayload: Objekt mit Daten für alle angefragten TimeSeries
        """
        logger.info(
            "Getting timeseries data",
            time_series_ids=time_series_ids,
            from_time=from_time,
            to_time=to_time,
        )

        params = {
            "timeSeriesIds": time_series_ids,
            "from": from_time.isoformat(),
            "to": to_time.isoformat(),
        }

        if time_unit and multiplier:
            params["timeUnit"] = time_unit.value
            params["multiplier"] = multiplier

        if aggregation:
            params["aggregation"] = aggregation.value

        if unit:
            params["unit"] = unit

        if time_zone:
            params["timeZone"] = time_zone

        if quotation_time:
            params["quotationTime"] = quotation_time.isoformat()

        params["quotationExactlyAt"] = quotation_exactly_at

        if quotation_behavior:
            params["quotationBehavior"] = quotation_behavior.value

        if exclude_qualities:
            params["excludeQualities"] = [q.value for q in exclude_qualities]

        result = self._get("/timeseries/data", params=params)

        # Parse Response
        data_list = []
        for ts_data in result.get("data", []):
            interval_data = ts_data.get("interval", {})
            interval = Interval(
                time_unit=TimeUnit(interval_data.get("timeUnit")),
                multiplier=interval_data.get("multiplier", 1),
            )

            values = []
            for val in ts_data.get("values", []):
                ts_value = TimeSeriesValue(
                    time=datetime.fromisoformat(val["time"].replace("Z", "+00:00")),
                    value=val["value"],
                    quality=Quality(val["quality"]) if val.get("quality") else None,
                )
                values.append(ts_value)

            ts_data_obj = TimeSeriesData(
                time_series_id=ts_data["timeSeriesId"],
                interval=interval,
                unit=ts_data.get("unit"),
                time_zone=ts_data.get("timeZone"),
                values=values,
            )
            data_list.append(ts_data_obj)

        logger.info(f"Retrieved data for {len(data_list)} timeseries")
        return TimeSeriesDataPayload(data=data_list)

    def set_data(
        self,
        data_inputs: list[SetTimeSeriesDataInput],
    ) -> None:
        """
        Setzt Zeitreihen-Daten für eine oder mehrere TimeSeries.

        Args:
            data_inputs: Liste von SetTimeSeriesDataInput-Objekten
        """
        logger.info(f"Setting timeseries data for {len(data_inputs)} timeseries")

        # Konvertiere zu JSON
        payload = []
        for data_input in data_inputs:
            item = {
                "timeSeriesId": data_input.time_series_id,
                "values": [
                    {
                        "time": val.time.isoformat(),
                        "value": val.value,
                        **({"quality": val.quality.value} if val.quality else {}),
                    }
                    for val in data_input.values
                ],
            }

            if data_input.interval:
                item["interval"] = {
                    "timeUnit": data_input.interval.time_unit.value,
                    "multiplier": data_input.interval.multiplier,
                }

            if data_input.unit:
                item["unit"] = data_input.unit

            if data_input.time_zone:
                item["timeZone"] = data_input.time_zone

            if data_input.quotation_time:
                item["quotationTime"] = data_input.quotation_time.isoformat()

            payload.append(item)

        logger.debug(f"Posting {len(payload)} timeseries data entries")
        self._post("/timeseries/data", json=payload)
        logger.info("Timeseries data set successfully")

    def get_quotations(
        self,
        time_series_ids: list[int],
        from_time: datetime,
        to_time: datetime,
        aggregated: bool = False,
    ) -> QuotationsPayload:
        """
        Ruft Quotierungen für eine oder mehrere TimeSeries ab.

        Args:
            time_series_ids: IDs der abzufragenden TimeSeries
            from_time: Start des Zeitbereichs
            to_time: Ende des Zeitbereichs
            aggregated: Quotierungen aggregiert zurückgeben

        Returns:
            QuotationsPayload: Objekt mit allen Quotierungen
        """
        logger.info(
            "Getting timeseries quotations",
            time_series_ids=time_series_ids,
            from_time=from_time,
            to_time=to_time,
            aggregated=aggregated,
        )

        params = {
            "timeSeriesIds": time_series_ids,
            "from": from_time.isoformat(),
            "to": to_time.isoformat(),
            "aggregated": aggregated,
        }

        result = self._get("/timeseries/quotations", params=params)

        # Parse Response
        items = []
        for quotation in result.get("items", []):
            values = []
            for val in quotation.get("values", []):
                quot_val = QuotationValue(
                    time=datetime.fromisoformat(val["time"].replace("Z", "+00:00")),
                    from_time=datetime.fromisoformat(
                        val["from"].replace("Z", "+00:00")
                    ),
                    to_time=datetime.fromisoformat(val["to"].replace("Z", "+00:00")),
                )
                values.append(quot_val)

            quot_obj = Quotations(
                time_series_id=quotation["timeSeriesId"],
                values=values,
            )
            items.append(quot_obj)

        logger.info(f"Retrieved quotations for {len(items)} timeseries")
        return QuotationsPayload(items=items)
