"""
函数式编程工具包
提供管道组合、函数包装、Monad 等函数式编程抽象
"""

import typing

import abc
from dataclasses import dataclass
from functools import reduce, wraps
from typing import Any, Callable, Generic, TypeVar, ParamSpec

__all__ = [
    'Pipeline',
    'PipelineExecution',
    'pipe',
    'FuncWrapper',
    'Result',
    'Maybe',
    'Validation',
    'returns_result',
    'returns_maybe',
    'safe',
    'nullable',
]

P = ParamSpec('P')
V = TypeVar('V')
E = TypeVar('E')
T = TypeVar('T')
U = TypeVar('U')


class PipelineError(Exception):
    """管道执行错误"""

    def __init__(self, func_name: str, step: int, original_error: Exception):
        self.func_name = func_name
        self.step = step
        self.original_error = original_error
        super().__init__(f"步骤 {step} 中函数 '{func_name}' 执行失败: {original_error}")


@dataclass(frozen=True)
class PipelineExecution(Generic[T]):
    """
    管道执行结果封装（线程安全）

    Attributes
    ----------
    - `result`: 管道最终返回值
    - `trace`: 每一步的中间结果列表
    """

    result: T
    trace: list[Any]


class Pipeline:
    def __init__(self, *funcs: Callable):
        """
        初始化函数管道

        将多个函数串联执行，前一个函数的输出作为后一个函数的输入

        特性：
        - 线程安全：不存储执行状态到实例
        - 提供详细的错误追踪
        - 支持 `|` 操作符语法
        - 使用 `run()` 方法可获取执行轨迹

        Parameters
        ----------
        - `funcs`: `Callable`, 要串联的函数序列

        Examples
        ----------
        ```py
        >>> def double(x):
        ...     return x * 2
        >>> def add_one(x):
        ...     return x + 1
        >>>
        >>> pipeline = Pipeline(double, add_one)
        >>> result = pipeline(3)
        >>> print(result)  # 输出: 7
        >>>
        >>> # 如需调试信息，使用 run() 方法
        >>> execution = pipeline.run(3)
        >>> print(execution.result)  # 7
        >>> print(execution.trace)   # [6, 7]
        ```
        """
        if not funcs:
            raise ValueError('Pipeline 至少需要一个函数')
        if not all(callable(f) for f in funcs):
            raise TypeError('所有参数必须是可调用对象')
        self.funcs = funcs

    def run(self, *args: Any, **kwargs: Any) -> PipelineExecution:
        """
        执行管道，返回包含结果和执行轨迹的 PipelineExecution 对象

        Parameters
        ----------
        - `args`: 传递给第一个函数的位置参数
        - `kwargs`: 传递给第一个函数的关键字参数

        Returns
        ----------
        - `PipelineExecution`, 包含 result 和 trace 的执行结果
        """
        trace: list[Any] = []

        # 第一个函数使用原始参数调用
        try:
            current_value = self.funcs[0](*args, **kwargs)
            trace.append(current_value)
        except Exception as e:
            raise PipelineError(
                func_name=getattr(self.funcs[0], '__name__', repr(self.funcs[0])),
                step=1,
                original_error=e,
            ) from e

        # 后续函数逐个调用，不进行隐式解包
        for i, func in enumerate(self.funcs[1:], start=2):
            try:
                current_value = func(current_value)
                trace.append(current_value)
            except Exception as e:
                raise PipelineError(
                    func_name=getattr(func, '__name__', repr(func)),
                    step=i,
                    original_error=e,
                ) from e

        return PipelineExecution(result=current_value, trace=trace)

    def __call__(self, *args: Any, **kwargs: Any) -> Any:
        """执行管道，直接返回最终结果"""
        return self.run(*args, **kwargs).result

    def __or__(self, other: Callable) -> 'Pipeline':
        """支持 `pipeline | func` 语法"""
        if isinstance(other, Pipeline):
            return Pipeline(*self.funcs, *other.funcs)
        return Pipeline(*self.funcs, other)

    def __repr__(self) -> str:
        func_names = [getattr(f, '__name__', repr(f)) for f in self.funcs]
        return f"Pipeline({' -> '.join(func_names)})"


class FuncWrapper:
    def __init__(
        self,
        func: Callable,
        fn_args: tuple | None = None,
        call_alias: str | typing.Sequence[str] | None = None,
        **fn_kwargs,
    ) -> None:
        """
        初始化函数包装器

        预设参数并支持别名调用

        Parameters
        ----------
        - `func`: `Callable`, 要包装的函数
        - `fn_args`: `tuple | None`, 预设的位置参数（会追加到调用参数后）, by default None
        - `call_alias`: `str | list[str] | None`, 可通过别名调用，例如 `wrapper.run()` 等价于 `wrapper()`, by default None
        - `fn_kwargs`: 预设的关键字参数（调用时的参数优先级更高）

        Examples
        ----------
        ```py
        >>> def greet(name, greeting="Hello"):
        ...     return f"{greeting}, {name}!"
        >>>
        >>> greet_casual = FuncWrapper(greet, greeting="Hi", call_alias="run")
        >>> greet_casual("Alice")  # 输出: "Hi, Alice!"
        >>> greet_casual.run("Bob")  # 输出: "Hi, Bob!"
        ```
        """
        self.func = func
        self.preset_args = fn_args or ()
        self.preset_kwargs = fn_kwargs
        self.alias = self._normalize_alias(call_alias)

        # 保留原函数的元数据
        wraps(func)(self)

    @staticmethod
    def _normalize_alias(alias: str | typing.Sequence[str] | None) -> list[str]:
        """标准化别名为列表"""
        if alias is None:
            return []
        if isinstance(alias, str):
            return [alias]
        return list(alias)

    def __getattr__(self, item: str) -> Any:
        """支持别名调用和透传原函数属性"""
        if item in self.alias:
            return self.__call__
        # 尝试从原函数获取属性
        try:
            return getattr(self.func, item)
        except AttributeError:
            raise AttributeError(f"'{type(self).__name__}' 和被包装函数均无属性 '{item}'")

    def __call__(self, *args, **kwargs) -> Any:
        """调用包装函数，合并预设参数"""
        # 预设参数在后，调用参数在前（调用时的参数优先）
        merged_args = args + self.preset_args
        # 调用时的 kwargs 会覆盖预设的 kwargs
        merged_kwargs = {**self.preset_kwargs, **kwargs}
        return self.func(*merged_args, **merged_kwargs)

    def __repr__(self) -> str:
        func_name = getattr(self.func, '__name__', repr(self.func))
        alias_str = f', aliases={self.alias}' if self.alias else ''
        return f'FuncWrapper({func_name}{alias_str})'


def pipe(inputs: Any, *funcs: Callable) -> Any:
    """
    管道函数：将输入依次传递给多个函数

    将输入依次传递给多个函数

    Parameters
    ----------
    - `inputs`: `Any`, 初始输入值
    - `funcs`: `Callable`, 要依次执行的函数序列

    Returns
    ----------
    - `Any`, 最后一个函数的返回值

    Examples
    ----------
    ```py
    >>> pipe(3, lambda x: x * 2, lambda x: x + 1)
    7
    ```
    """
    return reduce(lambda val, func: func(val), funcs, inputs)


# ============================================================================
# Monad 抽象基类
# ============================================================================


class Monad(abc.ABC, Generic[T]):
    """Monad 抽象基类"""

    @abc.abstractmethod
    def bind(self, func: Callable[[T], 'Monad[T]']) -> 'Monad[T]':
        """将函数应用于 Monad 内的值"""
        pass

    @abc.abstractmethod
    def unwrap(self) -> T:
        """提取 Monad 内的值（可能失败）"""
        pass

    def map(self, func: Callable[[T], Any]) -> 'Monad':
        """映射内部值（自动包装返回值）"""
        return self.bind(lambda x: type(self).pure(func(x)))

    @classmethod
    @abc.abstractmethod
    def pure(cls, value: T) -> 'Monad[T]':
        """将值包装进 Monad"""
        pass


# ============================================================================
# Result Monad
# ============================================================================


class Result(Monad[V], Generic[V, E]):
    def __init__(self, value: V | None = None, err: E | None = None):
        """
        初始化 Result Monad

        表示可能成功或失败的计算

        特性：
        - 成功时包含值 (V)
        - 失败时包含错误 (E)
        - 链式调用时自动传播错误

        Parameters
        ----------
        - `value`: `V | None`, 成功时的值, by default None
        - `err`: `E | None`, 失败时的错误, by default None

        Examples
        ----------
        ```py
        >>> def safe_divide(x, y):
        ...     if y == 0:
        ...         return Result.from_err("除数不能为零")
        ...     return Result.from_ok(x / y)
        >>>
        >>> result = Result.from_ok(10).bind(lambda x: safe_divide(x, 2))
        >>> result.unwrap()  # 输出: 5.0
        ```
        """
        if value is None and err is None:
            raise ValueError('Result 必须包含 value 或 err 之一')
        self.value = value
        self.err = err

    @property
    def is_ok(self) -> bool:
        """是否为成功状态"""
        return self.err is None

    @classmethod
    def from_ok(cls, value: V) -> 'Result[V, None]':
        """创建成功的 Result"""
        return typing.cast(Result[V, None], cls(value=value, err=None))

    @classmethod
    def from_err(cls, err: E) -> 'Result[None, E]':
        """创建失败的 Result"""
        return typing.cast(Result[None, E], cls(value=None, err=err))

    @classmethod
    def pure(cls, value: V) -> 'Result[V, None]':
        """将值包装为成功的 Result"""
        return cls.from_ok(value)

    def bind(self, func: Callable[[V], 'Result[Any, E]']) -> 'Result[Any, E]':
        """应用函数（仅在成功时）"""
        if self.is_ok:
            assert self.value is not None
            try:
                return func(self.value)
            except Exception as e:
                return typing.cast(Result[Any, E], Result.from_err(e))
        return typing.cast(Result[Any, E], self)

    def map_err(self, func: Callable[[E], Any]) -> 'Result[V, Any]':
        """映射错误值"""
        if not self.is_ok:
            assert self.err is not None
            return typing.cast(Result[V, Any], Result.from_err(func(self.err)))
        return typing.cast(Result[V, Any], self)

    def unwrap(self) -> V:
        """提取值（失败时抛出异常）"""
        if self.is_ok:
            assert self.value is not None
            return self.value
        raise ValueError(f'无法 unwrap 失败的 Result: {self.err}')

    def unwrap_or(self, default: V) -> V:
        """提取值或返回默认值"""
        if self.is_ok:
            assert self.value is not None
            return self.value
        return default

    def unwrap_or_else(self, func: Callable[[E], V]) -> V:
        """提取值或通过函数计算默认值"""
        if self.is_ok:
            assert self.value is not None
            return self.value
        assert self.err is not None
        return func(self.err)

    def __repr__(self) -> str:
        if self.is_ok:
            return f'Result.Ok({self.value!r})'
        return f'Result.Err({self.err!r})'


# ============================================================================
# Maybe Monad
# ============================================================================


class Maybe(Monad[V], Generic[V]):
    def __init__(self, value: V | None = None):
        """
        初始化 Maybe Monad

        表示可能存在或不存在的值

        特性：
        - Some(value): 包含值
        - None: 不包含值
        - 链式调用时自动处理空值

        Parameters
        ----------
        - `value`: `V | None`, 包含的值, by default None

        Examples
        ----------
        ```py
        >>> def safe_sqrt(x):
        ...     if x < 0:
        ...         return Maybe.none()
        ...     return Maybe.some(x ** 0.5)
        >>>
        >>> Maybe.some(16).bind(safe_sqrt).unwrap()  # 输出: 4.0
        >>> Maybe.some(-4).bind(safe_sqrt).unwrap()  # 抛出异常
        ```
        """
        self._value = value

    @property
    def is_some(self) -> bool:
        """是否包含值"""
        return self._value is not None

    @classmethod
    def some(cls, value: V) -> 'Maybe[V]':
        """创建包含值的 Maybe"""
        if value is None:
            raise ValueError('Maybe.some() 不能接受 None，请使用 Maybe.none()')
        return cls(value)

    @classmethod
    def none(cls) -> 'Maybe[None]':
        """创建空的 Maybe"""
        return typing.cast(Maybe[None], cls(None))

    @classmethod
    def pure(cls, value: V) -> 'Maybe[V]':
        """将值包装为 Maybe（自动处理 None）"""
        if value is not None:
            return cls.some(value)
        return typing.cast(Maybe[V], cls.none())

    def bind(self, func: Callable[[V], 'Maybe[Any]']) -> 'Maybe[Any]':
        """应用函数（仅在包含值时）"""
        if self.is_some:
            assert self._value is not None
            try:
                return func(self._value)
            except Exception:
                return Maybe.none()
        return typing.cast(Maybe[Any], self)

    def unwrap(self) -> V:
        """提取值（空值时抛出异常）"""
        if self.is_some:
            assert self._value is not None
            return self._value
        raise ValueError('无法 unwrap 空的 Maybe')

    def unwrap_or(self, default: V) -> V:
        """提取值或返回默认值"""
        if self.is_some:
            assert self._value is not None
            return self._value
        return default

    def unwrap_or_else(self, func: Callable[[], V]) -> V:
        """提取值或通过函数计算默认值"""
        if self.is_some:
            assert self._value is not None
            return self._value
        return func()

    def __repr__(self) -> str:
        if self.is_some:
            return f'Maybe.Some({self._value!r})'
        return 'Maybe.None'


# ============================================================================
# Validation Monad
# ============================================================================


class Validation(Monad[V], Generic[V, E]):
    def __init__(self, value: V | None = None, errors: typing.Sequence[E] | None = None):
        """
        初始化 Validation Monad

        支持累积多个错误（通过 `combine` 方法）

        特性：
        - Success：包含值
        - Failure：包含错误列表
        - `bind` 依然是短路的（因为后续步骤依赖前一步的输出）
        - `combine` 方法支持并行验证并累积所有错误
        - 适用于表单验证等需要显示所有错误的场景

        Parameters
        ----------
        - `value`: `V | None`, 成功时的值, by default None
        - `errors`: `list[E] | None`, 失败时的错误列表, by default None

        Examples
        ----------
        ```py
        >>> def validate_age(age):
        ...     if age < 0:
        ...         return Validation.failure(["年龄不能为负数"])
        ...     if age > 150:
        ...         return Validation.failure(["年龄不合理"])
        ...     return Validation.success(age)
        >>>
        >>> # 可以组合多个验证并累积错误
        >>> result = validate_age(-5)
        >>> result.errors  # ["年龄不能为负数"]
        ```
        """
        if value is None and not errors:
            raise ValueError('Validation 必须包含 value 或 errors')
        self.value = value
        self.errors = errors or []

    @property
    def is_success(self) -> bool:
        """是否验证成功"""
        return not self.errors

    @classmethod
    def success(cls, value: V) -> 'Validation[V, E]':
        """创建成功的 Validation"""
        return cls(value=value, errors=[])

    @classmethod
    def failure(cls, errors: typing.Sequence[E]) -> 'Validation[None, E]':
        """创建失败的 Validation"""
        return typing.cast(Validation[None, E], cls(value=None, errors=errors))

    @classmethod
    def pure(cls, value: V) -> 'Validation[V, E]':
        """将值包装为成功的 Validation"""
        return cls.success(value)

    def bind(self, func: Callable[[V], 'Validation[U, E]']) -> 'Validation[U, E]':
        """应用函数（累积错误）"""
        if self.is_success:
            assert self.value is not None
            try:
                result = func(self.value)
                # 即使当前成功，也要保留之前累积的错误
                if not result.is_success:
                    return typing.cast(
                        Validation[U, E],
                        Validation.failure(list(self.errors) + list(result.errors)),
                    )
                return result
            except Exception as e:
                return typing.cast(
                    Validation[U, E],
                    Validation.failure(list(self.errors) + [typing.cast(E, e)]),
                )
        return typing.cast(Validation[U, E], self)

    def combine(self, other: 'Validation[U, E]') -> 'Validation[tuple[V, U], E]':
        """组合两个 Validation（累积双方错误）"""
        if self.is_success and other.is_success:
            assert self.value is not None
            assert other.value is not None
            return Validation.success((self.value, other.value))
        return typing.cast(
            Validation[tuple[V, U], E],
            Validation.failure(list(self.errors) + list(other.errors)),
        )

    def unwrap(self) -> V:
        """提取值（失败时抛出异常）"""
        if self.is_success:
            assert self.value is not None
            return self.value
        raise ValueError(f'无法 unwrap 失败的 Validation，错误: {self.errors}')

    def unwrap_or(self, default: V) -> V:
        """提取值或返回默认值"""
        if self.is_success:
            assert self.value is not None
            return self.value
        return default

    def __repr__(self) -> str:
        if self.is_success:
            return f'Validation.Success({self.value!r})'
        return f'Validation.Failure({self.errors!r})'


# ============================================================================
# 装饰器工具
# ============================================================================


def returns_result(func: Callable[P, V]) -> Callable[P, Result[V, Exception]]:
    """
    装饰器：将函数返回值包装为 Result

    捕获异常并转换为 Result.Err

    Parameters
    ----------
    - `func`: `Callable[P, V]`, 要包装的函数

    Returns
    ----------
    - `Callable[P, Result[V, Exception]]`, 返回 Result 的函数

    Examples
    ----------
    ```py
    >>> @returns_result
    ... def divide(x, y):
    ...     return x / y
    >>>
    >>> result = divide(10, 2)
    >>> result.unwrap()  # 5.0
    >>> result = divide(10, 0)
    >>> result.is_ok  # False
    ```
    """

    @wraps(func)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> Result[V, Exception]:
        try:
            return typing.cast(
                Result[V, Exception], Result.from_ok(func(*args, **kwargs))
            )
        except Exception as e:
            return typing.cast(Result[V, Exception], Result.from_err(e))

    return wrapper


def returns_maybe(func: Callable[P, V | None]) -> Callable[P, Maybe[V]]:
    """
    装饰器：将函数返回值包装为 Maybe

    自动将 None 转换为 Maybe.none()

    Parameters
    ----------
    - `func`: `Callable[P, V | None]`, 可能返回 None 的函数

    Returns
    ----------
    - `Callable[P, Maybe[V]]`, 返回 Maybe 的函数

    Examples
    ----------
    ```py
    >>> @returns_maybe
    ... def find_user(user_id):
    ...     users = {1: "Alice", 2: "Bob"}
    ...     return users.get(user_id)
    >>>
    >>> result = find_user(1)
    >>> result.unwrap()  # "Alice"
    >>> result = find_user(999)
    >>> result.is_some  # False
    ```
    """

    @wraps(func)
    def wrapper(*args: P.args, **kwargs: P.kwargs) -> Maybe[V]:
        result = func(*args, **kwargs)
        return Maybe.pure(result)

    return wrapper


def safe(func: Callable[P, V]) -> Callable[P, Result[V, Exception]]:
    """
    装饰器：安全执行函数（等同于 returns_result）

    提供语义更清晰的别名

    Examples
    ----------
    ```py
    >>> @safe
    ... def parse_int(s):
    ...     return int(s)
    >>>
    >>> parse_int("123").unwrap()  # 123
    >>> parse_int("abc").is_ok  # False
    ```
    """
    return returns_result(func)


def nullable(func: Callable[P, V | None]) -> Callable[P, Maybe[V]]:
    """
    装饰器：处理可空返回值（等同于 returns_maybe）

    提供语义更清晰的别名

    Examples
    ----------
    ```py
    >>> @nullable
    ... def get_config(key):
    ...     config = {"debug": True}
    ...     return config.get(key)
    >>>
    >>> get_config("debug").unwrap()  # True
    >>> get_config("missing").unwrap_or(False)  # False
    ```
    """
    return returns_maybe(func)
