#!/usr/bin/env python3
from __future__ import annotations

import re
from pathlib import Path

from docx import Document
from docx.enum.section import WD_SECTION
from docx.enum.style import WD_STYLE_TYPE
from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_TABLE_ALIGNMENT
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_BREAK, WD_LINE_SPACING
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Cm, Inches, Pt, RGBColor


ROOT = Path(__file__).resolve().parent
STARTER = ROOT / "working-copy.docx"
SOURCE_MD = ROOT / "校园智慧食堂管理系统学校端操作手册_V1.0.md"
OUTPUT = ROOT / "校园智慧食堂管理系统学校端操作手册_V1.0.docx"


def set_cell_shading(cell, fill: str) -> None:
    tc_pr = cell._tc.get_or_add_tcPr()
    shd = tc_pr.find(qn("w:shd"))
    if shd is None:
        shd = OxmlElement("w:shd")
        tc_pr.append(shd)
    shd.set(qn("w:fill"), fill)


def set_cell_margins(cell, top=120, start=150, bottom=120, end=150) -> None:
    tc = cell._tc
    tc_pr = tc.get_or_add_tcPr()
    tc_mar = tc_pr.first_child_found_in("w:tcMar")
    if tc_mar is None:
        tc_mar = OxmlElement("w:tcMar")
        tc_pr.append(tc_mar)
    for m, value in (("top", top), ("start", start), ("bottom", bottom), ("end", end)):
        node = tc_mar.find(qn(f"w:{m}"))
        if node is None:
            node = OxmlElement(f"w:{m}")
            tc_mar.append(node)
        node.set(qn("w:w"), str(value))
        node.set(qn("w:type"), "dxa")


def set_table_borders(table, color="D9D9D9", size="6") -> None:
    tbl_pr = table._tbl.tblPr
    borders = tbl_pr.first_child_found_in("w:tblBorders")
    if borders is None:
        borders = OxmlElement("w:tblBorders")
        tbl_pr.append(borders)
    for edge in ("top", "left", "bottom", "right", "insideH", "insideV"):
        tag = f"w:{edge}"
        el = borders.find(qn(tag))
        if el is None:
            el = OxmlElement(tag)
            borders.append(el)
        el.set(qn("w:val"), "single")
        el.set(qn("w:sz"), size)
        el.set(qn("w:space"), "0")
        el.set(qn("w:color"), color)


def set_run_font(run, east_asia="Noto Sans CJK SC", latin="Arial", size=10.5, bold=None, color="000000") -> None:
    run.font.name = east_asia
    rfonts = run._element.get_or_add_rPr().rFonts
    rfonts.set(qn("w:ascii"), east_asia)
    rfonts.set(qn("w:hAnsi"), east_asia)
    rfonts.set(qn("w:eastAsia"), east_asia)
    rfonts.set(qn("w:cs"), east_asia)
    run.font.size = Pt(size)
    run.font.color.rgb = RGBColor.from_string(color)
    if bold is not None:
        run.bold = bold


def set_paragraph_defaults(p, space_after=5, line=1.35) -> None:
    fmt = p.paragraph_format
    fmt.space_after = Pt(space_after)
    fmt.line_spacing = line
    fmt.widow_control = True


def ensure_styles(doc: Document) -> None:
    styles = doc.styles
    normal = styles["Normal"]
    normal.font.name = "Noto Sans CJK SC"
    normal_fonts = normal._element.get_or_add_rPr().rFonts
    for key in ("ascii", "hAnsi", "eastAsia", "cs"):
        normal_fonts.set(qn(f"w:{key}"), "Noto Sans CJK SC")
    normal.font.size = Pt(10.5)
    normal.font.color.rgb = RGBColor(0, 0, 0)
    normal.paragraph_format.line_spacing = 1.35
    normal.paragraph_format.space_after = Pt(5)

    if "Title" not in styles:
        title = styles.add_style("Title", WD_STYLE_TYPE.PARAGRAPH)
    else:
        title = styles["Title"]
    title.font.name = "Noto Sans CJK SC"
    title_fonts = title._element.get_or_add_rPr().rFonts
    for key in ("ascii", "hAnsi", "eastAsia", "cs"):
        title_fonts.set(qn(f"w:{key}"), "Noto Sans CJK SC")
    title.font.size = Pt(26)
    title.font.bold = True
    title.font.color.rgb = RGBColor(0, 0, 0)
    title.paragraph_format.space_after = Pt(14)
    title.paragraph_format.keep_with_next = True

    for style_name, size, before, after in (
        ("Heading 1", 17, 18, 8),
        ("Heading 2", 13, 13, 6),
    ):
        style = styles[style_name]
        style.font.name = "Noto Sans CJK SC"
        style_fonts = style._element.get_or_add_rPr().rFonts
        for key in ("ascii", "hAnsi", "eastAsia", "cs"):
            style_fonts.set(qn(f"w:{key}"), "Noto Sans CJK SC")
        style.font.size = Pt(size)
        style.font.bold = True
        style.font.color.rgb = RGBColor(0, 0, 0)
        style.paragraph_format.space_before = Pt(before)
        style.paragraph_format.space_after = Pt(after)
        style.paragraph_format.keep_with_next = True

    if "Figure Caption" not in styles:
        cap = styles.add_style("Figure Caption", WD_STYLE_TYPE.PARAGRAPH)
    else:
        cap = styles["Figure Caption"]
    cap.font.name = "Noto Sans CJK SC"
    cap_fonts = cap._element.get_or_add_rPr().rFonts
    for key in ("ascii", "hAnsi", "eastAsia", "cs"):
        cap_fonts.set(qn(f"w:{key}"), "Noto Sans CJK SC")
    cap.font.size = Pt(9)
    cap.font.color.rgb = RGBColor.from_string("666666")
    cap.paragraph_format.space_before = Pt(3)
    cap.paragraph_format.space_after = Pt(8)

    for list_style_name in ("List Number", "List Bullet"):
        if list_style_name not in styles:
            list_style = styles.add_style(list_style_name, WD_STYLE_TYPE.PARAGRAPH)
            list_style.base_style = styles["List Paragraph"]
            list_style.font.name = "Arial"
            list_style._element.get_or_add_rPr().rFonts.set(qn("w:eastAsia"), "Noto Sans CJK SC")
            list_style.font.size = Pt(10.5)


def clear_body(doc: Document) -> None:
    body = doc._body._element
    sect_pr = body.sectPr
    for child in list(body):
        if child is not sect_pr:
            body.remove(child)


def add_cover(doc: Document) -> None:
    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    p.paragraph_format.space_before = Pt(85)
    p.paragraph_format.space_after = Pt(18)
    run = p.add_run("校园智慧食堂管理系统")
    set_run_font(run, east_asia="Noto Sans CJK SC", latin="Arial", size=27, bold=True)

    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    p.paragraph_format.space_after = Pt(42)
    run = p.add_run("学校端操作手册")
    set_run_font(run, east_asia="Noto Sans CJK SC", latin="Arial", size=23, bold=True)

    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    p.paragraph_format.space_after = Pt(10)
    run = p.add_run("V1.0")
    set_run_font(run, east_asia="Noto Sans CJK SC", latin="Arial", size=15, bold=True, color="F26B21")

    table = doc.add_table(rows=4, cols=2)
    table.alignment = WD_TABLE_ALIGNMENT.CENTER
    table.autofit = False
    table.columns[0].width = Cm(3.4)
    table.columns[1].width = Cm(10.3)
    set_table_borders(table)
    rows = [
        ("适用对象", "学校管理员、食堂管理员、营养师、账单管理员、系统管理员"),
        ("事实依据", "学校端线上页面与既有后台端操作手册"),
        ("页面核验日期", "2026年9月30日"),
        ("编制单位", "康比特数字体育科技"),
    ]
    for idx, (label, value) in enumerate(rows):
        left, right = table.rows[idx].cells
        left.width, right.width = Cm(3.4), Cm(10.3)
        left.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
        right.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
        set_cell_shading(left, "EAF2F8")
        for cell in (left, right):
            set_cell_margins(cell)
        lp = left.paragraphs[0]
        lp.alignment = WD_ALIGN_PARAGRAPH.CENTER
        lr = lp.add_run(label)
        set_run_font(lr, east_asia="Noto Sans CJK SC", latin="Arial", size=10, bold=True)
        rp = right.paragraphs[0]
        rr = rp.add_run(value)
        set_run_font(rr, size=10)

    p = doc.add_paragraph()
    p.paragraph_format.space_before = Pt(28)
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    r = p.add_run("本手册不包含登录密码，截图中的人员、账号与账单数据已做脱敏处理。")
    set_run_font(r, size=9.5, color="555555")
    p.add_run().add_break(WD_BREAK.PAGE)


def add_rich_paragraph(doc: Document, text: str, style=None, indent=False) -> None:
    p = doc.add_paragraph(style=style)
    set_paragraph_defaults(p)
    if indent:
        p.paragraph_format.left_indent = Cm(0.7)
        p.paragraph_format.first_line_indent = Cm(-0.45)

    parts = re.split(r"(\*\*.+?\*\*)", text)
    for part in parts:
        if not part:
            continue
        if part.startswith("**") and part.endswith("**"):
            run = p.add_run(part[2:-2])
            set_run_font(run, east_asia="Noto Sans CJK SC", latin="Arial", size=10.5, bold=True)
        else:
            run = p.add_run(part)
            set_run_font(run, size=10.5)


def add_figure(doc: Document, rel_path: str, alt: str) -> None:
    path = ROOT / rel_path
    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    p.paragraph_format.keep_with_next = True
    p.paragraph_format.space_before = Pt(5)
    p.paragraph_format.space_after = Pt(2)
    run = p.add_run()
    run.add_picture(str(path), width=Inches(5.75))
    caption = doc.add_paragraph(style="Figure Caption")
    caption.alignment = WD_ALIGN_PARAGRAPH.CENTER
    caption.add_run(f"图  {alt}")


def parse_manual(doc: Document) -> None:
    lines = SOURCE_MD.read_text(encoding="utf-8").splitlines()
    start = next(i for i, line in enumerate(lines) if line.startswith("## 1 "))
    for raw in lines[start:]:
        line = raw.strip()
        if not line:
            continue
        if line.startswith("## "):
            text = line[3:].strip()
            p = doc.add_paragraph(text, style="Heading 1")
            if not text.startswith(("1 ", "2 ")):
                p.paragraph_format.page_break_before = True
            continue
        if line.startswith("### "):
            text = line[4:].strip()
            doc.add_paragraph(text, style="Heading 2")
            continue
        m = re.match(r"!\[(.*?)\]\((.*?)\)", line)
        if m:
            add_figure(doc, m.group(2), m.group(1))
            continue
        if re.match(r"^\d+\.\s+", line):
            add_rich_paragraph(doc, line, style="List Paragraph", indent=True)
            continue
        if line.startswith("- "):
            add_rich_paragraph(doc, "• " + line[2:], style="List Paragraph", indent=True)
            continue
        add_rich_paragraph(doc, line)


def main() -> None:
    doc = Document(str(STARTER))
    clear_body(doc)
    ensure_styles(doc)

    for section in doc.sections:
        section.top_margin = Cm(2.54)
        section.bottom_margin = Cm(2.54)
        section.left_margin = Cm(3.175)
        section.right_margin = Cm(3.175)
        section.header_distance = Cm(1.2)
        section.footer_distance = Cm(1.0)

    doc.core_properties.title = "校园智慧食堂管理系统学校端操作手册"
    doc.core_properties.subject = "学校端后台操作说明"
    doc.core_properties.author = "康比特数字体育科技"
    doc.core_properties.keywords = "校园智慧食堂, 学校端, 操作手册, 膳食营养, 阳光消费"

    add_cover(doc)
    parse_manual(doc)
    doc.save(str(OUTPUT))
    print(OUTPUT)


if __name__ == "__main__":
    main()
