"""
Word文档格式化模块
按照GB/T 9704-2012标准格式化金融研究报告
"""

from docx import Document
from docx.shared import Pt, Mm, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING
from docx.oxml.ns import qn
from docx.oxml import OxmlElement


class GBT9704Formatter:
    """
    GB/T 9704-2012党政机关公文格式化器
    """

    # 字体映射（号数 -> 磅值）
    FONT_SIZES = {
        '三号': 16,
        '四号': 14,
        '小四': 12,
        '五号': 10.5
    }

    def __init__(self, doc: Document):
        """
        初始化格式化器

        Args:
            doc: Document对象
        """
        self.doc = doc
        self._setup_page()

    def _setup_page(self):
        """设置页面格式"""
        for section in self.doc.sections:
            # 页面边距（单位：毫米）
            section.top_margin = Mm(37)
            section.bottom_margin = Mm(35)
            section.left_margin = Mm(28)
            section.right_margin = Mm(26)

    def set_font(self, run, font_name: str, size: int = None, bold: bool = None, color: RGBColor = None):
        """
        设置字体

        Args:
            run: Run对象
            font_name: 字体名称（中文字体）
            size: 字号（磅值），如 16, 14, 12
            bold: 是否加粗
            color: 字体颜色
        """
        run.font.name = font_name
        run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name)

        if size is not None:
            run.font.size = Pt(size)
        if bold is not None:
            run.font.bold = bold
        if color is not None:
            run.font.color.rgb = color

    def add_heading(self, text: str, level: int = 1, font_name: str = '黑体', size: int = 16):
        """
        添加标题

        Args:
            text: 标题文本
            level: 标题级别（1-3）
            font_name: 字体名称
            size: 字号

        Returns:
            Paragraph对象
        """
        if level == 1:
            heading = self.doc.add_heading(text, level=1)
            run = heading.runs[0]
            self.set_font(run, font_name, size, bold=True)
            heading.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
            heading.paragraph_format.space_before = Pt(0)
            heading.paragraph_format.space_after = Pt(0)
            return heading
        else:
            # 二级、三级标题使用"一、"、"(一)"等格式
            prefix_map = {1: '一、', 2: '（一）', 3: '1.'}
            prefix = prefix_map.get(level, '')

            paragraph = self.doc.add_paragraph(f"{prefix}{text}")
            run = paragraph.runs[0]
            self.set_font(run, font_name, size, bold=True)

            # 段落格式
            paragraph.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
            paragraph.paragraph_format.space_before = Pt(0)
            paragraph.paragraph_format.space_after = Pt(0)
            paragraph.paragraph_format.line_spacing = Pt(25)
            paragraph.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY

            return paragraph

    def add_paragraph(self, text: str,
                     font_name: str = '仿宋',
                     size: int = 14,
                     first_line_indent: bool = True,
                     alignment: str = 'justify'):
        """
        添加正文段落

        Args:
            text: 段落文本
            font_name: 字体名称
            size: 字号（磅值）
            first_line_indent: 是否首行缩进
            alignment: 对齐方式（'left', 'center', 'right', 'justify'）

        Returns:
            Paragraph对象
        """
        paragraph = self.doc.add_paragraph(text)
        run = paragraph.runs[0]
        self.set_font(run, font_name, size)

        # 段落格式
        alignment_map = {
            'left': WD_ALIGN_PARAGRAPH.LEFT,
            'center': WD_ALIGN_PARAGRAPH.CENTER,
            'right': WD_ALIGN_PARAGRAPH.RIGHT,
            'justify': WD_ALIGN_PARAGRAPH.JUSTIFY
        }
        paragraph.paragraph_format.alignment = alignment_map.get(alignment, WD_ALIGN_PARAGRAPH.JUSTIFY)

        if first_line_indent:
            # 首行缩进2字符（28pt）
            paragraph.paragraph_format.first_line_indent = Pt(28)

        # 行距固定值28pt
        paragraph.paragraph_format.line_spacing = Pt(28)
        paragraph.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY

        # 段前段后距为0
        paragraph.paragraph_format.space_before = Pt(0)
        paragraph.paragraph_format.space_after = Pt(0)

        return paragraph

    def add_bullet_list(self, items: List[str], font_name: str = '仿宋', size: int = 14):
        """
        添加项目符号列表

        Args:
            items: 列表项
            font_name: 字体名称
            size: 字号
        """
        for item in items:
            paragraph = self.doc.add_paragraph(item, style='List Bullet')
            run = paragraph.runs[0]
            self.set_font(run, font_name, size)
            paragraph.paragraph_format.line_spacing = Pt(28)
            paragraph.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY
            paragraph.paragraph_format.space_before = Pt(0)
            paragraph.paragraph_format.space_after = Pt(0)

    def add_three_line_table(self, data: List[List[str]],
                            headers: List[str],
                            font_name: str = '仿宋',
                            size: int = 12):
        """
        添加三线表

        Args:
            data: 表格数据（二维列表）
            headers: 表头
            font_name: 字体名称
            size: 字号

        Returns:
            Table对象
        """
        # 创建表格
        table = self.doc.add_table(rows=len(data) + 1, cols=len(headers))
        self._set_three_line_table_borders(table)

        # 设置表头
        header_cells = table.rows[0].cells
        for i, header in enumerate(headers):
            cell = header_cells[i]
            self._set_cell_font(cell, header, font_name, size, bold=True)
            cell.paragraphs[0].paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER

        # 设置数据行
        for row_idx, row_data in enumerate(data, start=1):
            cells = table.rows[row_idx].cells
            for i, cell_data in enumerate(row_data):
                cell = cells[i]
                self._set_cell_font(cell, str(cell_data), font_name, size)
                cell.paragraphs[0].paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER

        return table

    def _set_three_line_table_borders(self, table):
        """设置三线表边框"""
        tblPr = table._tbl.tblPr
        tblBorders = OxmlElement('w:tblBorders')

        # 顶底边框 1.5pt (sz=18)
        for pos in ['top', 'bottom']:
            border = OxmlElement(f'w:{pos}')
            border.set(qn('w:val'), 'single')
            border.set(qn('w:sz'), '18')
            border.set(qn('w:color'), 'auto')
            tblBorders.append(border)

        # 内部横线 0.75pt (sz=9)
        for pos in ['insideH']:
            border = OxmlElement(f'w:{pos}')
            border.set(qn('w:val'), 'single')
            border.set(qn('w:sz'), '9')
            border.set(qn('w:color'), 'auto')
            tblBorders.append(border)

        # 无左右和竖线
        for pos in ['left', 'right', 'insideV']:
            border = OxmlElement(f'w:{pos}')
            border.set(qn('w:val'), 'none')
            tblBorders.append(border)

        tblPr.append(tblBorders)

    def _set_cell_font(self, cell, text: str, font_name: str, size: int, bold: bool = False):
        """设置单元格字体"""
        cell.text = ''
        run = cell.paragraphs[0].add_run(text)
        self.set_font(run, font_name, size, bold=bold)

    def add_page_number_footer(self):
        """添加页码页脚"""
        for section in self.doc.sections:
            footer = section.footer

            # 添加页码段落
            p = footer.paragraphs[0] if footer.paragraphs else footer.add_paragraph()
            p.alignment = WD_ALIGN_PARAGRAPH.CENTER

            # 页码格式：— 1 —
            self.add_footer_run(p, '— ', font_name='仿宋', size=12)

            # 添加页码字段
            self.add_page_number_field(p)

            self.add_footer_run(p, ' ', font_name='仿宋', size=12)
            self.add_footer_run(p, '—', font_name='仿宋', size=12)

    def add_footer_run(self, paragraph, text: str, font_name: str, size: int):
        """添加页脚run"""
        run = paragraph.add_run(text)
        self.set_font(run, font_name, size)

    def add_page_number_field(self, paragraph):
        """添加页码字段"""
        fldChar1 = OxmlElement('w:fldChar')
        fldChar1.set(qn('w:fldCharType'), 'begin')

        instrText = OxmlElement('w:instrText')
        instrText.set(qn('xml:space'), 'preserve')
        instrText.text = "PAGE"

        fldChar2 = OxmlElement('w:fldChar')
        fldChar2.set(qn('w:fldCharType'), 'end')

        run = paragraph.add_run()
        run._r.append(fldChar1)
        run._r.append(instrText)
        run._r.append(fldChar2)

        self.set_font(run, '仿宋', 12)

    def add_cover_page(self, title: str, organization: str, date_str: str):
        """
        添加封面页

        Args:
            title: 报告标题
            organization: 机构名称
            date_str: 日期
        """
        # 标题
        paragraph = self.doc.add_paragraph()
        run = paragraph.add_run(title)
        self.set_font(run, '黑体', 22, bold=True)
        paragraph.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
        paragraph.paragraph_format.space_before = Pt(100)
        paragraph.paragraph_format.space_after = Pt(50)

        # 机构名称
        paragraph = self.doc.add_paragraph()
        run = paragraph.add_run(organization)
        self.set_font(run, '黑体', 16)
        paragraph.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
        paragraph.paragraph_format.space_after = Pt(20)

        # 日期
        paragraph = self.doc.add_paragraph()
        run = paragraph.add_run(date_str)
        self.set_font(run, '黑体', 16)
        paragraph.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER

        # 添加分页符
        self.doc.add_page_break()

    def add_data_source_footnote(self, text: str = "数据来源：Wind资讯"):
        """
        添加数据来源注脚

        Args:
            text: 注脚文本
        """
        paragraph = self.doc.add_paragraph()
        run = paragraph.add_run(text)
        self.set_font(run, '仿宋', 12)
        paragraph.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.RIGHT


# 便捷函数
def create_formatted_document() -> Tuple[Document, GBT9704Formatter]:
    """
    创建格式化的Word文档

    Returns:
        (Document, GBT9704Formatter) 文档对象和格式化器
    """
    doc = Document()
    formatter = GBT9704Formatter(doc)
    return doc, formatter


if __name__ == '__main__':
    # 测试代码
    doc, formatter = create_formatted_document()

    # 添加封面
    formatter.add_cover_page(
        '某某股票投资研究报告',
        '某某证券研究所',
        '2026年2月6日'
    )

    # 添加核心提要
    formatter.add_heading('核心提要', level=1)
    formatter.add_bullet_list([
        '该公司2025年三季度净资产收益率（ROE）为15.2%，盈利能力较强',
        '当前市盈率（TTM）为25.3倍，略高于行业平均水平',
        '技术面显示股价呈现震荡上行趋势，建议关注回调机会',
        '给予"买入"评级，目标价格35元'
    ])

    # 添加投资评级表格
    formatter.add_heading('一、投资评级', level=1)
    data = [
        ['买入', '35.00元', '2026年12月31日', '中风险']
    ]
    formatter.add_three_line_table(
        data,
        ['投资建议', '目标价格', '有效期', '风险等级']
    )

    # 添加数据来源
    formatter.add_data_source_footnote()

    # 保存文档
    output_path = 'test_report.docx'
    doc.save(output_path)
    print(f"测试报告已保存到: {output_path}")
