"""DOCX 论文生成器."""

from typing import Any

from docx import Document
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Cm, Pt

from .formats import FontSettings, ParagraphSettings, ThesisFormat
from .models import Reference, Section, ThesisContent


class DocxGenerator:
    """DOCX 论文生成器."""

    def __init__(self, format_config: ThesisFormat):
        self.format = format_config
        self.document = Document()
        self._setup_page()
        self._setup_styles()

    def _setup_page(self) -> None:
        """设置页面格式."""
        section = self.document.sections[0]

        # 设置页面大小 (A4)
        section.page_width = Cm(self.format.page.page_width)
        section.page_height = Cm(self.format.page.page_height)

        # 设置页边距
        section.top_margin = Cm(self.format.page.top_margin)
        section.bottom_margin = Cm(self.format.page.bottom_margin)
        section.left_margin = Cm(self.format.page.left_margin)
        section.right_margin = Cm(self.format.page.right_margin)

        # 清除第一节的页脚（封面无页码）
        self._clear_footer(section)

    def _setup_styles(self) -> None:
        """设置文档样式."""
        # 设置默认字体
        style = self.document.styles["Normal"]
        font = style.font
        font.name = self.format.body_font.english_name
        font.size = Pt(self.format.body_font.size)
        style.element.rPr.rFonts.set(qn("w:eastAsia"), self.format.body_font.name)

    def _set_run_font(self, run: Any, font_settings: FontSettings) -> None:
        """设置文本运行字体."""
        run.font.name = font_settings.english_name
        run.font.size = Pt(font_settings.size)
        run.font.bold = font_settings.bold
        run.font.italic = font_settings.italic
        run.element.rPr.rFonts.set(qn("w:eastAsia"), font_settings.name)

    def _add_formatted_text(self, paragraph: Any, text: str, font_settings: FontSettings) -> None:
        """添加带格式的文本，支持 Markdown 加粗标记.
        
        处理 **文本** 格式的加粗标记，将其转换为 Word 的加粗格式。
        
        Args:
            paragraph: 段落对象
            text: 文本内容（可能包含 Markdown 加粗标记）
            font_settings: 字体设置
        """
        import re
        
        # 正则表达式匹配 **文本**
        pattern = r'\*\*(.*?)\*\*'
        
        # 分割文本
        parts = re.split(pattern, text)
        
        # 检查哪些是加粗部分
        matches = re.findall(pattern, text)
        
        # 构建加粗标记映射
        is_bold = [False] * len(parts)
        for i in range(len(parts)):
            # 如果这部分在 matches 中，说明它是加粗的
            if parts[i] in matches:
                is_bold[i] = True
        
        # 添加文本，根据标记设置加粗
        for i, part in enumerate(parts):
            if part:  # 跳过空字符串
                run = paragraph.add_run(part)
                # 创建字体设置副本
                current_font = FontSettings(
                    name=font_settings.name,
                    english_name=font_settings.english_name,
                    size=font_settings.size,
                    bold=font_settings.bold or is_bold[i],
                    italic=font_settings.italic,
                )
                self._set_run_font(run, current_font)

    def _set_paragraph_format(self, paragraph: Any, para_settings: ParagraphSettings) -> None:
        """设置段落格式."""
        # 对齐方式
        alignment_map = {
            "left": WD_ALIGN_PARAGRAPH.LEFT,
            "center": WD_ALIGN_PARAGRAPH.CENTER,
            "right": WD_ALIGN_PARAGRAPH.RIGHT,
            "justify": WD_ALIGN_PARAGRAPH.JUSTIFY,
        }
        paragraph.alignment = alignment_map.get(para_settings.alignment, WD_ALIGN_PARAGRAPH.LEFT)

        # 行距
        paragraph.paragraph_format.line_spacing = para_settings.line_spacing

        # 首行缩进
        if para_settings.first_line_indent > 0:
            # 转换为磅（1字符约等于0.35cm）
            paragraph.paragraph_format.first_line_indent = Cm(
                para_settings.first_line_indent * 0.35
            )

        # 段前段后间距
        paragraph.paragraph_format.space_before = Pt(para_settings.space_before)
        paragraph.paragraph_format.space_after = Pt(para_settings.space_after)

    def generate(self, content: ThesisContent) -> Any:
        """生成论文文档.

        Args:
            content: 论文内容结构

        Returns:
            Document: 生成的 DOCX 文档
        """
        # 第一节：封面（无页码）
        if self.format.has_cover_page:
            self._add_cover_page(content)

        # 第二节：摘要和目录（罗马数字页码）
        # 添加分节符
        self.document.add_section()
        section_abstract = self.document.sections[-1]
        self._setup_section_margins(section_abstract)
        # 添加罗马数字页码
        self._add_section_page_number(section_abstract, "roman")

        # 添加摘要
        self._add_abstract(content)

        # 添加目录
        if self.format.has_toc:
            self._add_toc(content)

        # 第三节：正文（阿拉伯数字页码）
        # 添加分节符
        self.document.add_section()
        section_body = self.document.sections[-1]
        self._setup_section_margins(section_body)
        # 添加阿拉伯数字页码
        self._add_section_page_number(section_body, "arabic")

        # 添加正文
        self._add_body(content)

        # 添加参考文献
        if content.references:
            self._add_references(content.references)

        return self.document

    def _clear_footer(self, section: Any) -> None:
        """清除页脚内容.

        Args:
            section: 文档节
        """
        footer = section.footer
        for para in footer.paragraphs:
            p = para._p
            p.getparent().remove(p)

    def _add_section_page_number(self, section: Any, style: str) -> None:
        """为节添加页码.

        使用底层 XML 创建独立的页脚，确保每个节可以有不同的页码格式。

        Args:
            section: 文档节
            style: 页码样式，"arabic" 或 "roman"
        """
        from docx.opc.constants import RELATIONSHIP_TYPE as RT
        from docx.oxml import parse_xml
        from docx.oxml.ns import nsdecls

        # 创建新的页脚部分
        footer_xml = f'''
        <w:ftr {nsdecls("w")} {nsdecls("r")}>
            <w:p>
                <w:pPr>
                    <w:jc w:val="center"/>
                </w:pPr>
                <w:r>
                    <w:rPr>
                        <w:rFonts w:ascii="{self.format.body_font.english_name}" w:eastAsia="{self.format.body_font.name}"/>
                        <w:sz w:val="{int(self.format.body_font.size * 2)}"/>
                    </w:rPr>
                    <w:fldChar w:fldCharType="begin"/>
                </w:r>
                <w:r>
                    <w:rPr>
                        <w:rFonts w:ascii="{self.format.body_font.english_name}" w:eastAsia="{self.format.body_font.name}"/>
                        <w:sz w:val="{int(self.format.body_font.size * 2)}"/>
                    </w:rPr>
                    <w:instrText xml:space="preserve">PAGE {' * ROMAN' if style == 'roman' else ''}</w:instrText>
                </w:r>
                <w:r>
                    <w:rPr>
                        <w:rFonts w:ascii="{self.format.body_font.english_name}" w:eastAsia="{self.format.body_font.name}"/>
                        <w:sz w:val="{int(self.format.body_font.size * 2)}"/>
                    </w:rPr>
                    <w:fldChar w:fldCharType="separate"/>
                </w:r>
                <w:r>
                    <w:rPr>
                        <w:rFonts w:ascii="{self.format.body_font.english_name}" w:eastAsia="{self.format.body_font.name}"/>
                        <w:sz w:val="{int(self.format.body_font.size * 2)}"/>
                    </w:rPr>
                    <w:fldChar w:fldCharType="end"/>
                </w:r>
            </w:p>
        </w:ftr>
        '''

        # 解析 XML
        footer_element = parse_xml(footer_xml)

        # 将页脚添加到文档
        # 获取文档的 parts
        document_part = self.document.part
        package = document_part.package

        # 获取下一个可用的 rId
        used_rids = set()
        for key, rel in document_part.rels.items():
            if key.startswith('rId'):
                try:
                    used_rids.add(int(key[3:]))
                except ValueError:
                    pass

        next_rid_num = max(used_rids, default=8) + 1
        rId = f'rId{next_rid_num}'

        # 添加页脚部分到包
        from docx.parts.document import FooterPart
        from docx.opc.packuri import PackURI

        # 创建新的页脚部分
        partname = PackURI(f'/word/footer{next_rid_num}.xml')
        content_type = 'application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml'
        footer_part = FooterPart(
            partname,
            content_type,
            footer_element,
            package
        )

        # 注册部分到包
        package.parts.append(footer_part)

        # 添加关系到文档部分
        rel = document_part.rels.get_or_add(RT.FOOTER, footer_part)
        rId = rel.rId

        # 添加 footerReference 到节的 sectPr
        sectPr = section._sectPr
        # 移除现有的 footerReference
        for child in list(sectPr):
            if 'footerReference' in child.tag:
                sectPr.remove(child)

        # 添加新的 footerReference
        footer_ref = OxmlElement('w:footerReference')
        footer_ref.set(qn('w:type'), 'default')
        footer_ref.set(qn('r:id'), rId)
        sectPr.append(footer_ref)

    def _setup_section_margins(self, section: Any) -> None:
        """设置节的页边距.

        Args:
            section: 文档节
        """
        section.top_margin = Cm(self.format.page.top_margin)
        section.bottom_margin = Cm(self.format.page.bottom_margin)
        section.left_margin = Cm(self.format.page.left_margin)
        section.right_margin = Cm(self.format.page.right_margin)

    def _add_cover_page(self, content: ThesisContent) -> None:
        """添加封面."""
        # 学校名称（如果有）
        if content.metadata.college:
            para = self.document.add_paragraph()
            run = para.add_run(content.metadata.college)
            self._set_run_font(run, self.format.heading1_font)
            self._set_paragraph_format(
                para,
                ParagraphSettings(
                    alignment="center",
                    space_before=60,
                    space_after=30,
                ),
            )

        # 论文类型
        para = self.document.add_paragraph()
        run = para.add_run("本科毕业论文（设计）")
        self._set_run_font(run, self.format.heading1_font)
        self._set_paragraph_format(
            para,
            ParagraphSettings(
                alignment="center",
                space_before=30,
                space_after=60,
            ),
        )

        # 论文标题
        para = self.document.add_paragraph()
        run = para.add_run(content.metadata.title)
        self._set_run_font(run, self.format.title_font)
        self._set_paragraph_format(para, self.format.title_paragraph)

        # 作者信息表格
        info_items = [
            ("学    院", content.metadata.college),
            ("专    业", content.metadata.major),
            ("年    级", ""),
            ("姓    名", content.metadata.author),
            ("学    号", content.metadata.student_id),
            ("指导教师", content.metadata.advisor),
        ]

        table = self.document.add_table(rows=len(info_items), cols=2)
        table.alignment = WD_ALIGN_PARAGRAPH.CENTER

        for i, (label, value) in enumerate(info_items):
            row = table.rows[i]
            
            # 设置单元格垂直居中（在添加文本之前）
            for cell in row.cells:
                cell.vertical_alignment = 1  # CENTER
            
            # 添加标签
            cell_label = row.cells[0]
            cell_label.text = label
            for paragraph in cell_label.paragraphs:
                for run in paragraph.runs:
                    self._set_run_font(run, self.format.body_font)
                paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
            
            # 添加值
            cell_value = row.cells[1]
            cell_value.text = value if value else ""
            for paragraph in cell_value.paragraphs:
                for run in paragraph.runs:
                    self._set_run_font(run, self.format.body_font)
                paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER

        # 日期
        if content.metadata.date:
            para = self.document.add_paragraph()
            run = para.add_run(content.metadata.date)
            self._set_run_font(run, self.format.body_font)
            self._set_paragraph_format(
                para,
                ParagraphSettings(
                    alignment="center",
                    space_before=60,
                ),
            )

        # 分页
        self.document.add_page_break()

    def _add_abstract(self, content: ThesisContent) -> None:
        """添加摘要."""
        # 中文摘要
        if content.metadata.abstract_cn:
            para = self.document.add_paragraph()
            run = para.add_run("摘  要")
            self._set_run_font(run, self.format.abstract_title_font)
            self._set_paragraph_format(para, ParagraphSettings(alignment="center"))

            # 添加空行
            self.document.add_paragraph()

            para = self.document.add_paragraph()
            # 使用格式化文本方法，支持 Markdown 加粗
            self._add_formatted_text(para, content.metadata.abstract_cn, self.format.abstract_content_font)
            self._set_paragraph_format(para, self.format.abstract_paragraph)

            # 关键词
            if content.metadata.keywords_cn:
                para = self.document.add_paragraph()
                run = para.add_run("关键词：")
                self._set_run_font(
                    run,
                    FontSettings(
                        name=self.format.body_font.name,
                        english_name=self.format.body_font.english_name,
                        size=self.format.body_font.size,
                        bold=True,
                    ),
                )
                run = para.add_run("，".join(content.metadata.keywords_cn))
                self._set_run_font(run, self.format.body_font)
                self._set_paragraph_format(
                    para,
                    ParagraphSettings(
                        first_line_indent=0,
                        space_before=12,
                    ),
                )

            if self.format.has_abstract_separate_page:
                self.document.add_page_break()

        # 英文摘要
        if content.metadata.abstract_en:
            para = self.document.add_paragraph()
            run = para.add_run("Abstract")
            self._set_run_font(run, self.format.abstract_title_font)
            self._set_paragraph_format(para, ParagraphSettings(alignment="center"))

            # 添加空行
            self.document.add_paragraph()

            para = self.document.add_paragraph()
            # 使用格式化文本方法，支持 Markdown 加粗
            self._add_formatted_text(para, content.metadata.abstract_en, self.format.abstract_content_font)
            self._set_paragraph_format(para, self.format.abstract_paragraph)

            # Keywords
            if content.metadata.keywords_en:
                para = self.document.add_paragraph()
                run = para.add_run("Keywords: ")
                self._set_run_font(
                    run,
                    FontSettings(
                        name=self.format.body_font.name,
                        english_name=self.format.body_font.english_name,
                        size=self.format.body_font.size,
                        bold=True,
                    ),
                )
                run = para.add_run(", ".join(content.metadata.keywords_en))
                self._set_run_font(run, self.format.body_font)
                self._set_paragraph_format(
                    para,
                    ParagraphSettings(
                        first_line_indent=0,
                        space_before=12,
                    ),
                )

        self.document.add_page_break()

    def _add_toc(self, content: ThesisContent) -> None:
        """添加目录.

        Args:
            content: 论文内容结构
        """
        para = self.document.add_paragraph()
        run = para.add_run("目  录")
        self._set_run_font(run, self.format.heading1_font)
        self._set_paragraph_format(para, ParagraphSettings(alignment="center"))

        # 添加空行
        self.document.add_paragraph()

        # 从内容结构中添加目录项
        for section in content.sections:
            self._add_toc_entry(section, level=0)

        self.document.add_page_break()

    def _add_toc_entry(self, section: Section, level: int = 0) -> None:
        """添加目录项.

        Args:
            section: 章节
            level: 层级
        """
        # 跳过参考文献章节
        if "参考文献" in section.title:
            return

        # 添加目录项
        para = self.document.add_paragraph()
        run = para.add_run(section.title)
        self._set_run_font(run, self.format.body_font)

        # 根据层级设置缩进
        indent_map = {0: 0, 1: 0.5, 2: 1.0, 3: 1.5}
        para.paragraph_format.left_indent = Cm(indent_map.get(level, 0))

        # 递归添加子章节
        for subsection in section.subsections:
            self._add_toc_entry(subsection, level + 1)

    def _add_body(self, content: ThesisContent) -> None:
        """添加正文."""
        for section in content.sections:
            # 跳过参考文献章节，避免重复添加
            if "参考文献" in section.title:
                continue
            self._add_section(section)

    def _add_section(self, section: Section) -> None:
        """添加章节."""
        # 根据层级选择字体和段落设置
        if section.level == 1:
            font_settings = self.format.heading1_font
            para_settings = self.format.heading1_paragraph
        elif section.level == 2:
            font_settings = self.format.heading2_font
            para_settings = self.format.heading2_paragraph
        elif section.level == 3:
            font_settings = self.format.heading3_font
            para_settings = self.format.heading3_paragraph
        else:
            font_settings = self.format.body_font
            para_settings = self.format.body_paragraph

        # 添加标题
        para = self.document.add_paragraph()
        run = para.add_run(section.title)
        self._set_run_font(run, font_settings)
        self._set_paragraph_format(para, para_settings)

        # 添加内容
        if section.content:
            # 分段处理
            paragraphs = section.content.split("\n\n")
            for text in paragraphs:
                text = text.strip()
                if text:
                    para = self.document.add_paragraph()
                    # 使用格式化文本方法，支持 Markdown 加粗
                    self._add_formatted_text(para, text, self.format.body_font)
                    self._set_paragraph_format(para, self.format.body_paragraph)

        # 递归添加子章节
        for subsection in section.subsections:
            self._add_section(subsection)

    def _add_references(self, references: list[Reference]) -> None:
        """添加参考文献."""
        # 添加标题
        para = self.document.add_paragraph()
        run = para.add_run("参考文献")
        self._set_run_font(run, self.format.heading1_font)
        self._set_paragraph_format(para, self.format.heading1_paragraph)

        # 添加参考文献列表
        for ref in references:
            para = self.document.add_paragraph()
            run = para.add_run(f"[{ref.index}] {ref.content}")
            self._set_run_font(run, self.format.reference_font)
            self._set_paragraph_format(para, self.format.reference_paragraph)

    def save(self, filepath: str) -> None:
        """保存文档.

        Args:
            filepath: 保存路径
        """
        self.document.save(filepath)


def generate_thesis_docx(
    content: ThesisContent, format_config: ThesisFormat, output_path: str
) -> str:
    """生成论文 DOCX 文件.

    Args:
        content: 论文内容
        format_config: 格式配置
        output_path: 输出路径

    Returns:
        str: 输出文件路径
    """
    generator = DocxGenerator(format_config)
    generator.generate(content)
    generator.save(output_path)
    return output_path
