import json
import zipfile
from pathlib import Path

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

ROOT = Path(__file__).resolve().parent
data = json.loads((ROOT / 'report-data.json').read_text())
doc = Document(ROOT / 'working-template.docx')
pending_page = False
doc.core_properties.author = '康比特数字体育科技'
doc.core_properties.title = data['title'] + '：' + data['subtitle']
doc.core_properties.subject = '项目问题处理进展与后续解决方案'
doc.core_properties.keywords = '中央美术学院,智慧餐厅,问题处理'

def set_font(run, size=11, bold=False, color='222222', name='宋体'):
    run.font.name = name
    run.font.size = Pt(size)
    run.font.bold = bold
    run.font.color.rgb = RGBColor.from_string(color)
    run._element.get_or_add_rPr().rFonts.set(qn('w:eastAsia'), name)

normal = doc.styles['Normal']
normal.font.name = '宋体'
normal.font.size = Pt(11)
normal._element.get_or_add_rPr().rFonts.set(qn('w:eastAsia'), '宋体')
normal.paragraph_format.space_after = Pt(6)
normal.paragraph_format.line_spacing = 1.25
for name, size in [('Heading 1', 15), ('Heading 2', 12)]:
    style = doc.styles[name]
    style.font.name = '黑体'
    style.font.size = Pt(size)
    style.font.bold = True
    style.font.color.rgb = RGBColor.from_string('202020')
    style._element.get_or_add_rPr().rFonts.set(qn('w:eastAsia'), '黑体')
    style.paragraph_format.space_before = Pt(10)
    style.paragraph_format.space_after = Pt(7)
    style.paragraph_format.keep_with_next = True

def para(text, size=11, bold=False, after=6, color='222222', align=None):
    p = doc.add_paragraph()
    p.paragraph_format.space_after = Pt(after)
    p.paragraph_format.line_spacing = 1.22
    if align is not None:
        p.alignment = align
    set_font(p.add_run(text), size, bold, color)
    return p

def heading(text, level=1):
    global pending_page
    p = doc.add_paragraph(text, style=f'Heading {level}')
    if pending_page:
        p.paragraph_format.page_break_before = True
        pending_page = False
    return p

def label_paragraph(label, text, size=10.5):
    p = doc.add_paragraph()
    p.paragraph_format.space_after = Pt(5)
    p.paragraph_format.line_spacing = 1.22
    set_font(p.add_run(label), size, True)
    set_font(p.add_run(text), size)
    return p

def page():
    global pending_page
    pending_page = True

# Remove the starter's blank body paragraph; preserve section and page furniture.
for p in list(doc.paragraphs):
    if not p.text:
        p._element.getparent().remove(p._element)

para(data['title'], 20, True, 8, align=WD_ALIGN_PARAGRAPH.CENTER)
para(data['subtitle'], 16, True, 10, align=WD_ALIGN_PARAGRAPH.CENTER)
para('康比特数字体育科技  |  ' + data['date'], 10.5, after=12,
     color='666666', align=WD_ALIGN_PARAGRAPH.CENTER)
para('本说明汇总已确认完成的阶段性处理事项，并列出现场待处理及尚需确认关闭的问题。完成日期以可核实的实施记录为准，后续事项按对应方案推进。', 11, after=9)
para('资料范围：4份已取得的群聊笔记（记录日期为2026年7月29日至9月9日，重复内容已去重），及已取得的2026年10月8日现场测试问题截图。', 9.5, after=9, color='666666')
heading('一、我们已经解决的问题')
para('共列出5项有完成记录的实施事项。', 10.5, after=7)
table = doc.add_table(rows=1, cols=3)
table.autofit = False
widths = [Cm(3.1), Cm(8.7), Cm(2.85)]
for col, width in zip(table.columns, widths):
    col.width = width
for cell, text, width in zip(table.rows[0].cells, ['问题 / 事项', '解决方案与完成情况', '完成时间'], widths):
    cell.width = width
    cell.text = text
header_repeat = OxmlElement('w:tblHeader')
table.rows[0]._tr.get_or_add_trPr().append(header_repeat)
for item in data['completed']:
    cells = table.add_row().cells
    for c, txt, width in zip(cells, [item['problem'], item['result'], item['date']], widths):
        c.width = width
        c.text = txt
for ri, row in enumerate(table.rows):
    no_split = OxmlElement('w:cantSplit')
    row._tr.get_or_add_trPr().append(no_split)
    for cell in row.cells:
        tcpr = cell._tc.get_or_add_tcPr()
        shading = OxmlElement('w:shd')
        shading.set(qn('w:fill'), 'EDF1F4' if ri == 0 else 'FFFFFF')
        tcpr.append(shading)
        margins = OxmlElement('w:tcMar')
        for side in ['top', 'bottom', 'left', 'right']:
            el = OxmlElement('w:' + side)
            el.set(qn('w:w'), '80' if side in ['top', 'bottom'] else '100')
            el.set(qn('w:type'), 'dxa')
            margins.append(el)
        tcpr.append(margins)
        borders = OxmlElement('w:tcBorders')
        for side in ['top', 'left', 'bottom', 'right']:
            el = OxmlElement('w:' + side)
            el.set(qn('w:val'), 'single')
            el.set(qn('w:sz'), '4')
            el.set(qn('w:color'), 'CCD3D9')
            borders.append(el)
        tcpr.append(borders)
        for p in cell.paragraphs:
            p.paragraph_format.space_after = Pt(2)
            p.paragraph_format.line_spacing = 1.15
            for r in p.runs:
                set_font(r, 10, bold=(ri == 0))

page()
heading('二、我们还没有解决的问题')
heading('（一）现场待处理问题', 2)
para('以下两项按照最新现场问题表更新状态，分别推进识别优化和进一步复测。', 10.5, after=9)
for item in data['current_pending']:
    heading(item['id'][1:].lstrip('0') + '. ' + item['title'], 2)
    para('当前状态：' + item['status'], 10, after=4, color='555555')
    label_paragraph('当前情况：', item['observed'])
    label_paragraph('最新结论：', item['conclusion'])
    label_paragraph('解决方案：', item['solution'])
    label_paragraph('完成判据：', item['acceptance'])
para('餐盘频闪、识别中断问题的视频记录：VID_20261008_155651.mp4。累计重量偏差问题在现场表中附有两段视频记录。', 9.5, after=7, color='555555')
para('安排建议：先完成识别优化与持续复测，再确认修复效果和关闭时间；复测未复现的问题继续观察，取得稳定运行结果后登记关闭。', 10.5, after=6)

page()
heading('二、我们还没有解决的问题（续）')
heading('（二）需核实关闭的历史事项', 2)
para('以下事项在现有笔记中尚未形成后续完成确认，需核实最新情况后关闭；不将缺少记录直接视为现场仍存在故障。', 10, after=9, color='555555')
for item in data['historical_pending'][:5]:
    heading(str(int(item['id'][1:])) + '. ' + item['title'], 2)
    para(item['date'] + '  |  ' + item['status'], 9, after=3, color='666666')
    label_paragraph('现有记录：', item['observed'], 10)
    label_paragraph('解决及关闭方案：', item['solution'], 10)

page()
heading('二、我们还没有解决的问题（续）')
for item in data['historical_pending'][5:]:
    heading(str(int(item['id'][1:])) + '. ' + item['title'], 2)
    para(item['date'] + '  |  ' + item['status'], 9, after=3, color='666666')
    label_paragraph('现有记录：', item['observed'], 10)
    label_paragraph('解决及关闭方案：', item['solution'], 10)
heading('后续推进安排', 2)
para('优先处理餐盘识别与累计重量问题，并在复测中一并核验金额累计；同步核实历史事项是否已经完成。针对确需继续处理的事项，由康比特会同校方、食堂及相关厂家确认负责人和完成节点，实施后以现场复测或书面确认关闭。', 10.5)
para('报告中的解决方案为后续处理建议，具体实施内容与时间以双方确认的安排为准。', 9, color='666666')

page()
heading('二、我们还没有解决的问题（附现场记录）')
para('现场测试问题表', 12, True, after=10)
para('本页保留2026年10月8日现场问题表截图中的问题、视频线索、状态、说明及总结，与前述两项现场问题对应。', 10.5, after=10)
doc.add_picture(str(ROOT / '现场测试问题表_20261008.png'), width=Cm(14.6))
para('表中“待复测”与“经复测未复现、持续观察处理中”共同表示尚未完成稳定性验证，不据此认定问题已最终关闭。', 10, after=8)

# Footer page number; leave the retained header and logo untouched.
footer = doc.sections[0].footer.paragraphs[0]
footer.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = footer.add_run('第 ')
set_font(run, 9, color='777777')
field = OxmlElement('w:fldSimple')
field.set(qn('w:instr'), 'PAGE')
footer._p.append(field)
set_font(footer.add_run(' 页'), 9, color='777777')

output = ROOT / '中央美术学院智慧餐厅项目_问题处理进展及后续解决方案_20261008.docx'
# The retained document grid otherwise rounds Chinese line heights up to
# multiple grid lines in LibreOffice. Keep page geometry and header intact,
# and give newly authored body paragraphs explicit compact line spacing.
body_paragraphs = list(doc.paragraphs)
for tb in doc.tables:
    for row in tb.rows:
        for cell in row.cells:
            body_paragraphs.extend(cell.paragraphs)
for p in body_paragraphs:
    snap = OxmlElement('w:snapToGrid')
    snap.set(qn('w:val'), '0')
    p._p.get_or_add_pPr().append(snap)
    if p._p.xpath('.//w:drawing'):
        p.paragraph_format.line_spacing = 1.0
        p.paragraph_format.keep_together = True
        continue
    sizes = [r.font.size.pt for r in p.runs if r.font.size]
    size = max(sizes) if sizes else (p.style.font.size.pt if p.style.font.size else 11)
    p.paragraph_format.line_spacing = Pt(max(size + 4, 14))
    p.paragraph_format.line_spacing_rule = WD_LINE_SPACING.EXACTLY
doc.save(output)

# Preserve the header package parts byte for byte after python-docx serialization.
preserve = ['word/header1.xml', 'word/_rels/header1.xml.rels', 'word/media/image1.png']
with zipfile.ZipFile(ROOT / 'working-template.docx') as z:
    parts = {p: z.read(p) for p in preserve}
with zipfile.ZipFile(output) as z:
    package = {p: z.read(p) for p in z.namelist()}
package.update(parts)
with zipfile.ZipFile(output, 'w', zipfile.ZIP_DEFLATED) as z:
    for p, content in package.items():
        z.writestr(p, content)

# Editable Markdown recovery surface, derived from the same reviewed data.
md = ['# ' + data['title'], '', data['subtitle'], '', data['date'], '',
      '## 一、我们已经解决的问题', '']
for i in data['completed']:
    md += ['### ' + i['problem'], '', '完成时间：' + i['date'], '', i['result'], '']
md += ['## 二、我们还没有解决的问题', '', '### 现场待处理问题', '']
for i in data['current_pending']:
    md += ['#### ' + i['id'] + ' ' + i['title'], '', i['observed'], '',
           '当前状态：' + i['status'], '', '最新结论：' + i['conclusion'], '',
           '解决方案：' + i['solution'], '', '完成判据：' + i['acceptance'], '']
md += ['### 需核实关闭的历史事项', '',
       '现有记录未提供后续完成确认，不直接等同于现场仍存在故障。', '']
for i in data['historical_pending']:
    md += ['#### ' + i['id'] + ' ' + i['title'], '', i['date'], '', i['observed'], '',
           '解决及关闭方案：' + i['solution'], '']
(ROOT / 'report.md').write_text('\n'.join(md), encoding='utf-8')
print(output)
