from __future__ import annotations

__all__ = ["SequentialPreprocessor"]

from collections.abc import Sequence

from coola.utils import str_indent, str_sequence
from pandas import DataFrame

from flamme.preprocessor.base import BasePreprocessor, setup_preprocessor


class SequentialPreprocessor(BasePreprocessor):
    r"""Implements a preprocessor to apply sequentially several
    preprocessors.

    Args:
    ----
        preprocessors (``Sequence``): Specifies the preprocessors or
            their configurations.

    Example usage:

    .. code-block:: pycon

        >>> import pandas as pd
        >>> from flamme.preprocessor import (
        ...     SequentialPreprocessor,
        ...     StripStrPreprocessor,
        ...     ToNumericPreprocessor,
        ... )
        >>> preprocessor = SequentialPreprocessor(
        ...     [
        ...         StripStrPreprocessor(columns=["col1", "col3"]),
        ...         ToNumericPreprocessor(columns=["col1", "col2"]),
        ...     ]
        ... )
        >>> preprocessor
        SequentialPreprocessor(
          (0): StripStrPreprocessor(columns=('col1', 'col3'))
          (1): ToNumericPreprocessor(columns=('col1', 'col2'))
        )
        >>> df = pd.DataFrame(
        ...     {
        ...         "col1": [1, 2, 3, 4, 5],
        ...         "col2": ["1", "2", "3", "4", "5"],
        ...         "col3": ["a ", " b", "  c  ", "d", "e"],
        ...         "col4": ["a ", " b", "  c  ", "d", "e"],
        ...     }
        ... )
        >>> df
           col1 col2   col3   col4
        0     1    1     a      a
        1     2    2      b      b
        2     3    3    c      c
        3     4    4      d      d
        4     5    5      e      e
        >>> df = preprocessor.preprocess(df)
        >>> df
           col1 col2 col3   col4
        0     1    1    a     a
        1     2    2    b      b
        2     3    3    c    c
        3     4    4    d      d
        4     5    5    e      e
    """

    def __init__(self, preprocessors: Sequence[BasePreprocessor | dict]) -> None:
        self._preprocessors = tuple(
            setup_preprocessor(preprocessor) for preprocessor in preprocessors
        )

    def __repr__(self) -> str:
        args = ""
        if self._preprocessors:
            args = f"\n  {str_indent(str_sequence(self._preprocessors))}\n"
        return f"{self.__class__.__qualname__}({args})"

    def preprocess(self, df: DataFrame) -> DataFrame:
        for preprocessor in self._preprocessors:
            df = preprocessor.preprocess(df)
        return df
