from pathlib import Path

from docx import Document
from docx.enum.section import WD_ORIENT
from docx.enum.table import WD_ALIGN_VERTICAL, WD_TABLE_ALIGNMENT
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Inches, Pt, RGBColor, Twips


ROOT = Path(__file__).resolve().parent
SOURCE = ROOT / "csfzx-host-vulnerability-remediation-20260720.md"
OUTPUT = ROOT / "城市副中心主机漏洞整改逐项最终判定.docx"


def set_cell_shading(cell, fill):
    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=75, start=95, bottom=75, end=95):
    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 margin, value in (("top", top), ("start", start), ("bottom", bottom), ("end", end)):
        node = tc_mar.find(qn(f"w:{margin}"))
        if node is None:
            node = OxmlElement(f"w:{margin}")
            tc_mar.append(node)
        node.set(qn("w:w"), str(value))
        node.set(qn("w:type"), "dxa")


def set_repeat_table_header(row):
    tr_pr = row._tr.get_or_add_trPr()
    tbl_header = OxmlElement("w:tblHeader")
    tbl_header.set(qn("w:val"), "true")
    tr_pr.append(tbl_header)


def set_row_cant_split(row):
    tr_pr = row._tr.get_or_add_trPr()
    cant_split = OxmlElement("w:cantSplit")
    tr_pr.append(cant_split)


def set_cell_width(cell, width_dxa):
    tc_pr = cell._tc.get_or_add_tcPr()
    tc_w = tc_pr.find(qn("w:tcW"))
    if tc_w is None:
        tc_w = OxmlElement("w:tcW")
        tc_pr.append(tc_w)
    tc_w.set(qn("w:w"), str(width_dxa))
    tc_w.set(qn("w:type"), "dxa")


def set_table_grid(table, widths):
    tbl = table._tbl
    tbl_pr = tbl.tblPr
    tbl_w = tbl_pr.find(qn("w:tblW"))
    if tbl_w is None:
        tbl_w = OxmlElement("w:tblW")
        tbl_pr.append(tbl_w)
    tbl_w.set(qn("w:w"), str(sum(widths)))
    tbl_w.set(qn("w:type"), "dxa")

    grid = tbl.tblGrid
    for child in list(grid):
        grid.remove(child)
    for width in widths:
        col = OxmlElement("w:gridCol")
        col.set(qn("w:w"), str(width))
        grid.append(col)
    for idx, width in enumerate(widths):
        table.columns[idx].width = Twips(width)


def set_run_font(run, name="Hiragino Sans GB", size=9, bold=False, color=None):
    run.font.name = name
    run._element.rPr.rFonts.set(qn("w:eastAsia"), name)
    run.font.size = Pt(size)
    run.bold = bold
    if color:
        run.font.color.rgb = RGBColor.from_string(color)


def add_page_number(paragraph):
    paragraph.alignment = WD_ALIGN_PARAGRAPH.RIGHT
    run = paragraph.add_run("第 ")
    set_run_font(run, size=8, color="667085")
    begin = OxmlElement("w:fldChar")
    begin.set(qn("w:fldCharType"), "begin")
    instr = OxmlElement("w:instrText")
    instr.set(qn("xml:space"), "preserve")
    instr.text = " PAGE "
    end = OxmlElement("w:fldChar")
    end.set(qn("w:fldCharType"), "end")
    run._r.extend([begin, instr, end])
    end_run = paragraph.add_run(" 页")
    set_run_font(end_run, size=8, color="667085")


def parse_table(markdown):
    marker = "| 序号 | 风险等级 | 原报告漏洞项 | 是否已修复 | 最终状态 | 判定依据 |"
    lines = markdown.splitlines()
    start = lines.index(marker)
    rows = []
    for line in lines[start + 2 :]:
        if not line.startswith("|"):
            break
        rows.append([part.strip().replace("`", "") for part in line.strip().strip("|").split("|")])
    if len(rows) != 26:
        raise RuntimeError(f"Expected 26 rows, got {len(rows)}")
    return rows


def build():
    markdown = SOURCE.read_text(encoding="utf-8")
    rows = parse_table(markdown)

    doc = Document()
    section = doc.sections[0]
    section.orientation = WD_ORIENT.LANDSCAPE
    section.page_width, section.page_height = section.page_height, section.page_width
    section.top_margin = Inches(0.55)
    section.bottom_margin = Inches(0.55)
    section.left_margin = Inches(0.48)
    section.right_margin = Inches(0.48)
    section.header_distance = Inches(0.22)
    section.footer_distance = Inches(0.25)

    styles = doc.styles
    normal = styles["Normal"]
    normal.font.name = "Hiragino Sans GB"
    normal._element.rPr.rFonts.set(qn("w:eastAsia"), "Hiragino Sans GB")
    normal.font.size = Pt(10)
    normal.paragraph_format.space_after = Pt(5)
    normal.paragraph_format.line_spacing = 1.1

    title = doc.add_paragraph()
    title.alignment = WD_ALIGN_PARAGRAPH.CENTER
    title.paragraph_format.space_after = Pt(5)
    run = title.add_run("城市副中心主机漏洞整改逐项最终判定")
    set_run_font(run, size=18, bold=True, color="1F4D78")

    subtitle = doc.add_paragraph()
    subtitle.alignment = WD_ALIGN_PARAGRAPH.CENTER
    subtitle.paragraph_format.space_after = Pt(10)
    run = subtitle.add_run("对应原漏扫报告：79-主机_REF-20260716-0038_机管局-综述报告.docx")
    set_run_font(run, size=9, color="667085")

    lead = doc.add_paragraph()
    lead.paragraph_format.space_after = Pt(8)
    run = lead.add_run(
        "判定说明：本表对原报告 26 个唯一漏洞项逐项闭环。‘是否已修复’为‘是’表示已执行补丁、升级、配置整改、明确缓解或消除受影响组件；为‘否’时应结合最终状态判断，不代表仍有待执行的风险整改。"
    )
    set_run_font(run, size=9.5, color="344054")

    headers = ["序号", "风险等级", "原报告漏洞项", "是否已修复", "最终状态", "判定依据"]
    widths = [520, 700, 1800, 850, 1800, 8200]
    table = doc.add_table(rows=1, cols=len(headers))
    table.alignment = WD_TABLE_ALIGNMENT.CENTER
    table.autofit = False
    table.style = "Table Grid"
    set_table_grid(table, widths)

    header = table.rows[0]
    set_repeat_table_header(header)
    set_row_cant_split(header)
    for idx, (cell, text) in enumerate(zip(header.cells, headers)):
        set_cell_width(cell, widths[idx])
        set_cell_shading(cell, "D9EAF7")
        set_cell_margins(cell, top=100, bottom=100)
        cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
        p = cell.paragraphs[0]
        p.alignment = WD_ALIGN_PARAGRAPH.CENTER
        p.paragraph_format.space_after = Pt(0)
        run = p.add_run(text)
        set_run_font(run, size=9, bold=True, color="1F2937")

    risk_fill = {
        "紧急": "FDE2E1",
        "高危": "FCE8D5",
        "中危": "FFF4CC",
        "低危": "E8F1FB",
        "信息": "EEF1F4",
    }

    for values in rows:
        row = table.add_row()
        set_row_cant_split(row)
        for idx, (cell, text) in enumerate(zip(row.cells, values)):
            set_cell_width(cell, widths[idx])
            set_cell_margins(cell)
            cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
            p = cell.paragraphs[0]
            p.paragraph_format.space_after = Pt(0)
            p.paragraph_format.line_spacing = 1.05
            if idx in (0, 1, 3):
                p.alignment = WD_ALIGN_PARAGRAPH.CENTER
            run = p.add_run(text)
            color = "157347" if idx == 3 and text == "是" else "475467"
            if idx == 3 and text == "否":
                color = "667085"
            set_run_font(run, size=8.2, bold=(idx in (1, 3)), color=color)
            if idx == 1:
                set_cell_shading(cell, risk_fill.get(text, "FFFFFF"))
        if int(values[0]) % 2 == 0:
            for idx, cell in enumerate(row.cells):
                if idx != 1:
                    set_cell_shading(cell, "F8FAFC")

    doc.add_paragraph()
    heading = doc.add_paragraph()
    heading.paragraph_format.space_before = Pt(6)
    heading.paragraph_format.space_after = Pt(5)
    run = heading.add_run("统计与结论")
    set_run_font(run, size=13, bold=True, color="2E74B5")

    bullets = [
        "原报告唯一漏洞项风险分布：紧急 1 项、高危 5 项、中危 7 项、低危 6 项、信息 7 项，共 26 项。",
        "原报告按 63、64 两台主机累计出现次数：紧急 2 次、高危 10 次、中危 14 次、低危 12 次、信息 23 次，共 61 次。",
        "直接修复、按升级建议完成、发行版补丁闭环、配置缓解或消除受影响组件：16 项。",
        "不适用、争议或原报告误报并已形成说明：3 项；纯信息项：7 项。",
        "63、64 双节点主机验证：FAILURES=0；PC 登录业务回归：LOGIN=PASS。",
    ]
    for text in bullets:
        p = doc.add_paragraph(style="List Bullet")
        p.paragraph_format.space_after = Pt(3)
        run = p.add_run(text)
        set_run_font(run, size=9.5, color="344054")

    conclusion = doc.add_paragraph()
    conclusion.paragraph_format.space_before = Pt(6)
    conclusion.paragraph_format.space_after = Pt(0)
    run = conclusion.add_run("最终结论：")
    set_run_font(run, size=10, bold=True, color="1F4D78")
    run = conclusion.add_run(
        "原主机漏扫报告中的所有可操作风险均已完成整改，没有仍待执行的中高危主机配置漏洞。后续复扫若因扫描器仅识别上游版本号而再次命中部分 OpenSSH CVE，应同步提交发行版回移补丁、官方包升级证明及不适用性说明。"
    )
    set_run_font(run, size=10, color="344054")

    footer = section.footer.paragraphs[0]
    add_page_number(footer)

    core = doc.core_properties
    core.title = "城市副中心主机漏洞整改逐项最终判定"
    core.subject = "原漏扫报告第16节整改判定"
    core.author = "城市副中心项目组"

    doc.save(OUTPUT)
    print(OUTPUT)


if __name__ == "__main__":
    build()
