"""
Excel处理工具模块

提供Excel文件的读写功能，支持：
- xlrd读取xls文件
- openpyxl读写xlsx文件
- pandas读取Excel文件
- 多Sheet页读写
- 自动格式转换

Example:
    >>> data = ExcelUtil.read_excel_xlrd("data.xls", ["name", "age"])
    >>> ExcelUtil.write_excel(data_list, "output.xlsx", sheet_title="Sheet1")
"""
import logging
from typing import List, Dict, Any, Optional, Union

import xlrd
import pandas
import openpyxl
from xlrd.sheet import ctype_text

logger = logging.getLogger(__name__)


class ExcelUtil:
    """
    Excel处理工具类

    提供静态方法进行Excel文件的读取和写入操作。
    支持xls（xlrd）和xlsx（openpyxl）两种格式。

    Example:
        >>> fields = ["id", "name", "value"]
        >>> data = ExcelUtil.read_excel_xlrd("input.xls", fields)
        >>> ExcelUtil.write_excel(data, "output.xlsx")
    """

    @staticmethod
    def format_number(number: Union[int, float]) -> Union[int, float]:
        """
        格式化数字，删除小数点后多余的0

        Args:
            number: 输入数字

        Returns:
            格式化后的数字（整数或浮点数）

        Example:
            >>> ExcelUtil.format_number(3.0)
            3
            >>> ExcelUtil.format_number(3.14)
            3.14
        """
        if isinstance(number, int):
            return number
        if isinstance(number, float):
            # 删除小数点后多余的0
            number_str = str(number).rstrip("0")

            # 只剩小数点直接转int，否则转回float
            if number_str.endswith("."):
                return int(number_str.rstrip("."))
            else:
                return float(number_str)
        return number

    @staticmethod
    def read_excel_xlrd(
        excel_file_path: str,
        field_list: List[str],
        sheet_idx_list: Optional[Union[int, List[int]]] = None,
        sheet_name: Optional[str] = None
    ) -> List[Dict[str, Any]]:
        """
        使用xlrd读取Excel文件（xls格式）

        对于xlsx文件会自动调用openpyxl方法读取。

        Args:
            excel_file_path: Excel文件路径
            field_list: 字段名列表，用于映射每行数据
            sheet_idx_list: Sheet页索引（从0开始），可以是单个索引或索引列表
            sheet_name: Sheet页名称，指定后只读取该页

        Returns:
            字典列表，每个字典表示一行数据

        Example:
            >>> fields = ["name", "age", "city"]
            >>> data = ExcelUtil.read_excel_xlrd("data.xls", fields, sheet_idx_list=0)
        """
        # xlsx文件使用openpyxl读取
        if str(excel_file_path).endswith(".xlsx"):
            return ExcelUtil.read_excel_openpyxl(
                excel_file_path, field_list,
                sheet_idx_list=sheet_idx_list, sheet_name=sheet_name
            )

        book = xlrd.open_workbook(excel_file_path)
        sheet_total = book.nsheets
        sheet_idx_max = sheet_total - 1

        # 处理sheet_idx_list参数
        if isinstance(sheet_idx_list, int) and sheet_idx_list >= 0:
            sheet_idx_list = [sheet_idx_list]
        if isinstance(sheet_idx_list, list) and len(sheet_idx_list) > 0:
            logger.info(f"sheet_idx_list: {sheet_idx_list}")
        else:
            sheet_idx_list = list(range(sheet_total))

        # 读取文件内容
        file_content: List[Dict[str, Any]] = []
        for idx_sheet in sheet_idx_list:
            if idx_sheet > sheet_idx_max:
                continue

            sheet = book.sheet_by_index(idx_sheet)
            sheet_name_current = str(sheet.name).lower().strip()
            row_total = sheet.nrows

            # 指定页名时，只匹配指定页的数据
            if sheet_name and str(sheet_name).lower().strip() != sheet_name_current:
                continue

            for idx_row in range(row_total):
                row = sheet.row(idx_row)
                col_total = len(row)

                line_content: List[Any] = []
                for idx_col in range(col_total):
                    col = row[idx_col]
                    col_ctype = col.ctype
                    col_ctype_name = ctype_text[col_ctype]
                    col_value = col.value

                    value = col_value
                    if col_ctype_name == "xldate":
                        value = xlrd.xldate.xldate_as_datetime(value, 0)
                    elif col_ctype_name == "number":
                        value = ExcelUtil.format_number(value)
                    line_content.append(value)

                line_dict = dict(zip(field_list, line_content))
                file_content.append(line_dict)

        return file_content

    @staticmethod
    def read_excel_openpyxl(
        excel_file_path: str,
        field_list: List[str],
        sheet_idx_list: Optional[Union[int, List[int]]] = None,
        sheet_name: Optional[str] = None
    ) -> List[Dict[str, Any]]:
        """
        使用openpyxl读取Excel文件（xlsx格式）

        Args:
            excel_file_path: Excel文件路径
            field_list: 字段名列表
            sheet_idx_list: Sheet页索引（从0开始）
            sheet_name: Sheet页名称

        Returns:
            字典列表，每个字典表示一行数据
        """
        wb = openpyxl.load_workbook(excel_file_path, read_only=True, data_only=True)
        sheets = wb.sheetnames

        # 确定要读取的Sheet页索引
        if sheet_name:
            idx_list = [sheets.index(sheet_name)] if sheet_name in sheets else []
        else:
            if isinstance(sheet_idx_list, int) and sheet_idx_list >= 0:
                idx_list = [sheet_idx_list]
            elif isinstance(sheet_idx_list, list) and len(sheet_idx_list) > 0:
                idx_list = sheet_idx_list
            else:
                idx_list = list(range(len(sheets)))

        file_content: List[Dict[str, Any]] = []
        for idx in idx_list:
            if idx < 0 or idx >= len(sheets):
                continue
            ws = wb[sheets[idx]]
            for row in ws.iter_rows(values_only=True):
                values = list(row)
                # 补齐/截断 + None统一处理
                n = len(field_list)
                if len(values) < n:
                    values = values + [None] * (n - len(values))
                else:
                    values = values[:n]
                file_content.append(dict(zip(field_list, values)))

        wb.close()
        return file_content

    @staticmethod
    def read_excel_pds(
        excel_file_path: str,
        sheet_name: str,
        field_list: Optional[List[str]] = None
    ) -> Union[List[Dict[str, Any]], Any]:
        """
        使用pandas读取Excel文件

        首行作为标题行，返回内容不包含首行。

        Args:
            excel_file_path: Excel文件路径
            sheet_name: Sheet页名称
            field_list: 字段名列表，为None时返回原始values

        Returns:
            字典列表或原始numpy数组
        """
        df = pandas.read_excel(excel_file_path, sheet_name=sheet_name)
        values = df.values

        if field_list is None:
            return values

        file_content: List[Dict[str, Any]] = []
        for i in range(len(values)):
            line_dict = dict(zip(field_list, values[i]))
            file_content.append(line_dict)

        return file_content

    @staticmethod
    def read_excel(excel_file_path: str, sheet_name: str) -> List[List[Any]]:
        """
        读取Excel文件为二维列表

        Args:
            excel_file_path: Excel文件路径
            sheet_name: Sheet页名称

        Returns:
            二维列表，每个子列表表示一行

        Raises:
            ValueError: Sheet页不存在时抛出
        """
        workbook = openpyxl.load_workbook(excel_file_path, read_only=False)
        sheet_names = workbook.sheetnames

        if sheet_name not in sheet_names:
            workbook.close()
            raise ValueError(f"Sheet '{sheet_name}' 不存在！")

        sheet = workbook[sheet_name]
        max_row = sheet.max_row
        max_col = sheet.max_column

        file_content: List[List[Any]] = []
        for i in range(max_row):
            file_line: List[Any] = []
            for j in range(max_col):
                value = sheet.cell(row=i + 1, column=j + 1).value
                file_line.append(value)
            file_content.append(file_line)

        workbook.close()
        return file_content

    @staticmethod
    def write_excel(
        data_list: List[Any],
        excel_file_path: str,
        sheet_title: str = ""
    ) -> None:
        """
        写入数据到Excel文件

        数据列表中的元素可以是字典或对象（会自动转换为__dict__）。
        第一行为标题行（字段名）。

        Args:
            data_list: 数据列表
            excel_file_path: 输出文件路径
            sheet_title: Sheet页标题

        Example:
            >>> data = [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]
            >>> ExcelUtil.write_excel(data, "output.xlsx", sheet_title="Users")
        """
        if data_list is None or len(data_list) == 0:
            logger.warning("No data to write!")
            return
        else:
            logger.info(f"Data size: {len(data_list)}")

        workbook = openpyxl.Workbook()
        if not sheet_title:
            sheet = workbook.active
            sheet.title = "Sheet1"
        else:
            sheet = workbook.create_sheet(title=sheet_title)

        # 获取字段列表（标题行）
        field_list: List[str] = []
        first_line = data_list[0]
        if not isinstance(first_line, dict):
            first_line = first_line.__dict__
        for key in first_line:
            field_list.append(key)

        # 写入标题行
        for i in range(len(field_list)):
            sheet.cell(row=1, column=(i + 1), value=field_list[i])

        # 写入数据内容
        idx_row = 2
        for line in data_list:
            if not isinstance(line, dict):
                line = line.__dict__
            for i in range(len(field_list)):
                value = line[field_list[i]]
                try:
                    sheet.cell(row=idx_row, column=(i + 1), value=value)
                except Exception as err:
                    logger.error(f"Write error at column {i + 1}, field {field_list[i]}, value {value}")
                    raise err
            idx_row += 1

        logger.info(f"Saving to: {excel_file_path}")
        workbook.save(excel_file_path)

    @staticmethod
    def write_excel_multi_sheet(
        data_dict: Dict[str, List[Any]],
        excel_file_path: str
    ) -> None:
        """
        多Sheet页写入Excel文件

        Args:
            data_dict: Sheet页数据字典，格式为 {"sheet1": [data1], "sheet2": [data2]}
            excel_file_path: 输出文件路径

        Example:
            >>> data = {
            ...     "Users": [{"name": "Alice"}, {"name": "Bob"}],
            ...     "Orders": [{"id": 1}, {"id": 2}]
            ... }
            >>> ExcelUtil.write_excel_multi_sheet(data, "output.xlsx")
        """
        workbook = openpyxl.Workbook()

        idx = 0
        for sheet_name in data_dict:
            idx += 1
            data_list = data_dict[sheet_name]

            if data_list is None or len(data_list) == 0:
                logger.warning(f"No data to write for sheet: {sheet_name}")
                continue
            else:
                logger.info(f"Data size for {sheet_name}: {len(data_list)}")

            # 第一个Sheet使用默认活动页
            if idx == 1:
                sheet = workbook.active
                sheet.title = sheet_name
            else:
                sheet = workbook.create_sheet(title=sheet_name)

            # 获取字段列表（标题行）
            field_list: List[str] = []
            first_line = data_list[0]
            if not isinstance(first_line, dict):
                first_line = first_line.__dict__
            for key in first_line:
                field_list.append(key)

            # 写入标题行
            for i in range(len(field_list)):
                sheet.cell(row=1, column=(i + 1), value=field_list[i])

            # 写入数据内容
            idx_row = 2
            for line in data_list:
                if not isinstance(line, dict):
                    line = line.__dict__
                for i in range(len(field_list)):
                    value = ""
                    if field_list[i] in line:
                        value = line[field_list[i]]
                    sheet.cell(row=idx_row, column=(i + 1), value=value)
                idx_row += 1

        logger.info(f"Saving to: {excel_file_path}")
        workbook.save(excel_file_path)


if __name__ == "__main__":
    pass
