"""
Functional programming tools
"""

from functools import reduce
from typing import Any, Callable, List

__all__ = ['Pipeline', 'pipe', 'FuncWrapper']


class PipelineError(Exception):
    """Custom exception for pipeline errors."""


class Pipeline:
    """
    创建一个复合函数，将多个函数串联起来，依次执行。

    Example
    ----------

    ```py
    def double(x):
        return x * 2
    def add_one(x):
        return x + 1
    composed_func = Compose(double, add_one)
    result = composed_func(3)
    print(result)
    7
    print(composed_func.get_intermediate_results())
    [6, 7]
    ```
    """

    def __init__(self, *funcs: Callable):
        if not funcs:
            raise ValueError('Compose requires at least one function.')
        self.funcs = funcs
        self._intermediate_results: List[Any] = []

    def __call__(self, *args: Any, **kwargs: Any) -> Any:
        self._intermediate_results = []

        # First function call with initial arguments
        try:
            current_value = self.funcs[0](*args, **kwargs)
            self._intermediate_results.append(current_value)
        except Exception as e:
            raise PipelineError(
                f"Error in function '{self.funcs[0].__name__}' at step 1: {str(e)}"
            ) from e

        # Subsequent function calls
        for i, func in enumerate(self.funcs[1:], start=2):
            try:
                current_value = (
                    func(*current_value)
                    if isinstance(current_value, tuple)
                    else func(current_value)
                )
                self._intermediate_results.append(current_value)
            except Exception as e:
                raise PipelineError(
                    f"Error in function '{func.__name__}' at step {i}: {str(e)}"
                ) from e

        return current_value

    def get_intermediate_results(self) -> List[Any]:
        return self._intermediate_results


class FuncWrapper:
    def __init__(
        self,
        func: Callable,
        fn_args: tuple = None,
        call_alias: str | list[str] | None = None,
        **fn_kwargs,
    ) -> None:
        self.func = func
        self.args = fn_args if fn_args is not None else ()
        self.kwargs = fn_kwargs
        if call_alias:
            self._set_callable_alias(call_alias)

    def _set_callable_alias(self, alias: str | list[str]):
        if isinstance(alias, str):
            alias = [alias]
        self.alias = alias

    def __getattr__(self, item: str) -> Any:
        if item in self.alias:
            return self.__call__
        return getattr(self.func, item)

    def __call__(self, *args, **kwargs) -> Any:
        # 合并传入的参数和初始化时的参数
        args = args + self.args
        kwargs = {**self.kwargs, **kwargs}
        return self.func(*args, **kwargs)


def pipe(inputs: Any, *funcs: Callable) -> Any:
    """
    将多个函数串联起来，依次执行。

    Parameters
    ----------
    - `inputs` : Any
        - 要传递给第一个函数的输入值
    - `*funcs` : Callable
        - 要串联的函数列表f

    Returns
    ----------
    - `Any`: 执行结果
    """
    if not funcs:
        return inputs

    return reduce(lambda val, func: func(val), funcs, inputs)
