# -*- coding: utf-8 -*-
"""
@Project :
@File    : limited_types.py  # 统一命名为受限类型模块
@Author  : YL_top01
@Date    : 2025/10/5 19:37
@Desc    : 封装路径、时间、编码等抽象数据类型，提供统一验证和操作接口
"""

# Built-in modules
import os
import re
import codecs
from datetime import datetime
from enum import Enum
from pathlib import Path, PurePath, PurePosixPath, PureWindowsPath
from typing import Union

# Third-party modules
# (无第三方依赖)

# Local modules
from yltop.recording.checks.path_checker import PathCheckExecutor
from yltop.recording.checks.factory import CheckLevel


# ------------------- 公共枚举定义 -------------------
class PathStyle(Enum):
    AUTO = "auto"          # 自动适应操作系统 (默认)
    POSIX = "posix"        # 强制使用 POSIX 风格
    WINDOWS = "windows"    # 强制使用 Windows 风格


class TimeFormat(Enum):
    """定义时间字符串的几种常见格式样式"""
    DOT_STYLE = '.'        # e.g., 2025.10.05
    SLASH = '/'            # e.g., 2025/10/05
    HYPHEN = '-'           # e.g., 2025-10-05
    EMPTY_FORMATTING = ' ' # e.g., 2025 10 05

    def get_format_string(self):
        """返回与样式对应的 strftime 格式化字符串"""
        sep = self.value
        return f"%Y{sep}%m{sep}%d"


class EncodingStyle(Enum):
    """常用编码类型枚举"""
    UTF8 = "utf-8"
    UTF8_SIG = "utf-8-sig"  # 带BOM的UTF-8（Windows友好）
    GBK = "gbk"
    GB2312 = "gb2312"
    ISO8859 = "iso-8859-1"

    @classmethod
    def get_supported(cls) -> list[str]:
        """返回所有支持的编码字符串"""
        return [item.value for item in cls]


# ------------------- 时间类型封装 -------------------
class LimitedTime:
    """封装日期时间，提供验证和比较能力"""

    def __init__(self, time_str: str):
        """初始化并验证日期字符串"""
        # 优化正则：支持月份/日期的前导零可选（1-12月，1-31日）
        match = re.match(
            r"^(\d{4})([-/. ])(0?[1-9]|1[0-2])\2(0?[1-9]|[12]\d|3[01])$",
            time_str
        )

        if not match:
            raise ValueError(
                f"无效的日期格式: '{time_str}'\n"
                "支持格式（分隔符需统一）：\n"
                "- YYYY-MM-DD (例: 2025-10-05 或 2025-1-5)\n"
                "- YYYY/MM/DD (例: 2025/10/05 或 2025/1/5)\n"
                "- YYYY.MM.DD (例: 2025.10.05 或 2025.1.5)\n"
                "- YYYY MM DD (例: 2025 10 05 或 2025 1 5)"
            )

        year, sep, month, day = match.groups()
        format_str = f"%Y{sep}%m{sep}%d"

        try:
            self._date_obj = datetime.strptime(time_str, format_str)
        except ValueError as e:
            raise ValueError(
                f"无效的日期: '{time_str}'\n原因: {e.args[0].capitalize()}"
            ) from e

        self._original_str = time_str

    def __repr__(self):
        return f"LimitedTime(date_obj={self._date_obj}, original_str='{self._original_str}')"

    def __str__(self):
        return self._original_str

    def format(self, time_format: TimeFormat) -> str:
        """按指定格式输出日期字符串"""
        return self._date_obj.strftime(time_format.get_format_string())

    # 时间比较魔法方法（核心新增功能）
    def __lt__(self, other: 'LimitedTime') -> bool:
        """判断当前日期是否早于另一个日期"""
        return self._date_obj < other._date_obj

    def __gt__(self, other: 'LimitedTime') -> bool:
        """判断当前日期是否晚于另一个日期"""
        return self._date_obj > other._date_obj

    def __eq__(self, other: 'LimitedTime') -> bool:
        """判断两个日期是否相等"""
        return self._date_obj == other._date_obj

    def __le__(self, other: 'LimitedTime') -> bool:
        return self < other or self == other

    def __ge__(self, other: 'LimitedTime') -> bool:
        return self > other or self == other

    # 日期属性
    @property
    def year(self) -> int:
        return self._date_obj.year

    @property
    def month(self) -> int:
        return self._date_obj.month

    @property
    def day(self) -> int:
        return self._date_obj.day

    @property
    def type(self):
        return "Time"


# ------------------- 路径类型封装 -------------------
class LimitedPath:
    """封装文件路径，复用recording的路径检查逻辑"""

    def __init__(
        self,
        path_str: str,
        style: PathStyle = PathStyle.AUTO,
        check_exists: bool = False  # 是否检查路径存在性
    ):
        """初始化路径并验证（复用recording的检查器）"""
        # 1. 初始化路径对象
        if style == PathStyle.POSIX:
            self._path_obj: Union[PurePath, Path] = PurePosixPath(path_str)
        elif style == PathStyle.WINDOWS:
            self._path_obj = PureWindowsPath(path_str)
        else:  # AUTO：使用实际文件系统路径
            self._path_obj = Path(path_str)

        # 2. 复用recording的路径检查逻辑（解决重复问题）
        self._checker = PathCheckExecutor(level=CheckLevel.ASSERT)
        self._validate_path(check_exists)

    def _validate_path(self, check_exists: bool):
        """使用recording的检查器验证路径"""
        path_str = str(self._path_obj)
        # 检查格式（必选）
        if not self._checker.check_format(path_str):
            raise ValueError(f"路径格式非法: {path_str}")
        # 检查存在性（可选）
        if check_exists and not self._checker.check_exists(path_str):
            raise FileNotFoundError(f"路径不存在: {path_str}")

    def __repr__(self):
        return f"LimitedPath(path_obj={self._path_obj!r})"

    def __str__(self):
        """修正：PurePath无resolve()，使用absolute()替代"""
        if isinstance(self._path_obj, PurePath) and not isinstance(self._path_obj, Path):
            return str(self._path_obj.absolute())
        return str(self._path_obj.resolve())

    @property
    def layer_count(self) -> int:
        """路径层级数（如 /a/b/c 为3层）"""
        return len(self._path_obj.parts)

    @property
    def root(self) -> str:
        """路径根目录（如 '/' 或 'C:\\'）"""
        return self._path_obj.root

    @property
    def name(self) -> str:
        """路径最后一部分（文件名/目录名）"""
        return self._path_obj.name

    @property
    def parent(self) -> 'LimitedPath':
        """父目录（返回新的LimitedPath对象）"""
        return LimitedPath(
            str(self._path_obj.parent),
            style=PathStyle.AUTO if isinstance(self._path_obj, Path)
            else PathStyle.POSIX if isinstance(self._path_obj, PurePosixPath)
            else PathStyle.WINDOWS
        )

    @property
    def type(self):
        return "Path"

    def exists(self) -> bool:
        """检查路径是否存在（仅对实际文件系统路径有效）"""
        return self._path_obj.exists() if isinstance(self._path_obj, Path) else False

    def is_file(self) -> bool:
        return self._path_obj.is_file() if isinstance(self._path_obj, Path) else False

    def is_dir(self) -> bool:
        return self._path_obj.is_dir() if isinstance(self._path_obj, Path) else False


# ------------------- 编码类型封装 -------------------
class LimitedEncoding:
    """封装编码类型，确保使用有效编码"""

    def __init__(self, encoding: str):
        """初始化并验证编码有效性"""
        self._encoding = self._validate_encoding(encoding)

    def _validate_encoding(self, encoding: str) -> str:
        """验证编码有效性（复用recording的编码检查场景）"""
        # 1. 尝试规范化编码名称（如 utf8 -> utf-8）
        try:
            normalized = codecs.lookup(encoding).name
        except LookupError:
            raise ValueError(
                f"无效的编码格式: '{encoding}'\n"
                f"支持的编码: {EncodingStyle.get_supported()}"
            )

        # 2. 限制为预定义的安全编码（避免使用危险编码如 'utf-7'）
        if normalized not in EncodingStyle.get_supported():
            raise ValueError(
                f"不推荐使用的编码: '{normalized}'\n"
                f"建议使用: {EncodingStyle.get_supported()}"
            )
        return normalized

    def __repr__(self):
        return f"LimitedEncoding(encoding='{self._encoding}')"

    def __str__(self):
        return self._encoding

    @property
    def encoding(self) -> str:
        """返回标准化的编码名称"""
        return self._encoding

    @property
    def type(self):
        return "Encoding"

    # 编码兼容性检查（用于日志等场景）
    def is_compatible_with(self, other: 'LimitedEncoding') -> bool:
        """判断当前编码是否与另一个编码兼容（如 utf-8 与 utf-8-sig）"""
        compat_map = {
            "utf-8": ["utf-8-sig"],
            "utf-8-sig": ["utf-8"],
            "gbk": ["gb2312"],
            "gb2312": ["gbk"]
        }
        return other.encoding in compat_map.get(self._encoding, [])

if __name__ == '__main__':
    from yltop.recording.checks.decorator_checker import validate_parameters

    @validate_parameters(enable_type_check=True)
    def process_data(
            log_path: LimitedPath,
            record_date: LimitedTime,
            file_encoding: LimitedEncoding
    ):
        print(f"处理路径: {log_path}")
        print(f"处理日期: {record_date}")
        print(f"使用编码: {file_encoding}")


    # 使用示例
    try:
        process_data(
            log_path=LimitedPath("/var/log/app.log"),
            record_date=LimitedTime("2025-10-05"),
            file_encoding=LimitedEncoding("utf-8-sig")
        )
    except Exception as e:
        print(f"参数错误: {e}")