"""
Symphra Excel库的通用验证工具模块

提供可重用的验证函数，用于参数校验、数据验证和格式检查。
所有验证失败时抛出 ValidationError 异常，并提供清晰的错误信息。
"""

import re
from pathlib import Path
from typing import Any, Callable, Collection, TypeVar

from ..utils.exceptions import ValidationError

T = TypeVar("T")


# ==================== 单元格和范围验证 ====================


def validate_cell_reference(cell_ref: str) -> tuple[str, int]:
    """
    验证并解析Excel单元格引用

    参数:
        cell_ref: 单元格引用字符串 (例如: 'A1', 'AB10', 'XFD1048576')

    返回:
        (列字母, 行号)元组，列字母已转换为大写

    异常:
        ValidationError: 如果单元格引用格式无效

    示例:
        >>> validate_cell_reference("A1")
        ('A', 1)
        >>> validate_cell_reference("AB123")
        ('AB', 123)
        >>> validate_cell_reference("invalid")
        ValidationError: 无效的单元格引用: invalid
    """
    if not cell_ref or not isinstance(cell_ref, str):
        raise ValidationError(
            f"单元格引用必须是非空字符串，收到: {type(cell_ref).__name__}"
        )

    cell_ref = cell_ref.strip()
    match = re.match(r"^([A-Za-z]+)(\d+)$", cell_ref)

    if not match:
        raise ValidationError(
            f"无效的单元格引用: '{cell_ref}'。格式应为字母+数字，如 'A1' 或 'AB123'"
        )

    column, row = match.groups()
    row_num = int(row)

    # 验证Excel限制
    if row_num < 1 or row_num > 1048576:
        raise ValidationError(f"行号必须在 1-1048576 之间，收到: {row_num}")

    return column.upper(), row_num


def validate_range_reference(range_ref: str) -> tuple[str, str]:
    """
    验证Excel范围引用格式

    参数:
        range_ref: 范围引用字符串 (例如: 'A1:B10', 'Sheet1!A1:B10')

    返回:
        (起始单元格, 结束单元格)元组

    异常:
        ValidationError: 如果范围引用格式无效

    示例:
        >>> validate_range_reference("A1:B10")
        ('A1', 'B10')
        >>> validate_range_reference("A1")
        ('A1', 'A1')
    """
    if not range_ref or not isinstance(range_ref, str):
        raise ValidationError(
            f"范围引用必须是非空字符串，收到: {type(range_ref).__name__}"
        )

    range_ref = range_ref.strip()

    # 移除工作表名称（如果存在）
    if "!" in range_ref:
        _, range_ref = range_ref.split("!", 1)

    if ":" not in range_ref:
        # 单个单元格
        validate_cell_reference(range_ref)
        return range_ref, range_ref

    parts = range_ref.split(":", 1)
    if len(parts) != 2:
        raise ValidationError(f"无效的范围引用: '{range_ref}'。格式应为 'A1:B10'")

    start_cell, end_cell = parts
    validate_cell_reference(start_cell)
    validate_cell_reference(end_cell)

    return start_cell.strip(), end_cell.strip()


# ==================== 文件路径验证 ====================


def validate_file_path(
    file_path: str | Path,
    *,
    must_exist: bool = False,
    must_be_file: bool = False,
    must_be_dir: bool = False,
    extensions: Collection[str] | None = None,
) -> Path:
    """
    验证文件路径并返回Path对象

    参数:
        file_path: 文件路径（字符串或Path对象）
        must_exist: 如果为True，路径必须存在
        must_be_file: 如果为True，路径必须是文件
        must_be_dir: 如果为True，路径必须是目录
        extensions: 允许的文件扩展名集合（如 ['.xlsx', '.xls']）

    返回:
        验证后的Path对象

    异常:
        ValidationError: 如果路径验证失败

    示例:
        >>> validate_file_path("/tmp/test.xlsx")
        Path('/tmp/test.xlsx')
        >>> validate_file_path("test.txt", extensions=['.xlsx'])
        ValidationError: 文件扩展名必须是 .xlsx 之一，收到: .txt
    """
    if file_path is None:
        raise ValidationError("文件路径不能为 None")

    if isinstance(file_path, str):
        if not file_path.strip():
            raise ValidationError("文件路径不能为空字符串")
        file_path = Path(file_path)
    elif not isinstance(file_path, Path):
        raise ValidationError(
            f"文件路径必须是 str 或 Path 类型，收到: {type(file_path).__name__}"
        )

    # 检查存在性
    if must_exist and not file_path.exists():
        raise ValidationError(f"路径不存在: {file_path}")

    # 检查文件类型
    if must_be_file and file_path.exists() and not file_path.is_file():
        raise ValidationError(f"路径不是文件: {file_path}")

    if must_be_dir and file_path.exists() and not file_path.is_dir():
        raise ValidationError(f"路径不是目录: {file_path}")

    # 检查扩展名
    if extensions:
        ext = file_path.suffix.lower()
        normalized_exts = [
            e.lower() if e.startswith(".") else f".{e.lower()}" for e in extensions
        ]
        if ext not in normalized_exts:
            ext_list = ", ".join(normalized_exts)
            raise ValidationError(f"文件扩展名必须是 {ext_list} 之一，收到: {ext}")

    return file_path


def validate_excel_file_path(
    file_path: str | Path,
    *,
    must_exist: bool = False,
) -> Path:
    """
    验证Excel文件路径

    参数:
        file_path: 文件路径
        must_exist: 如果为True，文件必须存在

    返回:
        验证后的Path对象

    异常:
        ValidationError: 如果路径不是有效的Excel文件
    """
    return validate_file_path(
        file_path,
        must_exist=must_exist,
        must_be_file=must_exist,  # 只有存在时才检查是否为文件
        extensions=[".xlsx", ".xlsm", ".xltx", ".xltm"],
    )


# ==================== 颜色值验证 ====================


def validate_color_hex(color: str) -> str:
    """
    验证十六进制颜色值

    参数:
        color: 颜色值字符串 (例如: '#FFFFFF', 'FFFFFF', '#FFF')

    返回:
        标准化的颜色值（大写，带#前缀，8位ARGB格式）

    异常:
        ValidationError: 如果颜色格式无效

    示例:
        >>> validate_color_hex("#FFFFFF")
        '#FFFFFFFF'
        >>> validate_color_hex("FF0000")
        '#FFFF0000'
        >>> validate_color_hex("#FFF")
        '#FFFFFFFF'
    """
    if not color or not isinstance(color, str):
        raise ValidationError(f"颜色值必须是非空字符串，收到: {type(color).__name__}")

    color = color.strip().upper()

    # 移除#前缀（如果有）
    if color.startswith("#"):
        color = color[1:]

    # 验证格式：3位、6位或8位十六进制
    if not re.match(r"^[0-9A-F]{3}$|^[0-9A-F]{6}$|^[0-9A-F]{8}$", color):
        raise ValidationError(
            f"无效的十六进制颜色值: '{color}'。"
            f"格式应为 3位(RGB)、6位(RRGGBB) 或 8位(AARRGGBB) 十六进制数字"
        )

    # 转换为8位ARGB格式
    if len(color) == 3:
        # RGB转换为RRGGBB格式
        color = "".join([c * 2 for c in color])

    if len(color) == 6:
        # RRGGBB转换为AARRGGBB格式（添加完全不透明的alpha通道）
        color = "FF" + color

    return f"#{color}"


def validate_color_rgb(
    r: int, g: int, b: int, a: int = 255
) -> tuple[int, int, int, int]:
    """
    验证RGB(A)颜色值

    参数:
        r: 红色分量 (0-255)
        g: 绿色分量 (0-255)
        b: 蓝色分量 (0-255)
        a: Alpha透明度 (0-255)，默认255（不透明）

    返回:
        验证后的(R, G, B, A)元组

    异常:
        ValidationError: 如果颜色值超出范围

    示例:
        >>> validate_color_rgb(255, 0, 0)
        (255, 0, 0, 255)
        >>> validate_color_rgb(300, 0, 0)
        ValidationError: RGB颜色分量必须在 0-255 之间
    """
    values = {"R": r, "G": g, "B": b, "A": a}

    for name, value in values.items():
        if not isinstance(value, int):
            raise ValidationError(
                f"{name} 分量必须是整数，收到: {type(value).__name__}"
            )
        if not 0 <= value <= 255:
            raise ValidationError(f"{name} 分量必须在 0-255 之间，收到: {value}")

    return r, g, b, a


# ==================== 数值范围验证 ====================


def validate_number_range(
    value: int | float,
    *,
    min_value: int | float | None = None,
    max_value: int | float | None = None,
    inclusive: bool = True,
    value_name: str = "值",
) -> int | float:
    """
    验证数值是否在指定范围内

    参数:
        value: 要验证的数值
        min_value: 最小值（可选）
        max_value: 最大值（可选）
        inclusive: 如果为True，包含边界值；否则为开区间
        value_name: 值的名称，用于错误消息

    返回:
        验证后的值

    异常:
        ValidationError: 如果值超出范围

    示例:
        >>> validate_number_range(5, min_value=0, max_value=10)
        5
        >>> validate_number_range(15, min_value=0, max_value=10)
        ValidationError: 值必须在 0 到 10 之间
    """
    if not isinstance(value, (int, float)):
        raise ValidationError(f"{value_name}必须是数字，收到: {type(value).__name__}")

    if min_value is not None:
        if inclusive and value < min_value:
            raise ValidationError(f"{value_name}必须 >= {min_value}，收到: {value}")
        elif not inclusive and value <= min_value:
            raise ValidationError(f"{value_name}必须 > {min_value}，收到: {value}")

    if max_value is not None:
        if inclusive and value > max_value:
            raise ValidationError(f"{value_name}必须 <= {max_value}，收到: {value}")
        elif not inclusive and value >= max_value:
            raise ValidationError(f"{value_name}必须 < {max_value}，收到: {value}")

    return value


def validate_positive_number(
    value: int | float,
    *,
    allow_zero: bool = False,
    value_name: str = "值",
) -> int | float:
    """
    验证数值是否为正数

    参数:
        value: 要验证的数值
        allow_zero: 如果为True，允许0
        value_name: 值的名称，用于错误消息

    返回:
        验证后的值

    异常:
        ValidationError: 如果值不是正数
    """
    if not isinstance(value, (int, float)):
        raise ValidationError(f"{value_name}必须是数字，收到: {type(value).__name__}")

    if allow_zero:
        if value < 0:
            raise ValidationError(f"{value_name}必须 >= 0，收到: {value}")
    else:
        if value <= 0:
            raise ValidationError(f"{value_name}必须 > 0，收到: {value}")

    return value


# ==================== 字符串验证 ====================


def validate_non_empty_string(
    value: str,
    *,
    value_name: str = "字符串",
    strip: bool = True,
) -> str:
    """
    验证字符串非空

    参数:
        value: 要验证的字符串
        value_name: 值的名称，用于错误消息
        strip: 如果为True，先去除首尾空白

    返回:
        验证后的字符串（如果strip=True则已去除空白）

    异常:
        ValidationError: 如果字符串为空或None
    """
    if value is None:
        raise ValidationError(f"{value_name}不能为 None")

    if not isinstance(value, str):
        raise ValidationError(f"{value_name}必须是字符串，收到: {type(value).__name__}")

    if strip:
        value = value.strip()

    if not value:
        raise ValidationError(f"{value_name}不能为空")

    return value


def validate_string_length(
    value: str,
    *,
    min_length: int | None = None,
    max_length: int | None = None,
    value_name: str = "字符串",
) -> str:
    """
    验证字符串长度

    参数:
        value: 要验证的字符串
        min_length: 最小长度（可选）
        max_length: 最大长度（可选）
        value_name: 值的名称，用于错误消息

    返回:
        验证后的字符串

    异常:
        ValidationError: 如果字符串长度不符合要求
    """
    if not isinstance(value, str):
        raise ValidationError(f"{value_name}必须是字符串，收到: {type(value).__name__}")

    length = len(value)

    if min_length is not None and length < min_length:
        raise ValidationError(
            f"{value_name}长度必须 >= {min_length}，当前长度: {length}"
        )

    if max_length is not None and length > max_length:
        raise ValidationError(
            f"{value_name}长度必须 <= {max_length}，当前长度: {length}"
        )

    return value


def validate_string_pattern(
    value: str,
    pattern: str,
    *,
    value_name: str = "字符串",
    flags: int = 0,
) -> str:
    """
    验证字符串是否匹配正则表达式模式

    参数:
        value: 要验证的字符串
        pattern: 正则表达式模式
        value_name: 值的名称，用于错误消息
        flags: 正则表达式标志

    返回:
        验证后的字符串

    异常:
        ValidationError: 如果字符串不匹配模式
    """
    if not isinstance(value, str):
        raise ValidationError(f"{value_name}必须是字符串，收到: {type(value).__name__}")

    if not re.match(pattern, value, flags):
        raise ValidationError(f"{value_name}格式无效: '{value}'，应匹配模式: {pattern}")

    return value


# ==================== 集合和类型验证 ====================


def validate_in_collection(
    value: T,
    collection: Collection[T],
    *,
    value_name: str = "值",
) -> T:
    """
    验证值是否在指定集合中

    参数:
        value: 要验证的值
        collection: 允许的值集合
        value_name: 值的名称，用于错误消息

    返回:
        验证后的值

    异常:
        ValidationError: 如果值不在集合中
    """
    if value not in collection:
        allowed = ", ".join(str(v) for v in collection)
        raise ValidationError(f"{value_name}必须是以下之一: {allowed}，收到: {value}")

    return value


def validate_type(
    value: Any,
    expected_type: type | tuple[type, ...],
    *,
    value_name: str = "值",
    allow_none: bool = False,
) -> Any:
    """
    验证值的类型

    参数:
        value: 要验证的值
        expected_type: 期望的类型或类型元组
        value_name: 值的名称，用于错误消息
        allow_none: 如果为True，允许None值

    返回:
        验证后的值

    异常:
        ValidationError: 如果值类型不匹配
    """
    if value is None and allow_none:
        return value

    if not isinstance(value, expected_type):
        if isinstance(expected_type, tuple):
            type_names = " 或 ".join(t.__name__ for t in expected_type)
        else:
            type_names = expected_type.__name__

        raise ValidationError(
            f"{value_name}必须是 {type_names} 类型，收到: {type(value).__name__}"
        )

    return value


def validate_callable(
    value: Any,
    *,
    value_name: str = "函数",
) -> Callable[..., Any]:
    """
    验证值是否可调用

    参数:
        value: 要验证的值
        value_name: 值的名称，用于错误消息

    返回:
        验证后的可调用对象

    异常:
        ValidationError: 如果值不可调用
    """
    if not callable(value):
        raise ValidationError(
            f"{value_name}必须是可调用对象，收到: {type(value).__name__}"
        )

    return value


# ==================== 复合验证 ====================


def validate_percentage(
    value: int | float,
    *,
    value_name: str = "百分比",
) -> float:
    """
    验证百分比值（0-100）

    参数:
        value: 要验证的百分比值
        value_name: 值的名称，用于错误消息

    返回:
        验证后的值（转换为float）

    异常:
        ValidationError: 如果值不在 0-100 范围内
    """
    validated = validate_number_range(
        value,
        min_value=0,
        max_value=100,
        inclusive=True,
        value_name=value_name,
    )
    return float(validated)


def validate_opacity(
    value: float,
    *,
    value_name: str = "不透明度",
) -> float:
    """
    验证不透明度值（0.0-1.0）

    参数:
        value: 要验证的不透明度值
        value_name: 值的名称，用于错误消息

    返回:
        验证后的值

    异常:
        ValidationError: 如果值不在 0.0-1.0 范围内
    """
    return validate_number_range(
        value,
        min_value=0.0,
        max_value=1.0,
        inclusive=True,
        value_name=value_name,
    )


def validate_dimension(
    width: int | None = None,
    height: int | None = None,
    *,
    min_width: int = 1,
    max_width: int = 16384,
    min_height: int = 1,
    max_height: int = 16384,
) -> tuple[int | None, int | None]:
    """
    验证尺寸参数（用于图片、单元格等）

    参数:
        width: 宽度
        height: 高度
        min_width: 最小宽度
        max_width: 最大宽度（Excel限制为16384）
        min_height: 最小高度
        max_height: 最大高度（Excel限制为16384）

    返回:
        验证后的(width, height)元组

    异常:
        ValidationError: 如果尺寸超出范围
    """
    if width is not None:
        validate_number_range(
            width,
            min_value=min_width,
            max_value=max_width,
            value_name="宽度",
        )

    if height is not None:
        validate_number_range(
            height,
            min_value=min_height,
            max_value=max_height,
            value_name="高度",
        )

    return width, height
