from __future__ import annotations

from typing import Generic, List, Union

from pygismeteo_base import models, types
from pygismeteo_base.step_n import mixins
from pygismeteo_base.step_n.abc import StepNABC
from typing_extensions import Literal, overload

from ._http import RequestsClient


class StepN(
    Generic[types.TStepNDays, types.TStepNModel, types.TStepNModelItem],
    StepNABC[RequestsClient],
):
    __slots__ = ()

    @overload
    def by_coordinates(
        self,
        latitude: types.Latitude,
        longitude: types.Longitude,
        days: types.TStepNDays,
        *,
        as_list: Literal[True] = ...,
    ) -> List[types.TStepNModelItem]: ...

    @overload
    def by_coordinates(
        self,
        latitude: types.Latitude,
        longitude: types.Longitude,
        days: types.TStepNDays,
        *,
        as_list: Literal[False],
    ) -> types.TStepNModel: ...

    @overload
    def by_coordinates(
        self,
        latitude: types.Latitude,
        longitude: types.Longitude,
        days: types.TStepNDays,
        *,
        as_list: bool,
    ) -> Union[List[types.TStepNModelItem], types.TStepNModel]: ...

    def by_coordinates(
        self,
        latitude: types.Latitude,
        longitude: types.Longitude,
        days: types.TStepNDays,
        *,
        as_list: bool = True,
    ) -> Union[List[types.TStepNModelItem], types.TStepNModel]:
        """По координатам.

        Args:
            latitude (-90 ≤ float ≤ 90):
                Широта.
            longitude (-180 ≤ float ≤ 180):
                Долгота.
            days (
                step3:  1 ≤ int ≤ 10,
                step6:  3 ≤ int ≤ 10,
                step24: 3 ≤ int ≤ 10,
            ):
                Количество дней.
            as_list (bool):
                Вернуть Model.root (list[ModelItem]) вместо Model.
                По умолчанию True.
        """
        url, params = self._get_params_by_coordinates(
            latitude, longitude, days=days
        )
        return self._get_result(url, params=params, as_list=as_list)

    @overload
    def by_id(
        self,
        id: types.LocalityID,  # noqa: A002
        days: types.TStepNDays,
        *,
        as_list: Literal[True] = ...,
    ) -> List[types.TStepNModelItem]: ...

    @overload
    def by_id(
        self,
        id: types.LocalityID,  # noqa: A002
        days: types.TStepNDays,
        *,
        as_list: Literal[False],
    ) -> types.TStepNModel: ...

    @overload
    def by_id(
        self,
        id: types.LocalityID,  # noqa: A002
        days: types.TStepNDays,
        *,
        as_list: bool,
    ) -> Union[List[types.TStepNModelItem], types.TStepNModel]: ...

    def by_id(
        self,
        id: types.LocalityID,  # noqa: A002
        days: types.TStepNDays,
        *,
        as_list: bool = True,
    ) -> Union[List[types.TStepNModelItem], types.TStepNModel]:
        """По ID географического объекта.

        Args:
            id (int ≥ 1):
                ID географического объекта. Получить можно через поиск.
            days (
                step3:  1 ≤ int ≤ 10,
                step6:  3 ≤ int ≤ 10,
                step24: 3 ≤ int ≤ 10,
            ):
                Количество дней.
            as_list (bool):
                Вернуть Model.root (list[ModelItem]) вместо Model.
                По умолчанию True.
        """
        url, params = self._get_params_by_id(id, days=days)
        return self._get_result(url, params=params, as_list=as_list)

    @overload
    def _get_result(
        self, url: str, *, params: types.Params, as_list: Literal[True]
    ) -> List[types.TStepNModelItem]: ...

    @overload
    def _get_result(
        self, url: str, *, params: types.Params, as_list: Literal[False]
    ) -> types.TStepNModel: ...

    @overload
    def _get_result(
        self, url: str, *, params: types.Params, as_list: bool
    ) -> Union[List[types.TStepNModelItem], types.TStepNModel]: ...

    def _get_result(
        self, url: str, *, params: types.Params, as_list: bool
    ) -> Union[List[types.TStepNModelItem], types.TStepNModel]:
        response = self._session.get_response(url, params=params)
        model = self._model().model_validate_json(response)
        return (
            model.response.root  # type: ignore[return-value]
            if as_list
            else model.response
        )


class Step3(
    mixins.Step3Mixin,
    StepN[types.Step3Days, models.step3.Model, models.step3.ModelItem],
):
    """Погода с шагом 3 часа."""

    __slots__ = ()


class Step6(
    mixins.Step6Mixin,
    StepN[types.Step6Days, models.step6.Model, models.step6.ModelItem],
):
    """Погода с шагом 6 часов."""

    __slots__ = ()


class Step24(
    mixins.Step24Mixin,
    StepN[types.Step24Days, models.step24.Model, models.step24.ModelItem],
):
    """Погода с шагом 24 часа."""

    __slots__ = ()
