"""公式解析异常定义。

提供清晰的错误信息，帮助用户快速定位公式解析问题。
"""

import difflib
from typing import List, Optional


class FormulaParseError(Exception):
    """公式解析错误基类。

    Attributes:
        expr: 原始表达式字符串
        lineno: 错误所在行号（从1开始）
        col_offset: 错误所在列号（从0开始）
    """

    def __init__(
        self,
        message: str,
        expr: Optional[str] = None,
        lineno: Optional[int] = None,
        col_offset: Optional[int] = None,
    ):
        self.expr = expr
        self.lineno = lineno
        self.col_offset = col_offset

        # 构建详细错误信息
        full_message = self._format_message(message)
        super().__init__(full_message)

    def _format_message(self, message: str) -> str:
        """格式化错误信息，包含位置指示器。"""
        lines = [f"FormulaParseError: {message}"]

        if self.expr:
            lines.append(f"  公式: {self.expr}")

            # 添加错误位置指示器
            if self.col_offset is not None and self.lineno is not None:
                # 计算错误行在表达式中的起始位置
                expr_lines = self.expr.split("\n")
                if 1 <= self.lineno <= len(expr_lines):
                    error_line = expr_lines[self.lineno - 1]
                    lines.append(f"       {error_line}")
                    # 添加指向错误位置的箭头
                    pointer = " " * (self.col_offset + 7) + "^--- 此处出错"
                    lines.append(pointer)

        return "\n".join(lines)


class UnknownFunctionError(FormulaParseError):
    """未知函数错误。

    当表达式中使用了未注册的函数时抛出。

    Attributes:
        func_name: 未知的函数名
        available: 可用函数列表
        suggestions: 模糊匹配建议列表
    """

    def __init__(
        self,
        func_name: str,
        available: List[str],
        expr: Optional[str] = None,
        lineno: Optional[int] = None,
        col_offset: Optional[int] = None,
    ):
        self.func_name = func_name
        self.available = available

        # 使用 difflib 获取模糊匹配建议
        self.suggestions = difflib.get_close_matches(
            func_name, available, n=3, cutoff=0.5
        )

        # 构建错误信息
        if self.suggestions:
            suggestion_str = ", ".join(f"'{s}'" for s in self.suggestions)
            hint_msg = f"你是不是想找: {suggestion_str}？"
        else:
            # 只显示前10个可用函数
            available_preview = ", ".join(available[:10])
            if len(available) > 10:
                available_preview += f", ... 等共 {len(available)} 个函数"
            hint_msg = f"可用函数预览: {available_preview}"

        msg = f"未知函数 '{func_name}'。{hint_msg}"

        super().__init__(
            message=msg,
            expr=expr,
            lineno=lineno,
            col_offset=col_offset,
        )


class InvalidSyntaxError(FormulaParseError):
    """语法错误。

    当表达式语法不正确或不支持时抛出。
    """

    pass


class UnsupportedOperatorError(InvalidSyntaxError):
    """不支持的运算符错误。

    当使用了不支持的运算符时抛出（如位运算、矩阵运算等）。
    """

    pass


class EmptyExpressionError(FormulaParseError):
    """空表达式错误。"""

    def __init__(self):
        super().__init__("表达式不能为空或只包含空白字符")


class RegistryError(Exception):
    """注册表错误基类。"""

    pass


class DuplicateFunctionError(RegistryError):
    """函数重复注册错误。

    当尝试注册已存在的函数且未设置 force=True 时抛出。
    """

    def __init__(self, func_name: str):
        self.func_name = func_name
        super().__init__(
            f"函数 '{func_name}' 已存在。使用 force=True 覆盖，或选择其他名称。"
        )
