#!/usr/bin/env python3
import argparse
import json
import re
from pathlib import Path

from docx import Document
from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_ROW_HEIGHT_RULE
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Pt, RGBColor


def set_run_font(run, east_asia="PingFang SC", latin="Arial", size=8, bold=False, color=None):
    run.font.name = latin
    run.font.size = Pt(size)
    run.font.bold = bold
    if color:
        run.font.color.rgb = RGBColor(*color)
    rpr = run._element.get_or_add_rPr()
    fonts = rpr.rFonts
    if fonts is None:
        fonts = OxmlElement("w:rFonts")
        rpr.insert(0, fonts)
    fonts.set(qn("w:ascii"), latin)
    fonts.set(qn("w:hAnsi"), latin)
    fonts.set(qn("w:eastAsia"), east_asia)


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


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


def set_cell_margins(cell, top=90, start=90, bottom=90, end=90):
    tcpr = cell._tc.get_or_add_tcPr()
    tcmar = tcpr.first_child_found_in("w:tcMar")
    if tcmar is None:
        tcmar = OxmlElement("w:tcMar")
        tcpr.append(tcmar)
    for key, value in (("top", top), ("start", start), ("bottom", bottom), ("end", end)):
        node = tcmar.find(qn(f"w:{key}"))
        if node is None:
            node = OxmlElement(f"w:{key}")
            tcmar.append(node)
        node.set(qn("w:w"), str(value))
        node.set(qn("w:type"), "dxa")


def set_table_geometry(table, widths):
    table.autofit = False
    tblpr = table._tbl.tblPr
    tblw = tblpr.find(qn("w:tblW"))
    if tblw is None:
        tblw = OxmlElement("w:tblW")
        tblpr.append(tblw)
    tblw.set(qn("w:w"), str(sum(widths)))
    tblw.set(qn("w:type"), "dxa")
    tblind = tblpr.find(qn("w:tblInd"))
    if tblind is None:
        tblind = OxmlElement("w:tblInd")
        tblpr.append(tblind)
    tblind.set(qn("w:w"), "0")
    tblind.set(qn("w:type"), "dxa")
    grid = table._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 row in table.rows:
        for index, cell in enumerate(row.cells):
            set_cell_width(cell, widths[min(index, len(widths) - 1)])


def set_repeat_header(row):
    trpr = row._tr.get_or_add_trPr()
    header = OxmlElement("w:tblHeader")
    header.set(qn("w:val"), "true")
    trpr.append(header)


def prevent_row_split(row):
    trpr = row._tr.get_or_add_trPr()
    trpr.append(OxmlElement("w:cantSplit"))


def set_paragraph(paragraph, text, size=8, bold=False, color=None, align=WD_ALIGN_PARAGRAPH.LEFT):
    paragraph.alignment = align
    paragraph.paragraph_format.space_before = Pt(0)
    paragraph.paragraph_format.space_after = Pt(0)
    paragraph.paragraph_format.line_spacing = 1.05
    run = paragraph.add_run(text)
    set_run_font(run, size=size, bold=bold, color=color)


def load_rows(html_path):
    text = Path(html_path).read_text(encoding="utf-8")
    match = re.search(r"const rows=(\[.*?\]);const esc=", text, re.S)
    if not match:
        raise RuntimeError("Could not find field-list rows in HTML")
    rows = json.loads(match.group(1))
    if len(rows) != 93:
        raise RuntimeError(f"Expected 93 rows, found {len(rows)}")
    return rows


def clear_body(document):
    body = document._element.body
    for child in list(body):
        if child.tag != qn("w:sectPr"):
            body.remove(child)


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("working_docx")
    parser.add_argument("field_list_html")
    parser.add_argument("output_docx")
    args = parser.parse_args()

    rows = load_rows(args.field_list_html)
    document = Document(args.working_docx)
    clear_body(document)
    document.core_properties.title = "滨州项目：万医生接口可提供字段清单"
    document.core_properties.subject = "万医生营养健康接口客户沟通字段范围"
    document.core_properties.author = "康比特数字体育科技"
    document.core_properties.last_modified_by = "康比特数字体育科技"
    document.core_properties.comments = "客户沟通版，仅列本次可提供字段。"

    normal = document.styles["Normal"]
    normal.font.name = "Arial"
    normal.font.size = Pt(9)
    normal._element.rPr.rFonts.set(qn("w:eastAsia"), "PingFang SC")

    title = document.add_paragraph()
    title.alignment = WD_ALIGN_PARAGRAPH.CENTER
    title.paragraph_format.space_before = Pt(3)
    title.paragraph_format.space_after = Pt(3)
    title.paragraph_format.keep_with_next = True
    title_run = title.add_run("滨州项目：万医生接口可提供字段清单")
    set_run_font(title_run, size=16, bold=True, color=(20, 66, 135))

    subtitle = document.add_paragraph()
    subtitle.alignment = WD_ALIGN_PARAGRAPH.CENTER
    subtitle.paragraph_format.space_after = Pt(8)
    subtitle.paragraph_format.keep_with_next = True
    subtitle_run = subtitle.add_run("客户沟通版｜本清单仅列本次可提供字段，共 93 项")
    set_run_font(subtitle_run, size=9, color=(89, 103, 122))

    headers = ["序号", "接口", "对方字段", "类型", "我方提供来源/口径", "提供方式"]
    widths = [430, 820, 2240, 700, 3160, 956]
    table = document.add_table(rows=1, cols=len(headers))
    table.style = "Table Grid"
    set_table_geometry(table, widths)

    header_row = table.rows[0]
    header_row.height_rule = WD_ROW_HEIGHT_RULE.AT_LEAST
    set_repeat_header(header_row)
    prevent_row_split(header_row)
    for index, text in enumerate(headers):
        cell = header_row.cells[index]
        cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
        set_cell_shading(cell, "DCE8F7")
        set_cell_margins(cell, top=110, bottom=110)
        set_paragraph(cell.paragraphs[0], text, size=8, bold=True, color=(27, 55, 94), align=WD_ALIGN_PARAGRAPH.CENTER)

    last_api = None
    for item in rows:
        if item["api"] != last_api:
            last_api = item["api"]
            group_row = table.add_row()
            merged = group_row.cells[0]
            for cell in group_row.cells[1:]:
                merged = merged.merge(cell)
            merged.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
            set_cell_shading(merged, "EAF2FF")
            set_cell_margins(merged, top=100, bottom=100)
            set_paragraph(merged.paragraphs[0], last_api, size=8.5, bold=True, color=(18, 62, 129))
            prevent_row_split(group_row)

        row = table.add_row()
        prevent_row_split(row)
        values = [str(item["no"]), item["api"], item["field"], item["type"], item["source"], item["mode"]]
        for index, value in enumerate(values):
            cell = row.cells[index]
            cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
            set_cell_margins(cell)
            align = WD_ALIGN_PARAGRAPH.CENTER if index in (0, 1, 3, 5) else WD_ALIGN_PARAGRAPH.LEFT
            color = (23, 74, 156) if index == 2 else None
            set_paragraph(cell.paragraphs[0], value, size=7.5 if index in (2, 4) else 8, color=color, align=align)
        if int(item["no"]) % 2 == 0:
            for cell in row.cells:
                set_cell_shading(cell, "F8FAFD")

    set_table_geometry(table, widths)
    output = Path(args.output_docx)
    output.parent.mkdir(parents=True, exist_ok=True)
    document.save(output)
    print(output)


if __name__ == "__main__":
    main()
