from __future__ import annotations

import shutil
from pathlib import Path

from PIL import Image, ImageDraw, ImageFont
from docx import Document
from docx.enum.section import WD_SECTION
from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT, 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 Cm, Inches, Pt, RGBColor


ROOT = Path(__file__).resolve().parents[1]
OUT_IMG = ROOT / "redrawn-images"
OUT_DOC = ROOT / "deliverables" / "baokang_smart_canteen_quality_after_sales_training_redesigned.docx"
DOWNLOAD_DOC = Path("/Users/jack/Downloads/baokang_smart_canteen_quality_after_sales_training_redesigned.docx")

TEAL = (19, 112, 108)
TEAL_DARK = (11, 76, 74)
MINT = (223, 243, 238)
BLUE = (229, 239, 250)
AMBER = (251, 240, 220)
ZINC = (245, 247, 249)
LINE = (188, 201, 210)
TEXT = (35, 47, 58)
MUTED = (94, 107, 119)


def font(size: int, bold: bool = False) -> ImageFont.FreeTypeFont:
    candidates = [
        "/System/Library/Fonts/PingFang.ttc",
        "/System/Library/Fonts/STHeiti Medium.ttc",
        "/Library/Fonts/Arial Unicode.ttf",
        "/System/Library/Fonts/Supplemental/Arial Unicode.ttf",
    ]
    for path in candidates:
        if Path(path).exists():
            try:
                return ImageFont.truetype(path, size=size, index=0)
            except Exception:
                continue
    return ImageFont.load_default()


def wrap_text(draw: ImageDraw.ImageDraw, text: str, fnt: ImageFont.FreeTypeFont, max_width: int) -> list[str]:
    lines: list[str] = []
    current = ""
    for ch in text:
        candidate = current + ch
        if draw.textbbox((0, 0), candidate, font=fnt)[2] <= max_width:
            current = candidate
        else:
            if current:
                lines.append(current)
            current = ch
    if current:
        lines.append(current)
    return lines


def rounded(draw: ImageDraw.ImageDraw, xy, radius=24, fill="white", outline=LINE, width=2):
    draw.rounded_rectangle(xy, radius=radius, fill=fill, outline=outline, width=width)


def title(draw: ImageDraw.ImageDraw, text: str, subtitle: str | None = None):
    draw.text((80, 54), text, font=font(46, True), fill=TEAL_DARK)
    draw.rounded_rectangle((80, 116, 210, 124), radius=4, fill=TEAL)
    if subtitle:
        draw.text((80, 138), subtitle, font=font(22), fill=MUTED)


def save_canvas(name: str, draw_fn):
    OUT_IMG.mkdir(parents=True, exist_ok=True)
    img = Image.new("RGB", (1600, 900), ZINC)
    draw = ImageDraw.Draw(img)
    draw_fn(draw)
    path = OUT_IMG / name
    img.save(path, quality=96)
    return path


def draw_score_overview():
    def _draw(draw):
        title(draw, "评分要求响应总览", "围绕质量保障、售后服务、培训方案形成可评审、可落地、可追溯的响应结构")
        modules = [
            ("质量保障措施 8分", ["标准规范", "质量体系", "安全可靠", "保密措施"], BLUE),
            ("售后服务 4分", ["服务方式", "服务范围", "响应机制", "现场应急"], MINT),
            ("培训方案 4分", ["培训计划", "培训形式", "培训内容", "效果保障"], AMBER),
        ]
        x0, y0, w, h, gap = 90, 235, 440, 380, 55
        for i, (head, items, fill) in enumerate(modules):
            x = x0 + i * (w + gap)
            rounded(draw, (x, y0, x + w, y0 + h), 28, fill, LINE, 2)
            draw.text((x + 32, y0 + 32), head, font=font(34, True), fill=TEAL_DARK)
            for j, item in enumerate(items, 1):
                yy = y0 + 112 + (j - 1) * 58
                draw.ellipse((x + 34, yy + 2, x + 64, yy + 32), fill="white", outline=TEAL, width=2)
                draw.text((x + 44, yy + 2), str(j), font=font(19, True), fill=TEAL)
                draw.text((x + 82, yy - 2), item, font=font(28), fill=TEXT)
            if i < 2:
                ax = x + w + 10
                ay = y0 + h // 2
                draw.line((ax, ay, ax + gap - 20, ay), fill=TEAL, width=4)
                draw.polygon([(ax + gap - 20, ay), (ax + gap - 35, ay - 10), (ax + gap - 35, ay + 10)], fill=TEAL)
        draw.rounded_rectangle((310, 710, 1290, 785), radius=24, fill="white", outline=(222, 229, 234), width=2)
        draw.text((520, 730), "质量交付 · 长期运维 · 按岗培训", font=font(34, True), fill=TEAL_DARK)

    return save_canvas("01-score-response-overview.png", _draw)


def draw_quality_loop():
    def _draw(draw):
        title(draw, "质量保证闭环管理机制", "以节点检查、问题台账、采购人确认和资料归档保障建设成果")
        steps = [
            ("01", "项目启动\n深化设计"),
            ("02", "设备到货\n规范安装"),
            ("03", "系统部署\n联调测试"),
            ("04", "试运行\n问题整改"),
            ("05", "验收交付\n资料移交"),
            ("06", "服务期\n持续改进"),
        ]
        cx0, y, r, gap = 160, 390, 72, 225
        for i, (num, label) in enumerate(steps):
            cx = cx0 + i * gap
            draw.ellipse((cx - r, y - r, cx + r, y + r), fill="white", outline=TEAL, width=5)
            draw.text((cx - 22, y - 50), num, font=font(28, True), fill=TEAL)
            lines = label.split("\n")
            draw.text((cx - 52, y - 10), lines[0], font=font(25, True), fill=TEXT)
            draw.text((cx - 52, y + 25), lines[1], font=font(25, True), fill=TEXT)
            if i < len(steps) - 1:
                x1 = cx + r + 18
                x2 = cx + gap - r - 18
                draw.line((x1, y, x2, y), fill=TEAL, width=5)
                draw.polygon([(x2, y), (x2 - 16, y - 10), (x2 - 16, y + 10)], fill=TEAL)
        draw.rounded_rectangle((105, 655, 1495, 770), radius=24, fill="white", outline=(222, 229, 234), width=2)
        draw.text((155, 684), "关键机制：实施记录、测试台账、整改闭环、采购人确认、资料归档", font=font(34, True), fill=TEAL_DARK)

    return save_canvas("02-quality-assurance-loop.png", _draw)


def draw_after_sales_loop():
    def _draw(draw):
        title(draw, "售后服务响应闭环图", "专人负责、分级响应、远程与现场结合，形成服务记录和持续优化")
        center = (800, 440)
        draw.ellipse((660, 300, 940, 580), fill=TEAL, outline=TEAL_DARK, width=4)
        draw.text((710, 398), "专人负责", font=font(40, True), fill="white")
        nodes = [
            ("问题受理", 800, 185, BLUE),
            ("远程诊断", 1095, 365, MINT),
            ("现场服务", 1005, 660, AMBER),
            ("结果确认", 595, 660, BLUE),
            ("记录归档", 505, 365, MINT),
        ]
        for name, x, y, fill in nodes:
            rounded(draw, (x - 140, y - 58, x + 140, y + 58), 22, fill, TEAL, 2)
            draw.text((x - 62, y - 18), name, font=font(31, True), fill=TEXT)
            draw.line((center[0], center[1], x, y), fill=(176, 199, 206), width=3)
        callouts = [
            ("一级故障", "立即响应 / 优先恢复核心业务", 80, 255),
            ("二级故障", "快速响应 / 定位并恢复", 80, 575),
            ("三级与咨询", "及时处理 / 操作指导", 1190, 575),
        ]
        for head, body, x, y in callouts:
            rounded(draw, (x, y, x + 330, y + 120), 20, "white", LINE, 2)
            draw.text((x + 28, y + 22), head, font=font(28, True), fill=TEAL_DARK)
            draw.text((x + 28, y + 66), body, font=font(22), fill=MUTED)

    return save_canvas("03-after-sales-response-loop.png", _draw)


def draw_training_matrix():
    def _draw(draw):
        title(draw, "培训对象与培训内容矩阵", "按岗位拆分培训重点，确保会用、能管、可查、可持续运行")
        headers = ["培训对象", "核心目标", "重点培训内容", "培训方式", "交付成果"]
        rows = [
            ["学校管理员", "掌握总览与监管", "登录、数据查看、报表、预警", "集中培训", "管理操作确认"],
            ["食堂负责人", "管好日常业务", "晨检、留样、采购、库存、公示", "分岗实操", "业务流程会用"],
            ["后勤监管人员", "闭环处置风险", "视频、AI预警、巡查、设备状态", "现场演示", "监管记录留痕"],
            ["系统管理员", "维护基础配置", "账号、权限、设备台账、日志", "专项培训", "配置维护清单"],
            ["一线操作人员", "熟练操作设备", "晨检仪、留样柜、AI收货秤、手持机", "现场练习", "设备操作确认"],
        ]
        x0, y0 = 70, 220
        widths = [210, 245, 460, 220, 285]
        row_h, head_h = 94, 70
        x = x0
        for i, h in enumerate(headers):
            draw.rounded_rectangle((x, y0, x + widths[i], y0 + head_h), radius=12, fill=TEAL, outline=TEAL, width=2)
            draw.text((x + 25, y0 + 20), h, font=font(25, True), fill="white")
            x += widths[i]
        for r, row in enumerate(rows):
            y = y0 + head_h + r * row_h
            x = x0
            fill = "white" if r % 2 == 0 else (249, 251, 252)
            for c, text in enumerate(row):
                draw.rectangle((x, y, x + widths[c], y + row_h), fill=fill, outline=(218, 226, 232), width=2)
                fs = 23 if c != 2 else 21
                lines = wrap_text(draw, text, font(fs, c == 0), widths[c] - 38)
                yy = y + 20
                for line in lines[:2]:
                    draw.text((x + 19, yy), line, font=font(fs, c == 0), fill=TEXT)
                    yy += 29
                x += widths[c]
        draw.rounded_rectangle((330, 790, 1270, 850), radius=22, fill="white", outline=(222, 229, 234), width=2)
        draw.text((485, 804), "按岗培训 · 现场实操 · 资料留存 · 持续复训", font=font(28, True), fill=TEAL_DARK)

    return save_canvas("04-training-target-content-matrix.png", _draw)


def set_cell_shading(cell, fill: str):
    tc_pr = cell._tc.get_or_add_tcPr()
    shd = OxmlElement("w:shd")
    shd.set(qn("w:fill"), fill)
    tc_pr.append(shd)


def set_cell_text(cell, text: str, bold=False, color=TEXT, size=9.5):
    cell.text = ""
    p = cell.paragraphs[0]
    run = p.add_run(text)
    run.bold = bold
    run.font.name = "Arial Unicode MS"
    run._element.rPr.rFonts.set(qn("w:eastAsia"), "PingFang SC")
    run.font.size = Pt(size)
    run.font.color.rgb = RGBColor(*color)
    cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER


def add_table(doc, headers, rows, widths=None):
    table = doc.add_table(rows=1, cols=len(headers))
    table.alignment = WD_TABLE_ALIGNMENT.CENTER
    table.style = "Table Grid"
    for i, h in enumerate(headers):
        set_cell_text(table.rows[0].cells[i], h, bold=True, color=(255, 255, 255), size=9.5)
        set_cell_shading(table.rows[0].cells[i], "13706C")
    for r, row in enumerate(rows):
        cells = table.add_row().cells
        for i, val in enumerate(row):
            set_cell_text(cells[i], val, size=9)
            if r % 2 == 0:
                set_cell_shading(cells[i], "F8FAFC")
    if widths:
        for row in table.rows:
            for i, width in enumerate(widths):
                row.cells[i].width = Cm(width)
    doc.add_paragraph()
    return table


def add_para(doc, text, style=None, bold_prefix: str | None = None):
    p = doc.add_paragraph(style=style)
    p.paragraph_format.first_line_indent = Cm(0.74) if style is None else None
    p.paragraph_format.line_spacing = 1.25
    if bold_prefix and text.startswith(bold_prefix):
        r = p.add_run(bold_prefix)
        r.bold = True
        p.add_run(text[len(bold_prefix):])
    else:
        p.add_run(text)
    return p


def add_heading(doc, text, level=1):
    p = doc.add_heading(text, level=level)
    for run in p.runs:
        run.font.name = "Arial Unicode MS"
        run._element.rPr.rFonts.set(qn("w:eastAsia"), "PingFang SC")
        run.font.color.rgb = RGBColor(*(TEAL_DARK if level == 1 else TEXT))
    return p


def add_picture(doc, path: Path, caption: str):
    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    p.add_run().add_picture(str(path), width=Inches(6.45))
    cp = doc.add_paragraph(caption)
    cp.alignment = WD_ALIGN_PARAGRAPH.CENTER
    cp.runs[0].font.size = Pt(9)
    cp.runs[0].font.color.rgb = RGBColor(90, 100, 110)


def apply_doc_style(doc: Document):
    section = doc.sections[0]
    section.page_width = Cm(21)
    section.page_height = Cm(29.7)
    section.top_margin = Cm(2.0)
    section.bottom_margin = Cm(1.8)
    section.left_margin = Cm(2.0)
    section.right_margin = Cm(2.0)
    styles = doc.styles
    styles["Normal"].font.name = "Arial Unicode MS"
    styles["Normal"]._element.rPr.rFonts.set(qn("w:eastAsia"), "PingFang SC")
    styles["Normal"].font.size = Pt(10.5)
    for name, size in [("Heading 1", 16), ("Heading 2", 13), ("Heading 3", 11.5)]:
        styles[name].font.name = "Arial Unicode MS"
        styles[name]._element.rPr.rFonts.set(qn("w:eastAsia"), "PingFang SC")
        styles[name].font.size = Pt(size)
        styles[name].font.bold = True


def add_cover(doc: Document):
    for _ in range(4):
        doc.add_paragraph()
    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    run = p.add_run("科尔沁左翼中旗保康镇中学\n智慧食堂建设项目")
    run.bold = True
    run.font.size = Pt(23)
    run.font.color.rgb = RGBColor(*TEAL_DARK)
    run.font.name = "Arial Unicode MS"
    run._element.rPr.rFonts.set(qn("w:eastAsia"), "PingFang SC")

    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    run = p.add_run("质量保障措施、售后服务及培训方案")
    run.bold = True
    run.font.size = Pt(18)
    run.font.color.rgb = RGBColor(*TEXT)

    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    run = p.add_run("投标文件技术部分专用章节")
    run.font.size = Pt(12)
    run.font.color.rgb = RGBColor(*MUTED)

    doc.add_paragraph()
    add_table(
        doc,
        ["项目", "内容"],
        [
            ["项目编号", "NMGXF-2026-5-1"],
            ["采购单位", "科尔沁左翼中旗保康镇中学"],
            ["服务期", "3年"],
            ["质量要求", "符合国家及采购人验收合格标准"],
        ],
        [4.2, 10.8],
    )
    doc.add_paragraph("\n投标人（盖章）：____________________________")
    doc.add_paragraph("日期：________年____月____日")
    doc.add_page_break()


def build_doc(image_paths: dict[str, Path]):
    doc = Document()
    apply_doc_style(doc)
    add_cover(doc)

    add_heading(doc, "一、方案定位与评分响应", 1)
    add_para(
        doc,
        "本章节依据科尔沁左翼中旗保康镇中学智慧食堂建设项目招标文件中技术部分评分要求编制，集中响应“质量保障措施（8分）”“售后服务（4分）”“培训方案（4分）”三项评审内容。方案围绕智慧膳食营养健康管理、智慧餐厅管理、餐厅公示、智慧采购、明厨亮灶视频AI、晨检、留样、温湿度监测、AI智能收货秤、库管手持机等建设内容展开，确保内容与学校智慧食堂实际使用场景相结合。",
    )
    add_picture(doc, image_paths["score"], "图1 评分要求响应总览图")
    add_table(
        doc,
        ["评分项", "分值", "招标关注点", "本方案响应位置"],
        [
            ["质量保障措施", "8分", "项目实施标准规范、质量保证体系、安全可靠质量保证措施、保密措施", "第二章"],
            ["售后服务", "4分", "售后服务方式、售后服务范围、响应机制、现场服务支持及应急处理", "第三章"],
            ["培训方案", "4分", "培训方案、培训计划安排、培训形式、培训内容", "第四章"],
        ],
        [3, 2, 7, 3.5],
    )
    add_para(doc, "编制原则：投标表达采用“承诺+措施+流程+表单”的结构，便于评审理解，也便于中标后落地执行；每项措施均围绕学校食堂实际场景展开，避免泛泛而谈；质量、售后、培训三项内容前后衔接，形成建设交付、长期运行和用户会用能用的完整闭环。")

    add_heading(doc, "二、质量保障措施", 1)
    add_heading(doc, "2.1 质量保障总体原则", 2)
    add_para(doc, "我公司充分理解本项目建设内容涉及智慧膳食营养健康管理系统、智慧营养管理平台移动端、智慧餐厅管理系统、智慧餐厅管理系统移动端、餐厅公示系统、智慧采购管理系统，以及行为分析算法终端、摄像机、硬盘录像机、无线温湿度监测设备、晨检仪、智能留样柜、膳食营养分析指导一体机、AI智能收货秤、库管手持机等软硬件系统。项目建成后将直接服务学校食品安全监管、采购验收、库存管理、明厨亮灶、人员晨检、食品留样、营养健康管理及信息公示等业务。")
    add_para(doc, "为确保项目正常稳定实施并保证产品稳定运行，我公司将按照“标准先行、过程受控、分级检查、闭环整改、资料完整、持续改进”的原则建立质量保障体系。实施过程中严格以招标文件、投标文件、合同约定、产品技术参数、深化实施方案及采购人验收标准为依据，确保建设成果满足学校实际使用需求并达到验收合格标准。")
    add_picture(doc, image_paths["quality"], "图2 质量保证闭环管理机制")

    add_heading(doc, "2.2 项目实施标准规范", 2)
    for head, body in [
        ("项目管理标准规范", "项目实施按照“启动准备—现场勘查—深化设计—到货验收—安装部署—系统调试—联调测试—试运行—培训交付—验收移交—售后运维”的流程进行。项目启动阶段明确项目负责人、技术负责人、实施人员、培训人员、售后人员以及采购人对接人，建立沟通机制、问题反馈机制和节点确认机制。实施计划、设备清单、到货验收单、安装记录、调试记录、试运行记录、问题整改记录、培训签到表、培训资料、验收资料等文件统一纳入项目档案管理。"),
        ("设备安装与布线标准规范", "针对摄像机、PoE交换机、硬盘录像机、行为分析算法终端、晨检仪、智能留样柜、温湿度监测设备、AI智能收货秤、库管手持机、膳食营养分析指导一体机等设备，我公司将结合学校食堂现场环境进行合理点位设计和规范化安装。摄像机重点覆盖加工区、操作区、仓储区、留样区、收货区等关键区域，确保画面清晰、角度合理、无遮挡、无明显逆光；网络设备安装位置满足通风、防潮、防尘、防误碰要求；线缆敷设做到横平竖直、标识清晰、固定牢靠、强弱电分离。"),
        ("软件部署与系统配置标准规范", "软件系统部署按照“基础数据先行、权限配置同步、业务流程贯通、设备数据联动”的原则执行。上线前完成学校组织架构、食堂基础信息、人员信息、供应商信息、菜品信息、商品信息、角色权限、设备台账、公示内容、预警规则等基础数据配置，确保各应用模块能够满足学校实际业务使用要求。"),
        ("调试测试标准规范", "系统调试阶段对软件功能、硬件设备、网络链路、数据上传、视频接入、AI识别、预警推送、二维码公示、移动端访问、报表导出等内容进行逐项测试。测试发现的问题形成问题清单，明确责任人、整改措施、完成时间和复核结果，整改完成后方可进入试运行和验收阶段。"),
    ]:
        add_heading(doc, head, 3)
        add_para(doc, body)
    add_table(
        doc,
        ["测试类别", "测试内容", "质量目标"],
        [
            ["软件功能测试", "营养管理、食安监管、采购库存、公示展示、移动监管等功能", "功能完整、流程顺畅、数据准确"],
            ["硬件联调测试", "摄像机、NVR、算法终端、晨检仪、留样柜、温湿度设备、AI收货秤等", "设备在线稳定、数据正常上传"],
            ["网络连通测试", "校内网络、设备网络、移动端访问、公示二维码访问", "访问稳定、权限受控"],
            ["AI识别测试", "未戴口罩、未戴工帽、抽烟、鼠患、垃圾桶异常等场景", "告警及时、证据可追溯"],
            ["数据一致性测试", "采购、库存、晨检、留样、营养分析、公示数据", "前后台数据一致、报表准确"],
            ["安全测试", "账号权限、数据导出、日志记录、访问控制", "权限清晰、操作留痕"],
        ],
        [3, 8, 4],
    )

    add_heading(doc, "2.3 质量保证体系及实际操作机制", 2)
    add_para(doc, "我公司建立覆盖组织保障、过程保障、技术保障、检查保障和交付保障的质量保证体系，并通过岗位分工、阶段检查、问题台账和验收资料归档实现可操作、可追溯、可复核。")
    add_table(
        doc,
        ["岗位", "主要职责"],
        [
            ["项目负责人", "负责项目总体协调、进度控制、质量把关、采购人沟通和验收组织。"],
            ["技术负责人", "负责技术方案落地、系统架构配置、联调测试和技术问题处理。"],
            ["软件实施人员", "负责应用系统部署、基础数据配置、权限配置和功能测试。"],
            ["硬件安装人员", "负责摄像机、交换机、NVR、算法终端、晨检仪、留样柜等设备安装。"],
            ["网络调试人员", "负责网络规划、设备联网、访问控制、视频链路和数据链路调试。"],
            ["培训人员", "负责管理员、食堂人员、后勤监管人员等用户培训。"],
            ["售后人员", "负责服务期内巡检、故障处理、远程支持和现场服务。"],
        ],
        [3.2, 11.8],
    )
    for item in [
        "第一阶段：项目启动与深化设计。核实学校食堂现场环境、网络条件、设备安装点位、供电条件、系统使用对象和业务流程，形成符合现场实际的实施计划。",
        "第二阶段：设备到货与安装。核对设备型号、数量、外观、配件、合格证明和技术参数，确保到货设备与投标响应内容一致；安装过程中严格执行施工规范。",
        "第三阶段：系统部署与联调。完成软件系统部署、基础数据导入、权限配置、视频接入、智能设备接入、预警规则设置和业务流程测试。",
        "第四阶段：试运行与整改。安排专人跟踪系统运行情况，收集学校管理人员、食堂人员和后勤监管人员的使用反馈，对发现的问题及时整改。",
        "第五阶段：验收交付。按照合同、采购文件、投标文件和采购人要求准备验收资料，配合采购人逐项检查系统功能、设备运行、数据联通、培训情况和资料完整性。",
    ]:
        add_para(doc, item)
    add_para(doc, "三级质量检查机制：一级检查为实施人员自检，二级检查为项目负责人复检，三级检查为采购人确认。阶段性成果完成后邀请采购人或使用部门确认，对提出意见进行整改并形成记录。")

    add_heading(doc, "2.4 安全可靠的质量保证措施", 2)
    for item in [
        "产品质量保证措施：所供软件系统、硬件设备及相关配套产品均满足招标文件技术要求和国家相关标准要求。设备到货后配合采购人开展开箱检查，对不符合要求或运输过程中发生损坏的设备及时更换。",
        "系统稳定性保证措施：上线前开展完整联调测试和稳定性验证，重点检查视频接入稳定性、AI告警生成及时性、晨检数据上传准确性、留样数据保存完整性、温湿度数据上报连续性、采购验收取证有效性、移动端访问流畅性和公示数据展示一致性。",
        "数据准确性保证措施：通过基础数据校验、业务流程校验、设备数据校验和报表数据校验保障数据准确。晨检、留样、温湿度、采购验收、库存出入库等业务数据与设备采集数据进行一致性核验。",
        "问题整改闭环措施：建立问题台账，记录问题来源、问题描述、影响范围、责任人、整改措施、计划完成时间和复核结果。对影响系统运行或验收的问题优先处理，对重复出现的问题进行原因分析并制定预防措施。",
    ]:
        add_para(doc, item)

    add_heading(doc, "2.5 保密措施", 2)
    add_para(doc, "本项目涉及学校师生信息、食堂从业人员信息、健康信息、晨检记录、留样记录、采购数据、库存数据、供应商资料、视频图像、AI告警截图、系统账号及网络配置信息。我公司将严格执行采购人保密管理要求，落实人员保密、数据保密、账号权限保密和运维保密措施。")
    add_table(
        doc,
        ["保密类别", "控制措施"],
        [
            ["人员保密", "项目实施前对实施、技术、售后人员开展保密教育，明确不得擅自复制、传播、外泄采购人业务数据、系统账号、网络配置、视频资料和人员信息。"],
            ["数据保密", "未经采购人书面同意，不向无关第三方提供人员信息、健康数据、视频图像、采购库存数据、供应商资料和系统配置文件。"],
            ["账号权限保密", "系统管理员账号、数据库账号、设备管理账号、远程运维账号统一管理，交付前清理测试账号、临时账号和无关权限。"],
            ["运维保密", "远程或现场运维须经采购人同意后开展，保留操作记录，不擅自拍摄、复制或带离涉密资料。"],
        ],
        [3, 12],
    )

    add_heading(doc, "三、售后服务方案", 1)
    add_heading(doc, "3.1 售后服务总体承诺", 2)
    add_para(doc, "我公司承诺，在本项目3年服务期内，为科尔沁左翼中旗保康镇中学智慧食堂建设项目提供持续、稳定、可靠的售后服务。服务覆盖本项目所建设的软件系统、硬件设备、网络联调、数据配置、用户使用、系统优化、故障处理、巡检维护和应急保障等内容。")
    add_para(doc, "我公司坚持“快速响应、及时处理、服务闭环、持续优化”的服务原则，建立专人负责、热线支持、远程协助、现场服务、定期巡检和应急处置相结合的服务机制，确保学校智慧食堂系统长期稳定运行。")
    add_picture(doc, image_paths["after_sales"], "图3 售后服务响应闭环图")
    add_heading(doc, "3.2 售后服务方式", 2)
    for item in [
        "电话及在线服务：采购人可通过电话、工作群、在线沟通等方式反馈问题。我公司售后人员第一时间了解故障现象、影响范围、使用场景和紧急程度，并给出初步处理意见。",
        "远程技术支持：对于软件配置、系统参数、设备在线状态、数据同步、接口联通、报表异常等可远程处理的问题，在采购人授权后通过安全方式开展远程协助。",
        "现场服务支持：对于远程无法解决或涉及硬件设备、网络链路、现场安装、视频接入、设备更换、系统联调等问题，安排技术人员到现场处理。",
        "定期巡检服务：服务期内开展系统和设备巡检，重点检查软件运行状态、服务器资源、数据库状态、设备在线率、录像状态、硬盘容量、晨检仪、留样柜、温湿度设备、AI收货秤等运行情况。",
    ]:
        add_para(doc, item)
    add_heading(doc, "3.3 售后服务范围", 2)
    add_table(
        doc,
        ["服务类别", "服务范围"],
        [
            ["软件系统服务", "智慧膳食营养健康管理系统、智慧营养管理平台移动端、智慧餐厅管理系统、移动端、公示系统、智慧采购管理系统的使用支持、配置调整和故障排查。"],
            ["硬件设备服务", "行为分析算法终端、摄像机、NVR、硬盘、PoE交换机、无线温湿度设备、晨检仪、智能留样柜、膳食营养分析指导一体机、AI智能收货秤、库管手持机等设备维护。"],
            ["数据服务", "基础数据维护、人员数据导入、菜品数据维护、供应商数据维护、采购库存数据核查、报表统计问题处理。"],
            ["网络服务", "设备联网、视频链路、数据上传链路、移动端访问、公示二维码访问等相关问题排查。"],
            ["业务支持", "食品安全监管、明厨亮灶、晨检、留样、采购验收、库存盘点、营养分析、公示展示等业务流程使用指导。"],
            ["安全服务", "账号权限检查、操作日志查询、数据备份建议、远程运维安全管理。"],
            ["巡检服务", "系统运行巡检、设备状态巡检、预警事件巡检、存储容量巡检、故障隐患排查。"],
        ],
        [3, 12],
    )
    add_heading(doc, "3.4 售后服务响应机制", 2)
    add_para(doc, "我公司将建立明确的售后服务响应机制，对不同等级问题实行分级响应、分类处理、闭环归档。每次服务完成后记录问题内容、处理过程、处理结果和采购人确认意见，形成可追溯的售后服务记录。")
    add_table(
        doc,
        ["故障等级", "故障描述", "响应要求", "处理要求"],
        [
            ["一级故障", "系统整体无法访问、核心业务中断、大面积设备离线、食品安全监管关键功能不可用", "立即响应", "优先处理，远程或现场快速恢复业务。"],
            ["二级故障", "部分模块异常、部分设备离线、视频或数据上传异常、影响部分业务使用", "快速响应", "及时定位原因，尽快恢复功能。"],
            ["三级故障", "单个账号、单项配置、报表查询、操作咨询、非紧急功能问题", "及时响应", "通过电话、远程或计划性现场服务处理。"],
            ["咨询类问题", "系统操作、数据维护、流程使用、培训答疑", "工作时间内响应", "提供操作指导和资料说明。"],
        ],
        [2.5, 6.2, 2.5, 3.8],
    )
    add_heading(doc, "3.5 现场服务支持与应急处理措施", 2)
    for item in [
        "现场服务支持：当出现远程无法解决的问题时，我公司安排具备相应经验的技术人员到达现场处理。现场服务人员携带必要工具、检测设备和常用备品备件，对设备供电、网络连接、视频链路、设备状态、系统配置和数据传输进行排查。处理完成后，与采购人共同确认系统或设备恢复情况，并填写现场服务记录。",
        "应急处理措施：针对系统中断、设备故障、数据异常、网络故障、视频无法存储、留样柜异常、晨检仪异常、AI收货秤无法取证等情况，售后人员首先判断影响范围，优先恢复核心业务运行，再进行原因分析和整改优化。",
        "重大问题升级机制：对于短时间内无法解决的重大问题，由项目负责人牵头组织技术负责人、厂家技术支持和现场工程师共同处理，必要时提供替代设备、临时恢复方案或数据补录方案，最大限度降低对学校食堂日常运行的影响。",
        "服务期运行保障：在3年服务期内持续提供系统巡检、设备巡检、故障处理、版本优化、数据维护指导、人员再培训和重大活动保障。遇到学校迎检、专项检查、食品安全检查、系统验收复查等重要场景，根据采购人需求提供必要技术支持。",
    ]:
        add_para(doc, item)

    add_heading(doc, "四、培训方案", 1)
    add_heading(doc, "4.1 培训总体方案", 2)
    add_para(doc, "为确保智慧食堂系统建成后能够真正用起来、管起来、发挥作用，我公司将在系统部署和试运行阶段组织系统化培训。培训坚持“按岗培训、注重实操、现场演示、反复答疑、资料留存”的原则，针对学校管理人员、食堂负责人、后勤监管人员、食堂从业人员和系统管理员设置不同培训内容。")
    add_para(doc, "培训目标是使管理人员掌握系统整体功能、数据查看、监管分析、报表导出和预警处置方法；使食堂负责人掌握晨检、留样、采购验收、库存管理、供应商管理、菜品管理和公示信息维护方法；使后勤监管人员掌握明厨亮灶视频查看、AI预警处置、设备状态查看、食品安全监管和移动端巡查方法；使系统管理员掌握基础数据、角色权限、设备台账、账号、日志和基础运维方法；使一线操作人员掌握晨检仪、智能留样柜、AI智能收货秤、库管手持机等现场设备的日常操作方法。")
    add_picture(doc, image_paths["training"], "图4 培训对象与培训内容矩阵")
    add_heading(doc, "4.2 培训计划安排", 2)
    add_table(
        doc,
        ["培训阶段", "培训时间", "培训对象", "培训重点", "培训目标"],
        [
            ["上线前培训", "系统部署完成后", "学校管理员、食堂负责人、后勤人员", "系统总体功能、账号登录、基础操作、业务流程", "了解系统基本功能和使用入口"],
            ["试运行培训", "试运行期间", "食堂人员、监管人员、库管人员", "晨检、留样、采购验收、库存、视频监管、预警处理", "能够独立完成日常业务操作"],
            ["验收前强化培训", "验收前", "项目相关管理人员", "报表查询、数据导出、验收资料查看、问题反馈", "支撑项目验收和常态化使用"],
            ["售后持续培训", "服务期内按需开展", "新增人员、轮岗人员、重点岗位人员", "功能复训、新功能说明、常见问题处理", "保证人员变动后系统持续使用"],
        ],
        [2.4, 2.4, 3.2, 4.4, 3.0],
    )
    add_heading(doc, "4.3 培训形式", 2)
    for item in [
        "集中培训：面向学校管理人员、食堂负责人、后勤监管人员和系统管理员，讲解项目建设内容、系统总体架构、各应用模块功能、业务流程和管理要求。",
        "分岗位培训：根据岗位职责进行针对性培训。管理员重点学习组织架构、账号权限、设备配置和数据维护；食堂负责人重点学习晨检、留样、采购、库存和公示；监管人员重点学习视频查看、AI预警、移动巡查和报表统计；库管人员重点学习AI智能收货秤和库管手持机操作。",
        "现场实操培训：针对晨检仪、智能留样柜、AI智能收货秤、库管手持机、膳食营养分析指导一体机等设备，采用现场演示和实操练习方式开展培训。",
        "远程培训与答疑：在服务期内，如学校新增人员或用户在使用过程中遇到问题，通过电话、远程会议、操作截图、视频演示等方式进行补充培训和答疑。",
        "资料化培训：培训结束后提供系统操作手册、常见问题说明、设备操作指引、管理员维护说明等资料，便于后续查阅和复训。",
    ]:
        add_para(doc, item)
    add_heading(doc, "4.4 培训内容", 2)
    add_table(
        doc,
        ["培训模块", "主要培训内容", "预期效果"],
        [
            ["系统管理员培训", "系统登录、账号管理、角色权限、组织架构、食堂基础信息、设备台账、系统参数、操作日志、基础数据导入导出、数据备份建议和安全注意事项。", "能够独立完成基础配置、权限维护和常见问题排查。"],
            ["智慧膳食营养健康管理系统", "人员信息、健康专项信息、菜品营养统计、菜谱管理、菜品管理、食物成分查询、个体/团体膳食监控、营养报告查看、报告打印和数据导出。", "能够开展膳食营养分析，为学校营养健康管理提供数据支撑。"],
            ["智慧餐厅管理系统", "全域态势、预警中心、互联网+明厨亮灶、AI违规识别、视频查看、晨检、留样、设备巡检、资质证照、预警处置和报表查看。", "能够实现食品安全风险发现、告警处置、结果确认和资料留痕。"],
            ["移动端系统", "移动端登录、首页总览、视频直播、预警中心、预警事件处理、移动巡查、信息查询和常见操作问题处理。", "能够通过移动端开展远程监管和快速处置。"],
            ["餐厅公示系统", "食堂信息维护、许可证展示、健康证公示、晨检记录、检测报告、供应商信息、留样记录、预警概览、公示二维码和意见反馈。", "能够规范开展食堂信息公开，提升管理透明度。"],
            ["智慧采购管理系统", "商品管理、供应商管理、采购申请、采购下单、采购审核、验收入库、退货、库存、盘点、调拨、报表和智能设备取证。", "能够规范完成采购验收、库存管理和取证留痕。"],
            ["智能硬件设备", "摄像机和NVR基础查看、算法终端告警、温湿度设备、晨检仪、智能留样柜、膳食营养分析一体机、AI收货秤、库管手持机。", "一线人员可熟练使用现场设备，减少操作失误和业务中断。"],
        ],
        [4, 7.5, 4],
    )
    add_heading(doc, "4.5 培训效果保障", 2)
    for item in [
        "培训前：根据岗位职责确定培训对象和培训重点，避免培训内容泛化；提前准备培训课件、操作手册、账号环境和现场演示设备。",
        "培训中：结合学校食堂真实业务流程进行演示，采用讲解、演示、实操、答疑相结合的方式，确保参训人员能够理解关键业务流程。",
        "培训后：安排现场答疑和实操确认，对关键岗位人员进行常用操作验证；培训签到表、培训记录、问题答疑记录和培训资料纳入项目交付资料。",
        "服务期内：对新增人员、岗位调整人员和重点业务人员提供补充培训，保障系统长期稳定应用。",
    ]:
        add_para(doc, item)
    add_para(doc, "通过上述培训安排，采购人相关人员能够熟练使用本项目建设的各类系统和设备，实现智慧食堂系统“会用、能管、可查、可追溯、可持续运行”的建设目标。")

    add_heading(doc, "五、附表模板", 1)
    add_para(doc, "为便于项目实施、售后服务和培训过程留痕，我公司拟采用以下记录模板，具体格式可根据采购人管理要求进一步调整。")
    templates = [
        ("5.1 问题整改台账模板", ["序号", "问题来源", "问题描述", "责任人", "整改措施", "完成时间", "复核结果"]),
        ("5.2 售后服务记录模板", ["服务日期", "问题类型", "问题描述", "处理方式", "处理结果", "采购人确认"]),
        ("5.3 培训签到及反馈模板", ["培训日期", "培训主题", "参训人员", "岗位", "签到", "反馈意见"]),
        ("5.4 系统巡检记录模板", ["巡检日期", "巡检对象", "巡检项目", "运行状态", "发现问题", "处理建议"]),
    ]
    for heading, headers in templates:
        add_heading(doc, heading, 2)
        add_table(doc, headers, [["" for _ in headers] for _ in range(4)])

    OUT_DOC.parent.mkdir(parents=True, exist_ok=True)
    doc.save(OUT_DOC)
    shutil.copy2(OUT_DOC, DOWNLOAD_DOC)
    return OUT_DOC


def main():
    gpt_dir = ROOT / "gpt-image-2-reference"
    paths = {
        "score": gpt_dir / "reference-01.png" if (gpt_dir / "reference-01.png").exists() else draw_score_overview(),
        "quality": gpt_dir / "reference-02.png" if (gpt_dir / "reference-02.png").exists() else draw_quality_loop(),
        "after_sales": gpt_dir / "reference-03.png" if (gpt_dir / "reference-03.png").exists() else draw_after_sales_loop(),
        "training": gpt_dir / "reference-04.png" if (gpt_dir / "reference-04.png").exists() else draw_training_matrix(),
    }
    doc = build_doc(paths)
    print(doc)
    print(DOWNLOAD_DOC)


if __name__ == "__main__":
    main()
