"""
Workbook类 - Symphra Excel库的核心组件
"""

from __future__ import annotations

import io
from pathlib import Path
from typing import Any, Type

from openpyxl import Workbook as OpenpyxlWorkbook
from openpyxl import load_workbook
from openpyxl.workbook.workbook import Workbook as OpenpyxlBaseWorkbook
from openpyxl.worksheet.worksheet import Worksheet as OpenpyxlWorksheet

from ..core.worksheet import Worksheet
from ..utils.exceptions import (
    FileFormatError,
    ValidationError,
    handle_file_errors,
    handle_validation_errors,
)
from ..utils.helpers import is_excel_file, validate_file_path
from ..utils.memory_manager import MemoryManager

WorkbookMode = str | None


class Workbook:
    """
    高性能工作簿类，支持多种操作模式
    """

    _SUPPORTED_MODES = {"default", "read_only", "write_only"}

    def __init__(
        self,
        filename: str | Path | None = None,
        *,
        mode: WorkbookMode = "default",
        read_only: bool | None = None,
        write_only: bool | None = None,
        memory_manager: MemoryManager | None = None,
    ) -> None:
        """
        初始化工作簿

        参数:
            filename: 可选的Excel文件路径用于加载
            mode: 工作流模式，可选值为 "default" | "read_only" | "write_only"
            read_only: 旧版标志，映射到 mode="read_only"
            write_only: 旧版标志，映射到 mode="write_only"
            memory_manager: 内存管理器，用于配合高性能模式
        """
        if read_only:
            mode = "read_only"
        if write_only:
            mode = "write_only"

        if mode not in self._SUPPORTED_MODES:
            raise ValidationError(f"不支持的工作簿模式: {mode}")

        self._mode = mode or "default"
        self._filename: Path | None = None
        self._memory_manager = memory_manager or MemoryManager()
        self._memory_manager.register_object(self)

        self._workbook: OpenpyxlBaseWorkbook
        if filename:
            self._workbook = self._load_workbook(filename, self._mode)
            self._filename = validate_file_path(filename)
        else:
            self._workbook = self._create_new_workbook(self._mode)

        self._worksheets: dict[str, Worksheet] = {}
        if self._mode != "read_only":
            for ws in self._workbook.worksheets:
                self._register_wrapper(ws)

    # --------------------------------------------------------------------- #
    # 属性
    # --------------------------------------------------------------------- #
    @property
    def mode(self) -> str:
        """获取工作簿操作模式"""
        return self._mode

    @property
    def memory_manager(self) -> MemoryManager:
        """获取内存管理器实例"""
        return self._memory_manager

    @property
    def active(self) -> Worksheet:
        """获取活动工作表，考虑只写模式的限制"""
        if self._mode == "write_only":
            if not self._worksheets:
                return self.create_worksheet("Sheet1")
            return next(iter(self._worksheets.values()))

        active_ws = self._workbook.active
        if active_ws is None:
            # 如果没有活动工作表，创建一个新工作表
            return self.create_worksheet("Sheet1")
        return self._register_wrapper(active_ws)

    @property
    def worksheets(self) -> list[Worksheet]:
        """返回所有工作表的包装器"""
        wrappers: list[Worksheet] = []
        for name in self.sheetnames:
            worksheet = self.get_worksheet(name)
            if worksheet is not None:
                wrappers.append(worksheet)
        return wrappers

    @property
    def sheetnames(self) -> list[str]:
        """返回工作表名称列表"""
        try:
            return list(self._workbook.sheetnames)
        except AttributeError:
            return [ws.title for ws in getattr(self._workbook, "worksheets", [])]

    def get_sheet_names(self) -> list[str]:
        """获取工作表名称的向后兼容别名"""
        return self.sheetnames

    # --------------------------------------------------------------------- #
    # 工作表辅助方法
    # --------------------------------------------------------------------- #
    def _register_wrapper(self, openpyxl_ws: OpenpyxlWorksheet) -> Worksheet:
        """包装openpyxl工作表并缓存包装器"""
        if openpyxl_ws.title in self._worksheets:
            return self._worksheets[openpyxl_ws.title]

        wrapper = Worksheet(
            openpyxl_ws,
            mode=self._mode,
            memory_manager=self._memory_manager,
        )
        self._worksheets[openpyxl_ws.title] = wrapper
        return wrapper

    def create_worksheet(
        self, title: str | None = None, index: int | None = None
    ) -> Worksheet:
        """创建新工作表的别名方法"""
        return self.create_sheet(title=title, index=index)

    def create_sheet(
        self, title: str | None = None, index: int | None = None
    ) -> Worksheet:
        """
        创建新工作表（只读模式下禁用）
        """
        if self._mode == "read_only":
            raise ValidationError("只读模式下无法创建工作表")

        ws = self._workbook.create_sheet(title=title, index=index)
        return self._register_wrapper(ws)

    def remove_sheet(self, worksheet: str | Worksheet) -> None:
        """
        通过包装器或名称删除工作表
        """
        if self._mode == "write_only":
            raise ValidationError("只写模式下无法删除工作表")

        if isinstance(worksheet, str):
            sheet_name = worksheet
            if sheet_name not in self.sheetnames:
                raise ValidationError(f"未找到工作表 '{sheet_name}'")
            ws_to_remove = self._workbook[sheet_name]
        else:
            ws_to_remove = worksheet._worksheet
            sheet_name = ws_to_remove.title

        self._workbook.remove(ws_to_remove)
        self._worksheets.pop(sheet_name, None)

    def get_sheet_by_name(self, name: str) -> Worksheet | None:
        """
        通过名称获取工作表
        """
        if name in self._worksheets:
            return self._worksheets[name]

        if name not in self.sheetnames:
            return None

        ws = self._workbook[name]
        return self._register_wrapper(ws)

    def get_worksheet(self, name: str) -> Worksheet | None:
        """获取工作表的别名方法"""
        return self.get_sheet_by_name(name)

    def delete_worksheet(self, name: str) -> bool:
        """通过名称删除工作表"""
        if name not in self.sheetnames:
            return False
        self.remove_sheet(name)
        return True

    def copy_sheet(
        self, source: str | Worksheet, title: str | None = None
    ) -> Worksheet:
        """
        复制工作表（仅支持默认模式）
        """
        if self._mode != "default":
            raise ValidationError("工作表复制仅在默认模式下支持")

        if isinstance(source, str):
            source_ws = self.get_sheet_by_name(source)
            if source_ws is None:
                raise ValidationError(f"未找到源工作表 '{source}'")
        else:
            source_ws = source

        copied_ws = self._workbook.copy_worksheet(source_ws._worksheet)
        if title:
            copied_ws.title = title
        return self._register_wrapper(copied_ws)

    def move_sheet(self, worksheet: str | Worksheet, offset: int) -> None:
        """
        移动工作表位置（仅默认模式）
        """
        if self._mode != "default":
            raise ValidationError("只能在默认模式下重新排序工作表")

        if isinstance(worksheet, str):
            ws = self.get_sheet_by_name(worksheet)
            if ws is None:
                raise ValidationError(f"未找到工作表 '{worksheet}'")
        else:
            ws = worksheet

        self._workbook.move_sheet(ws._worksheet, offset)

    # --------------------------------------------------------------------- #
    # 持久化辅助方法
    # --------------------------------------------------------------------- #
    @handle_file_errors(error_message="保存工作簿失败")
    def save(self, filename: str | Path | None = None) -> None:
        """
        保存工作簿到文件

        参数:
            filename: 目标文件路径，如果为None则使用原始文件名

        异常:
            ValidationError: 只读模式或未指定文件名时抛出
            FileFormatError: 文件保存失败时抛出
        """
        if self._mode == "read_only":
            raise ValidationError("无法保存只读工作簿；请创建新实例")

        save_path: Path
        if filename is None:
            if self._filename is None:
                raise ValidationError("未指定文件名且无原始文件名可用")
            save_path = self._filename
        else:
            save_path = validate_file_path(filename)

        save_path.parent.mkdir(parents=True, exist_ok=True)
        self._workbook.save(save_path)
        self._filename = save_path

    def save_to_memory(self) -> bytes:
        """
        保存工作簿到字节缓冲区
        """
        if self._mode == "read_only":
            raise ValidationError("只读工作簿不支持内存保存")

        buffer = io.BytesIO()
        self._workbook.save(buffer)
        data = buffer.getvalue()
        self._memory_manager.allocate_memory(len(data))
        self._memory_manager.deallocate_memory(len(data))
        return data

    def save_to_bytes(self) -> bytes:
        """保存到内存的别名方法"""
        return self.save_to_memory()

    # --------------------------------------------------------------------- #
    # 类构造方法
    # --------------------------------------------------------------------- #
    @classmethod
    def load(
        cls,
        filename: str | Path,
        *,
        mode: WorkbookMode = "default",
        memory_manager: MemoryManager | None = None,
    ) -> "Workbook":
        """从文件加载工作簿"""
        return cls(filename, mode=mode, memory_manager=memory_manager)

    @classmethod
    def from_bytes(
        cls,
        data: bytes,
        *,
        mode: WorkbookMode = "default",
        memory_manager: MemoryManager | None = None,
    ) -> "Workbook":
        """从字节数据加载工作簿"""
        if mode == "write_only":
            raise ValidationError("无法从字节数据构建只写工作簿")

        buffer = io.BytesIO(data)
        workbook = cls(mode=mode, memory_manager=memory_manager)
        workbook._workbook = load_workbook(
            buffer,
            read_only=mode == "read_only",
            data_only=False,
        )
        workbook._worksheets.clear()
        if mode != "read_only":
            for ws in workbook._workbook.worksheets:
                workbook._register_wrapper(ws)
        return workbook

    # --------------------------------------------------------------------- #
    # 元数据辅助方法
    # --------------------------------------------------------------------- #
    def get_properties(self) -> dict[str, str | None]:
        """
        获取工作簿文档属性
        """
        props = self._workbook.properties
        return {
            "title": props.title,
            "creator": props.creator,
            "description": props.description,
            "subject": props.subject,
            "created": props.created,
            "modified": props.modified,
            "last_modified_by": props.lastModifiedBy,
            "category": props.category,
            "keywords": props.keywords,
        }

    @handle_validation_errors(error_message="设置工作簿属性失败")
    def set_properties(self, **kwargs: Any) -> None:
        """
        更新工作簿文档属性

        参数:
            **kwargs: 属性键值对 (如 title, creator, description 等)

        异常:
            ValidationError: 只读模式或属性无效时抛出
        """
        if self._mode == "read_only":
            raise ValidationError("只读模式下无法修改属性")

        props = self._workbook.properties
        for key, value in kwargs.items():
            if hasattr(props, key):
                setattr(props, key, value)

    # --------------------------------------------------------------------- #
    # 生命周期管理
    # --------------------------------------------------------------------- #
    def close(self) -> None:
        """
        清理缓存并优化内存使用
        """
        for ws in self._worksheets.values():
            ws.clear_cached_styles()
        self._worksheets.clear()
        self._memory_manager.optimize_memory()

    def __enter__(self) -> "Workbook":
        return self

    def __exit__(
        self,
        exc_type: Type[BaseException | None],
        exc_val: BaseException | None,
        exc_tb: Any | None,
    ) -> None:
        self.close()

    def __len__(self) -> int:
        return len(self.sheetnames)

    def __contains__(self, name: str) -> bool:
        return name in self.sheetnames

    # --------------------------------------------------------------------- #
    # 内部辅助方法
    # --------------------------------------------------------------------- #
    @staticmethod
    def _create_new_workbook(mode: str) -> OpenpyxlBaseWorkbook:
        if mode == "write_only":
            return OpenpyxlWorkbook(write_only=True)
        return OpenpyxlWorkbook()

    @staticmethod
    @handle_file_errors(error_message="加载工作簿失败")
    def _load_workbook(filename: str | Path, mode: str) -> OpenpyxlBaseWorkbook:
        """
        从文件加载工作簿

        参数:
            filename: Excel文件路径
            mode: 工作簿模式 (default/read_only/write_only)

        返回:
            openpyxl工作簿对象

        异常:
            FileFormatError: 文件不存在、格式无效或加载失败时抛出
        """
        file_path = validate_file_path(filename)
        if not file_path.exists():
            raise FileFormatError(f"文件未找到: {file_path}")
        if not is_excel_file(file_path):
            raise FileFormatError(f"无效的Excel文件格式: {file_path}")

        return load_workbook(
            file_path,
            read_only=mode == "read_only",
            data_only=False,
        )
