"""
Module to type a Series.
"""

from inspect import stack
from logging import Logger, getLogger
from typing import ClassVar

import numpy as np
from pandas import Series, to_datetime

from genov.helpers.dataframe.df_typer import DfTyper


class STyper:
    """
    Class to type a Series.
    """

    #: Column to type as a string
    TYPE_STRING: str = DfTyper.TYPE_STRING
    #: Column to type as a datetime
    TYPE_DATETIME: str = DfTyper.TYPE_DATETIME
    #: Column to type as a date
    TYPE_DATE: str = DfTyper.TYPE_DATE
    #: Column to type as a float
    TYPE_FLOAT: str = DfTyper.TYPE_FLOAT
    #: Column to type as an integer
    TYPE_INT: str = DfTyper.TYPE_INT

    #: All the eligible types
    TYPES: ClassVar[list[str]] = [TYPE_STRING, TYPE_DATE, TYPE_DATETIME, TYPE_INT, TYPE_FLOAT]

    _obj_logger: Logger = getLogger(__name__)

    def __init__(self, str_type):
        """
        Instantiation of a Series Typer, very similar to DataFrame Typer but more simple.

        Series can be typed as:
        - string
        - datetime
        - date
        - float
        - int.

        :param str_type: the target type
        """
        self._str_target_type: list[dict[str:str]] = str_type

    def s_type(self, s_instance: Series) -> Series:
        """
        The function cast the Series s_instance according to the target type provided at initialization.

        TypeError exceptions are thrown:
        - During the transformation, whenever errors occur.

        :param s_instance: the Series to type
        :return: the same Series withing the relevant type
        """
        self._obj_logger.debug("Function '%s - %s' is called", stack()[0].filename, stack()[0].function)

        if self._str_target_type == self.TYPE_STRING:
            try:
                s_instance = s_instance.astype(str)
            except Exception as an_exception:
                msg = f"Exception when typing Series to '{self._str_target_type}'."
                raise TypeError(msg) from an_exception

        elif self._str_target_type == self.TYPE_DATETIME:
            try:
                s_instance = to_datetime(s_instance)
            except Exception as an_exception:
                msg = f"Exception when typing Series to '{self._str_target_type}'."
                raise TypeError(msg) from an_exception

        elif self._str_target_type == self.TYPE_DATE:
            try:
                s_instance = to_datetime(s_instance).dt.date
            except Exception as an_exception:
                msg = f"Exception when typing Series to '{self._str_target_type}'."
                raise TypeError(msg) from an_exception

            # The below happens when column is empty, and fully made of NaT
            if str(s_instance.dtype).startswith("datetime"):
                s_instance = s_instance.astype("object")

        elif self._str_target_type == self.TYPE_FLOAT:
            try:
                s_instance = s_instance.astype(np.float64)  # to_numeric(arg=s_instance, downcast="float")
            except Exception as an_exception:
                msg = f"Exception when typing Series to '{self._str_target_type}'."
                raise TypeError(msg) from an_exception

        elif self._str_target_type == self.TYPE_INT:
            try:
                s_instance = s_instance.astype(np.int64)  # to_numeric(arg=s_instance, downcast="integer")
            except Exception as an_exception:
                msg = f"Exception when typing Series to '{self._str_target_type}'."
                raise TypeError(msg) from an_exception
        else:
            msg = (
                f"When transcoding the date extract, we did not know what to do with Series "
                f"as we don't know the target type '{self._str_target_type}'..."
            )
            raise TypeError(msg)

        self._obj_logger.debug("Function '%s - %s' is returning", stack()[0].filename, stack()[0].function)

        return s_instance
