"""
图表布局配置模块

控制图表的结构性属性：
- 图例位置、大小
- 横轴配置（日期轴、刻度）
- 纵轴配置（位置、刻度）
"""

from pptx.enum.chart import XL_LEGEND_POSITION, XL_TICK_MARK, XL_TICK_LABEL_POSITION
from pptx.util import Pt
from typing import Optional, Dict, Any

# ============================================================================
# 图例配置
# ============================================================================

class LegendConfig:
    """图例配置"""
    
    # 预设位置
    BOTTOM = XL_LEGEND_POSITION.BOTTOM      # 底部
    TOP = XL_LEGEND_POSITION.TOP            # 顶部
    LEFT = XL_LEGEND_POSITION.LEFT          # 左侧
    RIGHT = XL_LEGEND_POSITION.RIGHT        # 右侧
    CORNER = XL_LEGEND_POSITION.CORNER      # 右上角
    
    def __init__(
        self,
        position = XL_LEGEND_POSITION.BOTTOM,
        font_size_pt: float = 10,
        font_name: str = "黑体",
        include_in_layout: bool = False,
    ):
        """
        初始化图例配置
        
        Args:
            position: 图例位置
                - XL_LEGEND_POSITION.BOTTOM (默认)
                - XL_LEGEND_POSITION.TOP
                - XL_LEGEND_POSITION.LEFT
                - XL_LEGEND_POSITION.RIGHT
                - XL_LEGEND_POSITION.CORNER
            font_size_pt: 字体大小（pt）
            font_name: 字体名称（默认：黑体）
            include_in_layout: 是否包含在布局中（False 防止图例覆盖图表）
        """
        self.position = position
        self.font_size_pt = font_size_pt
        self.font_name = font_name
        self.include_in_layout = include_in_layout
    
    def apply_to_chart(self, chart):
        """应用到图表"""
        chart.has_legend = True
        chart.legend.position = self.position
        chart.legend.include_in_layout = self.include_in_layout
        
        if self.font_size_pt:
            chart.legend.font.size = Pt(self.font_size_pt)
        if self.font_name:
            chart.legend.font.name = self.font_name
        
        print(f"  - 图例配置: 位置={self._position_name()}, 字体={self.font_name} {self.font_size_pt}pt")
    
    def _position_name(self):
        """获取位置名称"""
        position_names = {
            XL_LEGEND_POSITION.BOTTOM: "底部",
            XL_LEGEND_POSITION.TOP: "顶部",
            XL_LEGEND_POSITION.LEFT: "左侧",
            XL_LEGEND_POSITION.RIGHT: "右侧",
            XL_LEGEND_POSITION.CORNER: "右上角",
        }
        return position_names.get(self.position, "未知")


# ============================================================================
# 横轴配置
# ============================================================================

class CategoryAxisConfig:
    """横轴（分类轴）配置"""
    
    def __init__(
        self,
        is_date_axis: bool = False,
        major_unit: Optional[float] = None,
        major_unit_days: Optional[int] = None,
        tick_label_position = XL_TICK_LABEL_POSITION.LOW,
        major_tick_mark = XL_TICK_MARK.NONE,
        minor_tick_mark = XL_TICK_MARK.NONE,
        number_format: Optional[str] = None,
        font_size_pt: float = 10,
        font_name: str = "黑体",
    ):
        """
        初始化横轴配置
        
        Args:
            is_date_axis: 是否为日期轴
            major_unit: 主刻度单位（用于日期轴，单位为天）
            major_unit_days: 主刻度单位（天）- 便捷参数，与 major_unit 相同
            tick_label_position: 刻度标签位置
            major_tick_mark: 主刻度线样式
            minor_tick_mark: 次刻度线样式
            number_format: 数字格式（如 'yyyy-mm-dd'）
            font_size_pt: 字体大小
            font_name: 字体名称（默认：黑体）
        """
        self.is_date_axis = is_date_axis
        self.major_unit = major_unit or major_unit_days
        self.tick_label_position = tick_label_position
        self.major_tick_mark = major_tick_mark
        self.minor_tick_mark = minor_tick_mark
        self.number_format = number_format
        self.font_size_pt = font_size_pt
        self.font_name = font_name
    
    def apply_to_chart(self, chart):
        """应用到图表"""
        try:
            category_axis = chart.category_axis
            
            # 设置日期轴
            if self.is_date_axis:
                from pptx.enum.chart import XL_CATEGORY_TYPE
                category_axis.category_type = XL_CATEGORY_TYPE.TIME_SCALE
                print(f"  - 横轴: 日期轴")
                
                # 设置刻度单位
                if self.major_unit:
                    from pptx.enum.chart import XL_TIME_UNIT
                    category_axis.major_unit = self.major_unit
                    category_axis.major_unit_scale = XL_TIME_UNIT.DAYS
                    print(f"  - 刻度单位: {self.major_unit} 天")
            else:
                print(f"  - 横轴: 分类轴")
            
            # 设置刻度标签位置
            category_axis.tick_label_position = self.tick_label_position
            
            # 设置刻度线
            category_axis.major_tick_mark = self.major_tick_mark
            category_axis.minor_tick_mark = self.minor_tick_mark
            
            # 设置数字格式
            if self.number_format:
                category_axis.tick_labels.number_format = self.number_format
                print(f"  - 日期格式: {self.number_format}")
            
            # 设置字体大小和字体名称
            if self.font_size_pt:
                category_axis.tick_labels.font.size = Pt(self.font_size_pt)
            if self.font_name:
                category_axis.tick_labels.font.name = self.font_name
            print(f"  - 横轴字体: {self.font_name} {self.font_size_pt}pt")
                
        except Exception as e:
            print(f"  ⚠️ 横轴配置失败: {e}")


# ============================================================================
# 纵轴配置
# ============================================================================

class ValueAxisConfig:
    """纵轴（数值轴）配置"""
    
    def __init__(
        self,
        tick_label_position = XL_TICK_LABEL_POSITION.LOW,
        major_tick_mark = XL_TICK_MARK.OUTSIDE,
        minor_tick_mark = XL_TICK_MARK.NONE,
        number_format: Optional[str] = None,
        font_size_pt: float = 10,
        font_name: str = "黑体",
        has_major_gridlines: bool = True,
        min_value: Optional[float] = None,
        max_value: Optional[float] = None,
        major_unit: Optional[float] = None,
    ):
        """
        初始化纵轴配置
        
        Args:
            tick_label_position: 刻度标签位置
            major_tick_mark: 主刻度线样式
            minor_tick_mark: 次刻度线样式
            number_format: 数字格式（如 '0.00%'）
            font_size_pt: 字体大小
            font_name: 字体名称（默认：黑体）
            has_major_gridlines: 是否显示主网格线
            min_value: 最小值（可选）
            max_value: 最大值（可选）
            major_unit: 主刻度单位（可选）
        """
        self.tick_label_position = tick_label_position
        self.major_tick_mark = major_tick_mark
        self.minor_tick_mark = minor_tick_mark
        self.number_format = number_format
        self.font_size_pt = font_size_pt
        self.font_name = font_name
        self.has_major_gridlines = has_major_gridlines
        self.min_value = min_value
        self.max_value = max_value
        self.major_unit = major_unit
    
    def apply_to_chart(self, chart):
        """应用到图表（主值轴）"""
        try:
            # ⭐ 通过 XML 直接设置主值轴，确保格式生效
            chart_element = chart._element
            val_ax_elements = chart_element.findall('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}valAx')
            
            # 主值轴是 position='l' 的那个
            primary_ax = None
            for ax in val_ax_elements:
                ax_pos = ax.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}axPos')
                if ax_pos is not None and ax_pos.get('val') == 'l':
                    primary_ax = ax
                    break
            
            if primary_ax is None:
                print(f"  ⚠️ 未找到主值轴")
                return
            
            from lxml import etree
            
            # 设置数字格式（通过 XML）
            if self.number_format:
                num_fmt = primary_ax.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}numFmt')
                if num_fmt is None:
                    # 创建 numFmt 元素
                    num_fmt = etree.Element('{http://schemas.openxmlformats.org/drawingml/2006/chart}numFmt')
                    # 在 tickLblPos 之后插入
                    tick_lbl_pos = primary_ax.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}tickLblPos')
                    if tick_lbl_pos is not None:
                        parent_list = list(primary_ax)
                        pos_index = parent_list.index(tick_lbl_pos)
                        primary_ax.insert(pos_index + 1, num_fmt)
                    else:
                        primary_ax.append(num_fmt)
                
                num_fmt.set('formatCode', self.number_format)
                num_fmt.set('sourceLinked', '0')
                print(f"  - 主值轴格式: {self.number_format}")
            
            # 设置字体（通过 XML）
            if self.font_size_pt or self.font_name:
                self._apply_font_to_axis_xml(primary_ax, self.font_name, self.font_size_pt)
                print(f"  - 主值轴字体: {self.font_name} {self.font_size_pt}pt")
            
            # 设置轴范围和刻度间隔（通过 XML）
            if self.min_value is not None or self.max_value is not None:
                scaling = primary_ax.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}scaling')
                if scaling is None:
                    scaling = etree.Element('{http://schemas.openxmlformats.org/drawingml/2006/chart}scaling')
                    # 在 axId 之后插入
                    ax_id = primary_ax.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}axId')
                    if ax_id is not None:
                        parent_list = list(primary_ax)
                        ax_id_index = parent_list.index(ax_id)
                        primary_ax.insert(ax_id_index + 1, scaling)
                    else:
                        primary_ax.insert(0, scaling)
                    
                    # 添加 orientation
                    orientation = etree.SubElement(scaling, '{http://schemas.openxmlformats.org/drawingml/2006/chart}orientation')
                    orientation.set('val', 'minMax')
                
                if self.min_value is not None:
                    min_elem = scaling.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}min')
                    if min_elem is None:
                        min_elem = etree.SubElement(scaling, '{http://schemas.openxmlformats.org/drawingml/2006/chart}min')
                    min_elem.set('val', str(self.min_value))
                    print(f"  - 主值轴最小值: {self.min_value}")
                
                if self.max_value is not None:
                    max_elem = scaling.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}max')
                    if max_elem is None:
                        max_elem = etree.SubElement(scaling, '{http://schemas.openxmlformats.org/drawingml/2006/chart}max')
                    max_elem.set('val', str(self.max_value))
                    print(f"  - 主值轴最大值: {self.max_value}")
            
            if self.major_unit is not None:
                major_unit_elem = primary_ax.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}majorUnit')
                if major_unit_elem is None:
                    major_unit_elem = etree.SubElement(primary_ax, '{http://schemas.openxmlformats.org/drawingml/2006/chart}majorUnit')
                major_unit_elem.set('val', str(self.major_unit))
                print(f"  - 主值轴刻度间隔: {self.major_unit}")
            
        except Exception as e:
            print(f"  ⚠️ 主值轴配置失败: {e}")
            import traceback
            traceback.print_exc()
    
    def _apply_font_to_axis_xml(self, axis_element, font_name: str, font_size_pt: float):
        """通过 XML 设置轴字体"""
        from lxml import etree
        
        # 查找或创建 txPr
        txPr = axis_element.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}txPr')
        if txPr is None:
            txPr = etree.SubElement(axis_element, '{http://schemas.openxmlformats.org/drawingml/2006/chart}txPr')
            bodyPr = etree.SubElement(txPr, '{http://schemas.openxmlformats.org/drawingml/2006/main}bodyPr')
            lstStyle = etree.SubElement(txPr, '{http://schemas.openxmlformats.org/drawingml/2006/main}lstStyle')
        
        # 设置字体
        p = txPr.find('.//{http://schemas.openxmlformats.org/drawingml/2006/main}p')
        if p is None:
            p = etree.SubElement(txPr, '{http://schemas.openxmlformats.org/drawingml/2006/main}p')
        
        pPr = p.find('.//{http://schemas.openxmlformats.org/drawingml/2006/main}pPr')
        if pPr is None:
            pPr = etree.SubElement(p, '{http://schemas.openxmlformats.org/drawingml/2006/main}pPr')
        
        defRPr = pPr.find('.//{http://schemas.openxmlformats.org/drawingml/2006/main}defRPr')
        if defRPr is None:
            defRPr = etree.SubElement(pPr, '{http://schemas.openxmlformats.org/drawingml/2006/main}defRPr')
        
        if font_size_pt:
            defRPr.set('sz', str(int(font_size_pt * 100)))
        
        if font_name:
            latin = defRPr.find('.//{http://schemas.openxmlformats.org/drawingml/2006/main}latin')
            if latin is None:
                latin = etree.SubElement(defRPr, '{http://schemas.openxmlformats.org/drawingml/2006/main}latin')
            latin.set('typeface', font_name)



# ============================================================================
# 默认配置
# ============================================================================

# 默认图例配置：底部、9pt 黑体
DEFAULT_LEGEND_CONFIG = LegendConfig(
    position=XL_LEGEND_POSITION.BOTTOM,
    font_size_pt=9,
    font_name="黑体",
    include_in_layout=False,
)

# 默认横轴配置：普通分类轴、外侧刻度线、9pt 黑体
DEFAULT_CATEGORY_AXIS_CONFIG = CategoryAxisConfig(
    is_date_axis=False,
    major_tick_mark=XL_TICK_MARK.OUTSIDE,  # ⭐ 改为 OUTSIDE（与原图一致）
    minor_tick_mark=XL_TICK_MARK.NONE,
    font_size_pt=9,
    font_name="黑体",
)

# 默认纵轴配置：无网格线、9pt 黑体
DEFAULT_VALUE_AXIS_CONFIG = ValueAxisConfig(
    tick_label_position=XL_TICK_LABEL_POSITION.LOW,
    major_tick_mark=XL_TICK_MARK.OUTSIDE,
    minor_tick_mark=XL_TICK_MARK.NONE,
    font_size_pt=9,
    font_name="黑体",
    has_major_gridlines=False,  # ⭐ 改为 False（原图无网格线）
)


# ============================================================================
# 完整布局配置
# ============================================================================

class ChartLayoutConfig:
    """完整的图表布局配置"""
    
    def __init__(
        self,
        title: Optional[str] = None,
        legend_config: Optional[LegendConfig] = None,
        category_axis_config: Optional[CategoryAxisConfig] = None,
        value_axis_config: Optional[ValueAxisConfig] = None,
        secondary_value_axis_config: Optional[ValueAxisConfig] = None,  # ⭐ 新增次值轴配置
        date_axis_config = None,  # DateAxisConfig（避免循环导入）
    ):
        """
        初始化布局配置
        
        Args:
            title: 图表标题
            legend_config: 图例配置
            category_axis_config: 横轴配置
            value_axis_config: 主值轴（左轴）配置
            secondary_value_axis_config: 次值轴（右轴）配置
            date_axis_config: 日期轴配置（DateAxisConfig，用于精确控制日期轴）
        """
        self.title = title
        self.legend_config = legend_config or DEFAULT_LEGEND_CONFIG
        self.category_axis_config = category_axis_config or DEFAULT_CATEGORY_AXIS_CONFIG
        self.value_axis_config = value_axis_config or DEFAULT_VALUE_AXIS_CONFIG
        self.secondary_value_axis_config = secondary_value_axis_config  # ⭐ 新增
        self.date_axis_config = date_axis_config  # ⭐ 新增
    
    def apply_to_chart(self, chart):
        """应用所有配置到图表"""
        print(f"\n⚙️ 应用布局配置:")
        
        # 应用标题
        if self.title:
            try:
                chart.has_title = True
                chart.chart_title.text_frame.text = self.title
                print(f"  - 标题: {self.title}")
            except Exception as e:
                print(f"  ⚠️ 标题设置失败: {e}")
        
        # 应用图例配置
        if self.legend_config:
            self.legend_config.apply_to_chart(chart)
        
        # ⭐ 应用日期轴配置（优先于普通横轴配置）
        if self.date_axis_config:
            self.date_axis_config.apply_to_chart(chart)
        # 应用横轴配置（如果没有日期轴配置）
        elif self.category_axis_config:
            self.category_axis_config.apply_to_chart(chart)
        
        # 应用主值轴配置（左轴）
        if self.value_axis_config:
            self.value_axis_config.apply_to_chart(chart)
        
        # ⭐ 应用次值轴配置（右轴）
        if self.secondary_value_axis_config:
            self._apply_secondary_axis_config(chart)
    
    def _apply_secondary_axis_config(self, chart):
        """应用次值轴配置（通过 XML）"""
        try:
            # 通过 XML 查找次值轴
            chart_element = chart._element
            val_ax_elements = chart_element.findall('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}valAx')
            
            # 次值轴通常是第二个 valAx（position='r'）
            secondary_ax = None
            for ax in val_ax_elements:
                ax_pos = ax.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}axPos')
                if ax_pos is not None and ax_pos.get('val') == 'r':
                    secondary_ax = ax
                    break
            
            if secondary_ax is None:
                print(f"  ⚠️ 未找到次值轴")
                return
            
            config = self.secondary_value_axis_config
            
            # 设置数字格式
            if config.number_format:
                num_fmt = secondary_ax.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}numFmt')
                if num_fmt is not None:
                    num_fmt.set('formatCode', config.number_format)
                    num_fmt.set('sourceLinked', '0')
                print(f"  - 次值轴格式: {config.number_format}")
            
            # 设置字体
            if config.font_size_pt or config.font_name:
                from pptx.util import Pt
                from lxml import etree
                
                # 查找或创建 txPr (文本属性)
                txPr = secondary_ax.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}txPr')
                if txPr is None:
                    txPr = etree.SubElement(secondary_ax, '{http://schemas.openxmlformats.org/drawingml/2006/chart}txPr')
                    bodyPr = etree.SubElement(txPr, '{http://schemas.openxmlformats.org/drawingml/2006/main}bodyPr')
                    lstStyle = etree.SubElement(txPr, '{http://schemas.openxmlformats.org/drawingml/2006/main}lstStyle')
                
                # 设置字体
                p = txPr.find('.//{http://schemas.openxmlformats.org/drawingml/2006/main}p')
                if p is None:
                    p = etree.SubElement(txPr, '{http://schemas.openxmlformats.org/drawingml/2006/main}p')
                
                pPr = p.find('.//{http://schemas.openxmlformats.org/drawingml/2006/main}pPr')
                if pPr is None:
                    pPr = etree.SubElement(p, '{http://schemas.openxmlformats.org/drawingml/2006/main}pPr')
                
                defRPr = pPr.find('.//{http://schemas.openxmlformats.org/drawingml/2006/main}defRPr')
                if defRPr is None:
                    defRPr = etree.SubElement(pPr, '{http://schemas.openxmlformats.org/drawingml/2006/main}defRPr')
                
                if config.font_size_pt:
                    defRPr.set('sz', str(int(config.font_size_pt * 100)))
                
                if config.font_name:
                    latin = defRPr.find('.//{http://schemas.openxmlformats.org/drawingml/2006/main}latin')
                    if latin is None:
                        latin = etree.SubElement(defRPr, '{http://schemas.openxmlformats.org/drawingml/2006/main}latin')
                    latin.set('typeface', config.font_name)
                
                print(f"  - 次值轴字体: {config.font_name} {config.font_size_pt}pt")
            
            # ⭐ 设置轴范围和刻度间隔（通过 XML）
            from lxml import etree
            
            if config.min_value is not None or config.max_value is not None:
                # 查找或创建 scaling 元素
                scaling = secondary_ax.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}scaling')
                if scaling is None:
                    scaling = etree.SubElement(secondary_ax, '{http://schemas.openxmlformats.org/drawingml/2006/chart}scaling')
                    # 添加 orientation
                    orientation = etree.SubElement(scaling, '{http://schemas.openxmlformats.org/drawingml/2006/chart}orientation')
                    orientation.set('val', 'minMax')
                
                if config.min_value is not None:
                    min_elem = scaling.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}min')
                    if min_elem is None:
                        min_elem = etree.SubElement(scaling, '{http://schemas.openxmlformats.org/drawingml/2006/chart}min')
                    min_elem.set('val', str(config.min_value))
                    print(f"  - 次值轴最小值: {config.min_value}")
                
                if config.max_value is not None:
                    max_elem = scaling.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}max')
                    if max_elem is None:
                        max_elem = etree.SubElement(scaling, '{http://schemas.openxmlformats.org/drawingml/2006/chart}max')
                    max_elem.set('val', str(config.max_value))
                    print(f"  - 次值轴最大值: {config.max_value}")
            
            if config.major_unit is not None:
                major_unit_elem = secondary_ax.find('.//{http://schemas.openxmlformats.org/drawingml/2006/chart}majorUnit')
                if major_unit_elem is None:
                    major_unit_elem = etree.SubElement(secondary_ax, '{http://schemas.openxmlformats.org/drawingml/2006/chart}majorUnit')
                major_unit_elem.set('val', str(config.major_unit))
                print(f"  - 次值轴刻度间隔: {config.major_unit}")
            
        except Exception as e:
            print(f"  ⚠️ 次值轴配置失败: {e}")
            import traceback
            traceback.print_exc()

