#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Generate formal meeting minutes PDF for Yugong Biotech audit remediation.
"""

from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.lib.styles import ParagraphStyle
from reportlab.platypus import (
    SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
    HRFlowable, KeepTogether
)
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
import os

# ── 字体注册 ──────────────────────────────────────────────────────────────────
pdfmetrics.registerFont(TTFont('STHeiti', '/System/Library/Fonts/STHeiti Medium.ttc', subfontIndex=0))
pdfmetrics.registerFont(TTFont('STHeitiLight', '/System/Library/Fonts/STHeiti Light.ttc', subfontIndex=0))

# ── 颜色定义 ──────────────────────────────────────────────────────────────────
DARK_BLUE   = colors.HexColor('#1B3A6B')
MID_BLUE    = colors.HexColor('#2E6DA4')
LIGHT_BLUE  = colors.HexColor('#D6E4F0')
RED_CRIT    = colors.HexColor('#C0392B')
ORANGE_MAJ  = colors.HexColor('#E67E22')
YELLOW_MIN  = colors.HexColor('#F1C40F')
BG_CRIT     = colors.HexColor('#FADBD8')
BG_MAJ      = colors.HexColor('#FDEBD0')
BG_MIN      = colors.HexColor('#FEF9E7')
BG_HEAD     = colors.HexColor('#EBF5FB')
GRAY_LINE   = colors.HexColor('#BDC3C7')
WHITE       = colors.white
DARK_GRAY   = colors.HexColor('#2C3E50')
LIGHT_GRAY  = colors.HexColor('#F2F3F4')

# ── 段落样式 ──────────────────────────────────────────────────────────────────
def make_styles():
    base = {'fontName': 'STHeiti', 'leading': 16}
    return {
        'title': ParagraphStyle('title',
            fontName='STHeiti', fontSize=16, leading=22,
            alignment=TA_CENTER, textColor=WHITE, spaceAfter=4),
        'subtitle': ParagraphStyle('subtitle',
            fontName='STHeitiLight', fontSize=8.5, leading=13,
            alignment=TA_CENTER, textColor=colors.HexColor('#D6EAF8'), spaceAfter=2),
        'section': ParagraphStyle('section',
            fontName='STHeiti', fontSize=12, leading=18,
            textColor=WHITE, spaceBefore=6, spaceAfter=4),
        'subsection': ParagraphStyle('subsection',
            fontName='STHeiti', fontSize=10.5, leading=16,
            textColor=DARK_BLUE, spaceBefore=8, spaceAfter=4),
        'body': ParagraphStyle('body',
            fontName='STHeitiLight', fontSize=9.5, leading=15,
            textColor=DARK_GRAY, spaceAfter=3, alignment=TA_LEFT),
        'body_bold': ParagraphStyle('body_bold',
            fontName='STHeiti', fontSize=9.5, leading=15,
            textColor=DARK_GRAY, spaceAfter=3),
        'cell': ParagraphStyle('cell',
            fontName='STHeitiLight', fontSize=8.5, leading=13,
            textColor=DARK_GRAY),
        'cell_bold': ParagraphStyle('cell_bold',
            fontName='STHeiti', fontSize=8.5, leading=13,
            textColor=DARK_GRAY),
        'cell_crit': ParagraphStyle('cell_crit',
            fontName='STHeiti', fontSize=8.5, leading=13,
            textColor=RED_CRIT),
        'cell_maj': ParagraphStyle('cell_maj',
            fontName='STHeiti', fontSize=8.5, leading=13,
            textColor=ORANGE_MAJ),
        'cell_min': ParagraphStyle('cell_min',
            fontName='STHeiti', fontSize=8.5, leading=13,
            textColor=colors.HexColor('#9B7D11')),
        'footer': ParagraphStyle('footer',
            fontName='STHeitiLight', fontSize=7.5, leading=11,
            textColor=colors.HexColor('#7F8C8D'), alignment=TA_CENTER),
        'cell_header': ParagraphStyle('cell_header',
            fontName='STHeiti', fontSize=8.5, leading=13,
            textColor=WHITE),
        'reg_title': ParagraphStyle('reg_title',
            fontName='STHeiti', fontSize=9.5, leading=14,
            textColor=MID_BLUE, spaceAfter=2),
        'bullet': ParagraphStyle('bullet',
            fontName='STHeitiLight', fontSize=9, leading=14,
            textColor=DARK_GRAY, leftIndent=12, spaceAfter=2),
    }

S = make_styles()

# ── 辅助：带背景色的节标题 ────────────────────────────────────────────────────
def section_header(text, color=MID_BLUE, text_style='section'):
    data = [[Paragraph(text, S[text_style])]]
    t = Table(data, colWidths=[17.5*cm])
    t.setStyle(TableStyle([
        ('BACKGROUND', (0,0), (-1,-1), color),
        ('TOPPADDING',    (0,0), (-1,-1), 5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 5),
        ('LEFTPADDING',   (0,0), (-1,-1), 10),
        ('RIGHTPADDING',  (0,0), (-1,-1), 10),
        ('ROWBACKGROUNDS', (0,0), (-1,-1), [color]),
    ]))
    return t

def info_table(rows):
    """Two-column key-value table for meeting info."""
    data = [[Paragraph(k, S['cell_bold']), Paragraph(v, S['cell'])]
            for k, v in rows]
    t = Table(data, colWidths=[3.8*cm, 13.7*cm])
    t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (0,-1), BG_HEAD),
        ('BACKGROUND',    (1,0), (1,-1), WHITE),
        ('GRID',          (0,0), (-1,-1), 0.4, GRAY_LINE),
        ('TOPPADDING',    (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING',   (0,0), (-1,-1), 6),
        ('RIGHTPADDING',  (0,0), (-1,-1), 6),
        ('VALIGN',        (0,0), (-1,-1), 'TOP'),
    ]))
    return t

# ── 主文档构建 ────────────────────────────────────────────────────────────────
def build_pdf(output_path):
    doc = SimpleDocTemplate(
        output_path,
        pagesize=A4,
        leftMargin=2.0*cm, rightMargin=2.0*cm,
        topMargin=2.2*cm, bottomMargin=2.2*cm,
        title='愚公生物（百时美）供应商审计整改会议纪要',
        author='愚公生物科技有限公司 质量管理部',
    )

    story = []
    W = 17.5 * cm  # 可用宽度

    # ═══════════════════════════════════════════════════════════════════════════
    # 封面标题区
    # ═══════════════════════════════════════════════════════════════════════════
    cover_data = [
        [Paragraph('愚公生物科技有限公司（百时美）', S['subtitle'])],
        [Paragraph('供应商审计问题复盘与质量整改专项会议纪要', S['title'])],
        [Paragraph('SUPPLIER AUDIT REMEDIATION — INTERNAL MEETING MINUTES', S['subtitle'])],
        [Paragraph('文件编号：YG-QA-MOM-2026-001　　版本：V1.0　　密级：内部', S['subtitle'])],
    ]
    cover_t = Table(cover_data, colWidths=[W])
    cover_t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (-1,-1), DARK_BLUE),
        ('TOPPADDING',    (0,0), (-1,-1), 12),
        ('BOTTOMPADDING', (0,0), (-1,-1), 12),
        ('LEFTPADDING',   (0,0), (-1,-1), 14),
        ('RIGHTPADDING',  (0,0), (-1,-1), 14),
    ]))
    story.append(cover_t)
    story.append(Spacer(1, 0.4*cm))

    # ═══════════════════════════════════════════════════════════════════════════
    # 一、会议基本信息
    # ═══════════════════════════════════════════════════════════════════════════
    story.append(section_header('一、会议基本信息'))
    story.append(Spacer(1, 0.2*cm))
    story.append(info_table([
        ('会议主题', '申基供应商审计问题复盘、质量体系整改及 4000 万订单配合工作'),
        ('会议日期', '2026 年 4 月 10 日'),
        ('会议地点', '愚公生物科技有限公司 会议室'),
        ('主持人',   '任宏杰（说话人 A）'),
        ('参会人员', '任宏杰（A，主持）、二袁（B）、说话人 C、毕海（D）、说话人 E'),
        ('记录人',   '质量管理部'),
        ('核心背景', '申基承接制药企业 CDMO 业务，委托愚公生物生产关键工具酶原料。\n'
                    '申基依其客户要求对愚公生物开展现场供应商审计，发现若干缺陷。\n'
                    '本次会议为愚公生物内部复盘，制定整改方案与行动计划。\n'
                    '4000 万元订单执行不受本次审计直接影响，各方共担提考连带责任。'),
    ]))
    story.append(Spacer(1, 0.5*cm))

    # ═══════════════════════════════════════════════════════════════════════════
    # 二、适用质量法规体系综述
    # ═══════════════════════════════════════════════════════════════════════════
    story.append(section_header('二、酶类原料供应商适用质量法规体系综述'))
    story.append(Spacer(1, 0.25*cm))

    story.append(Paragraph('2.1 原料定位判断', S['subsection']))
    story.append(Paragraph(
        '愚公生物生产的工具酶（含 RI 核糖核酸酶抑制剂等）在申基 CDMO 场景中，以工艺辅助或关键'
        '原料身份介入药物生产，<b>不作为最终药品活性成分（API）存在于制成品中</b>。依据 ICH Q7、'
        'IPEC-PQG GMP 指南及 FDA/EMA 相关指导原则，其定位层级如下：',
        S['body']))

    pos_data = [
        [Paragraph('定位层级', S['cell_header']),
         Paragraph('判断依据', S['cell_header']),
         Paragraph('适用情景', S['cell_header']),
         Paragraph('适用标准', S['cell_header'])],
        [Paragraph('API 起始物料\n（API Starting Material）', S['cell']),
         Paragraph('酶直接掺入 API 结构，或残留物影响 API 关键质量属性（CQAs）', S['cell']),
         Paragraph('酶参与关键化学反应步骤，残留显著影响杂质谱', S['cell']),
         Paragraph('ICH Q7 全文、供应商审计、DMF 备案', S['cell'])],
        [Paragraph('关键原料 / 工艺助剂\n（Critical Reagent）\n★ 当前最可能定位', S['cell_bold']),
         Paragraph('酶仅参与上游工艺步骤，最终产品中不含酶活或酶蛋白；\n但其质量直接影响产品 CQA（如活性、纯度、内毒素）', S['cell']),
         Paragraph('RI 酶用于 mRNA/蛋白合成工艺，\n纯化后被去除', S['cell']),
         Paragraph('ICH Q7（原材料章节）、IPEC-PQG GMP（基础）、\nISO 9001:2015、质量协议（QA）', S['cell'])],
        [Paragraph('辅料 / 工艺助剂\n（Excipient/Processing Aid）', S['cell']),
         Paragraph('酶以非治疗性功能加入制剂，如增稠、稳定等', S['cell']),
         Paragraph('较少见于工具酶场景', S['cell']),
         Paragraph('IPEC-PQG GMP、EXCiPACT 认证、ISO 9001', S['cell'])],
    ]
    pos_t = Table(pos_data, colWidths=[3.5*cm, 5*cm, 4*cm, 5*cm])
    pos_t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (-1,0), DARK_BLUE),
        ('TEXTCOLOR',     (0,0), (-1,0), WHITE),
        ('FONTNAME',      (0,0), (-1,0), 'STHeiti'),
        ('BACKGROUND',    (0,2), (-1,2), LIGHT_BLUE),
        ('GRID',          (0,0), (-1,-1), 0.4, GRAY_LINE),
        ('TOPPADDING',    (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING',   (0,0), (-1,-1), 5),
        ('RIGHTPADDING',  (0,0), (-1,-1), 5),
        ('VALIGN',        (0,0), (-1,-1), 'TOP'),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [WHITE, LIGHT_BLUE, WHITE]),
    ]))
    story.append(pos_t)
    story.append(Spacer(1, 0.3*cm))

    story.append(Paragraph('2.2 核心适用法规框架', S['subsection']))

    reg_rows = [
        ('ICH Q7\n（API GMP）', 'GMP 原材料章节要求供应商评估与批准；关键原材料须执行三批次全检基准测试；质量协议中须明确检验方法、CoA 要求、供应商变更通知条款。'),
        ('IPEC-PQG GMP 指南\n（辅料/关键原料）', '适用于药物辅料及工艺助剂生产商；要求在工艺中"有理可循"的节点执行 GMP；覆盖文件管理、变更控制、偏差管理、清洁验证、稳定性研究。与 ISO 9001 互补。'),
        ('ISO 9001:2015\n（质量管理体系）', '作为最低基线的质量管理体系；涵盖记录控制、不合格品管理、持续改进、内审等。不能替代药品 GMP，但为药企客户审计的基础期望。'),
        ('中国 NMPA\n相关法规', '《原辅料及直接接触药品包装材料供应商审计指南》（2020年）：药企须对关键原辅料供应商定期审计；供应商须具备与产品风险相称的质量体系；原辅料变更须履行评估与报告程序。'),
        ('质量协议（QA）\n与供应商资质', '申基作为 CDMO 依托其下游客户要求，须与愚公生物签订正式质量协议，约定：CoA 内容与放行标准、审计权利、变更通知时限、可溯源性要求、稳定性和储存条件、内毒素/生物负载限度。'),
        ('关键检测要求\n（酶原料特有）', '活性（RI 活性等效力测定）、纯度（SEC-HPLC）、内毒素（LAL）、生物负载（TAMC/TYMC）、宿主细胞蛋白（HCP）、残余 DNA、重金属；稳定性数据（支持储存条件）。'),
    ]
    for title, content in reg_rows:
        data = [[Paragraph(title, S['reg_title']), Paragraph(content, S['cell'])]]
        t = Table(data, colWidths=[3.8*cm, 13.7*cm])
        t.setStyle(TableStyle([
            ('BACKGROUND',    (0,0), (0,-1), BG_HEAD),
            ('GRID',          (0,0), (-1,-1), 0.4, GRAY_LINE),
            ('TOPPADDING',    (0,0), (-1,-1), 5),
            ('BOTTOMPADDING', (0,0), (-1,-1), 5),
            ('LEFTPADDING',   (0,0), (-1,-1), 6),
            ('RIGHTPADDING',  (0,0), (-1,-1), 6),
            ('VALIGN',        (0,0), (-1,-1), 'TOP'),
        ]))
        story.append(t)
        story.append(Spacer(1, 0.15*cm))

    story.append(Paragraph(
        '结论：愚公生物作为药物关键工具酶原料供应商，不必全面执行药品 GMP（21 CFR Part 211 或《药品生产质量管理规范》），'
        '但须建立并有效运行<b>以 ISO 9001 为基础、参照 IPEC-PQG GMP 指南核心要求的质量体系</b>，并满足申基及其客户在'
        '质量协议中约定的特定要求，包括：数据完整性、验证体系、关键参数检测（尤其 RI 活性）及稳定性研究。',
        S['body']))
    story.append(Spacer(1, 0.5*cm))

    # ═══════════════════════════════════════════════════════════════════════════
    # 三、审计缺陷项分级分析
    # ═══════════════════════════════════════════════════════════════════════════
    story.append(section_header('三、审计缺陷项严重等级分析'))
    story.append(Spacer(1, 0.2*cm))

    # 分级说明
    legend_data = [
        [Paragraph('严重（Critical）', S['cell_crit']),
         Paragraph('重大（Major）', S['cell_maj']),
         Paragraph('一般（Minor）', S['cell_min'])],
        [Paragraph('直接危及产品质量/安全性，或存在系统性数据诚信问题，不可接受，须立即整改', S['cell']),
         Paragraph('显著违反质量体系要求，可能导致产品质量问题，须限期整改', S['cell']),
         Paragraph('程序性缺陷，当前影响有限，须计划整改', S['cell'])],
    ]
    legend_t = Table(legend_data, colWidths=[W/3, W/3, W/3])
    legend_t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (0,-1), BG_CRIT),
        ('BACKGROUND',    (1,0), (1,-1), BG_MAJ),
        ('BACKGROUND',    (2,0), (2,-1), BG_MIN),
        ('GRID',          (0,0), (-1,-1), 0.4, GRAY_LINE),
        ('TOPPADDING',    (0,0), (-1,-1), 5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 5),
        ('LEFTPADDING',   (0,0), (-1,-1), 6),
        ('RIGHTPADDING',  (0,0), (-1,-1), 6),
        ('VALIGN',        (0,0), (-1,-1), 'TOP'),
        ('ALIGN',         (0,0), (-1,0), 'CENTER'),
    ]))
    story.append(legend_t)
    story.append(Spacer(1, 0.35*cm))

    # ── 3.1 严重缺陷 ──────────────────────────────────────────────────────────
    story.append(section_header('3.1 严重缺陷（Critical）', color=RED_CRIT))
    story.append(Spacer(1, 0.2*cm))

    crit_header = [
        Paragraph('编号', S['cell_header']),
        Paragraph('缺陷描述', S['cell_header']),
        Paragraph('法规依据', S['cell_header']),
        Paragraph('风险说明', S['cell_header']),
    ]
    crit_items = [
        ('C-01', '系统性记录造假与数据完整性严重缺失',
         '记录普遍事后补写；一批生产对应三批数据；温湿度/压差辅助记录迎审时统一补填，笔迹一眼可辨，与主批记录时间矛盾；批记录存在明显造假痕迹。',
         'ICH Q7 第 6 章；IPEC-PQG GMP 4.5；ISO 9001 7.5；FDA Data Integrity Guidance（2018）',
         '数据诚信（Data Integrity）是 GMP/质量体系的核心基石。系统性造假导致审计方对所有数据完全不信任，无法证明产品质量受控，直接危及下游客户的药品申报。'),
        ('C-02', 'RI 活性检测能力缺失，未纳入质量标准',
         '生产端荧光仪无法检测 RI 活性；RI 活性效力测定未纳入原酶及成品质量标准；无法证明每批产品活性符合要求。',
         'ICH Q7 11.1；IPEC-PQG GMP 8.1；FDA Potency Guidance；质量协议约定检测项目',
         'RI 活性是该产品的核心关键质量属性（CQA）。无活性检测即无法保证产品功能一致性，直接影响客户药物工艺结果，属最高级别产品质量风险。'),
        ('C-03', '清洁验证严重缺失（交叉污染风险）',
         '100+ 品种共线生产，无任何残留限度设定、无棉签回收率验证、无中转容器清洁验证；无法证明产品间无交叉污染。',
         'IPEC-PQG GMP 6.4；ICH Q7 第 12 章；EU GMP Annex 15',
         '多品种共线而无清洁验证，存在产品间交叉污染的实质性风险，可能导致客户产品出现工艺外源污染物，危及药品安全性与有效性。'),
    ]

    for code, title, desc, ref, risk in crit_items:
        header_row = [
            Paragraph(f'<b>{code}</b>', S['cell_crit']),
            Paragraph(f'<b>{title}</b>', S['cell_crit']),
            Paragraph('', S['cell']),
            Paragraph('', S['cell']),
        ]
        desc_row = [
            Paragraph('缺陷描述', S['cell_bold']),
            Paragraph(desc, S['cell']),
            Paragraph('法规依据', S['cell_bold']),
            Paragraph(ref, S['cell']),
        ]
        risk_row = [
            Paragraph('风险说明', S['cell_bold']),
            Paragraph(risk, S['cell']),
            Paragraph('', S['cell']),
            Paragraph('', S['cell']),
        ]
        t = Table([header_row, desc_row, risk_row],
                  colWidths=[1.8*cm, 6.8*cm, 2*cm, 6.9*cm])
        t.setStyle(TableStyle([
            ('BACKGROUND',    (0,0), (-1,0), BG_CRIT),
            ('BACKGROUND',    (0,1), (0,-1), LIGHT_GRAY),
            ('BACKGROUND',    (2,1), (2,-1), LIGHT_GRAY),
            ('GRID',          (0,0), (-1,-1), 0.4, GRAY_LINE),
            ('SPAN',          (1,0), (3,0)),
            ('SPAN',          (1,2), (3,2)),
            ('TOPPADDING',    (0,0), (-1,-1), 4),
            ('BOTTOMPADDING', (0,0), (-1,-1), 4),
            ('LEFTPADDING',   (0,0), (-1,-1), 5),
            ('RIGHTPADDING',  (0,0), (-1,-1), 5),
            ('VALIGN',        (0,0), (-1,-1), 'TOP'),
        ]))
        story.append(KeepTogether([t, Spacer(1, 0.2*cm)]))

    # ── 3.2 重大缺陷 ──────────────────────────────────────────────────────────
    story.append(Spacer(1, 0.15*cm))
    story.append(section_header('3.2 重大缺陷（Major）', color=ORANGE_MAJ))
    story.append(Spacer(1, 0.2*cm))

    maj_items = [
        ('M-01', '文件体系不具可执行性，照搬药企模板',
         '文件大量照搬恒瑞/天晴模板，与公司实际人力、产能、工艺严重不符；存在替换页、纸张颜色不一致等低级错误；文件被审计方整体质疑。',
         'IPEC-PQG GMP 4.1；ISO 9001 7.5；ICH Q7 第 6 章'),
        ('M-02', '文件发放、接收、销毁流程缺失，虚拟执行',
         '无接收人记录、文件名错误、发放流程形同虚设；存在虚拟执行痕迹（文件从未真实流转）。',
         'IPEC-PQG GMP 4.2；ICH Q7 6.5；ISO 9001 7.5.3'),
        ('M-03', '偏差/OOS/OOT 管理体系完全缺失',
         '无任何偏差、超标（OOS）、超趋势（OOT）记录与处理程序；审计方判定"产品是检出来的，不是生产出来的"。',
         'ICH Q7 第 15 章；IPEC-PQG GMP 5.2；FDA OOS Guidance（2006）'),
        ('M-04', '工艺验证报告不可信，关键参数未覆盖',
         '工艺验证报告未覆盖关键工艺参数、收率及内毒素去除效率；批记录与工艺规程描述不一致；纯化多峰即收，无固定标准。',
         'ICH Q7 第 12 章；IPEC-PQG GMP 7.1；EU GMP Annex 15'),
        ('M-05', '稳定性研究缺失，产品全生命周期数据空白',
         '原酶未开展稳定性研究；现有方案设计不合理（25℃放置 12 个月，不符合酶类特性）；无法为储存条件和效期提供数据支撑。',
         'ICH Q1A(R2)；IPEC-PQG GMP 8.2；ICH Q7 11.6'),
        ('M-06', 'QA/QC 人员专业能力严重不足，QA 负责人不称职',
         'QA/QC 团队多为外行，不了解酶行业特性；QA 负责人言语轻浮、以第三方心态应对客户，现场被当场质疑；人员一人多岗，审计方直接判定数据不可信。',
         'ICH Q7 第 2 章；IPEC-PQG GMP 3.2；ISO 9001 7.2'),
        ('M-07', '关键设备缺失，设备标识不全',
         '缺少灭菌锅、干燥箱等必要设备；砝码、设备标识不全；荧光仪无法满足 RI 活性检测要求。',
         'ICH Q7 第 5 章；IPEC-PQG GMP 6.1；ISO 9001 7.1'),
        ('M-08', '酶与 buffer 未分离管理，不符合规范',
         '酶与 buffer 混合管理，无独立稳定性和质量控制；不符合 GMP/IPEC 产品分类管理规范。',
         'IPEC-PQG GMP 5.1；ICH Q7 11.2'),
    ]

    maj_header = [
        Paragraph('编号', S['cell_header']),
        Paragraph('缺陷名称', S['cell_header']),
        Paragraph('缺陷描述', S['cell_header']),
        Paragraph('法规依据', S['cell_header']),
    ]
    maj_data = [maj_header] + [
        [Paragraph(f'<b>{c}</b>', S['cell_maj']),
         Paragraph(f'<b>{t}</b>', S['cell_bold']),
         Paragraph(d, S['cell']),
         Paragraph(r, S['cell'])]
        for c, t, d, r in maj_items
    ]
    maj_t = Table(maj_data, colWidths=[1.5*cm, 3.5*cm, 7.8*cm, 4.7*cm])
    maj_t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (-1,0), ORANGE_MAJ),
        ('TEXTCOLOR',     (0,0), (-1,0), WHITE),
        ('FONTNAME',      (0,0), (-1,0), 'STHeiti'),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [WHITE, BG_MAJ]),
        ('GRID',          (0,0), (-1,-1), 0.4, GRAY_LINE),
        ('TOPPADDING',    (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING',   (0,0), (-1,-1), 5),
        ('RIGHTPADDING',  (0,0), (-1,-1), 5),
        ('VALIGN',        (0,0), (-1,-1), 'TOP'),
    ]))
    story.append(maj_t)
    story.append(Spacer(1, 0.35*cm))

    # ── 3.3 一般缺陷 ──────────────────────────────────────────────────────────
    story.append(section_header('3.3 一般缺陷（Minor）', color=colors.HexColor('#9B7D11')))
    story.append(Spacer(1, 0.2*cm))

    min_items = [
        ('N-01', '现场卫生问题',
         '下水管道锈蚀严重，现场整洁度不达标，影响客户观感与检查结论。',
         'IPEC-PQG GMP 6.3；ISO 9001 7.1'),
        ('N-02', '文件低级错误（替换页、颜色不一致）',
         '文件中存在替换页、纸张颜色不一致等低级错误，直接导致文件整体可信度下降。',
         'ICH Q7 6.2；IPEC-PQG GMP 4.2'),
        ('N-03', '批记录数据项不完整（缺少计算过程）',
         '批记录中关键数据缺少计算过程记录，数据可追溯性不足（此项与 C-01 系统造假有别，属记录习惯问题）。',
         'ICH Q7 6.4；IPEC-PQG GMP 4.5'),
    ]

    min_header = [
        Paragraph('编号', S['cell_header']),
        Paragraph('缺陷名称', S['cell_header']),
        Paragraph('缺陷描述', S['cell_header']),
        Paragraph('法规依据', S['cell_header']),
    ]
    min_data = [min_header] + [
        [Paragraph(f'<b>{c}</b>', S['cell_min']),
         Paragraph(f'<b>{t}</b>', S['cell_bold']),
         Paragraph(d, S['cell']),
         Paragraph(r, S['cell'])]
        for c, t, d, r in min_items
    ]
    min_t = Table(min_data, colWidths=[1.5*cm, 3.5*cm, 7.8*cm, 4.7*cm])
    min_t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (-1,0), colors.HexColor('#9B7D11')),
        ('TEXTCOLOR',     (0,0), (-1,0), WHITE),
        ('FONTNAME',      (0,0), (-1,0), 'STHeiti'),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [WHITE, BG_MIN]),
        ('GRID',          (0,0), (-1,-1), 0.4, GRAY_LINE),
        ('TOPPADDING',    (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING',   (0,0), (-1,-1), 5),
        ('RIGHTPADDING',  (0,0), (-1,-1), 5),
        ('VALIGN',        (0,0), (-1,-1), 'TOP'),
    ]))
    story.append(min_t)
    story.append(Spacer(1, 0.5*cm))

    # ═══════════════════════════════════════════════════════════════════════════
    # 四、整改行动计划
    # ═══════════════════════════════════════════════════════════════════════════
    story.append(section_header('四、整改行动计划（CAPA）'))
    story.append(Spacer(1, 0.15*cm))
    story.append(Paragraph(
        '以下行动计划以缺陷严重等级为优先序，明确责任部门/人及预估完成日期。'
        '所有整改措施须由 QA 部门完成审核后方可关闭。',
        S['body']))
    story.append(Spacer(1, 0.25*cm))

    # ── 4.1 严重缺陷行动项 ────────────────────────────────────────────────────
    story.append(Paragraph('4.1 严重缺陷整改行动项（须立即启动）', S['subsection']))

    action_header = [
        Paragraph('关联\n缺陷', S['cell_header']),
        Paragraph('行动项', S['cell_header']),
        Paragraph('具体措施', S['cell_header']),
        Paragraph('责任人', S['cell_header']),
        Paragraph('预估完成\n日期', S['cell_header']),
    ]

    crit_actions = [
        ('C-01', 'A-01\n禁止事后\n补录并建立\n数据诚信\n管理制度',
         '① 即日起发布内部令：禁止任何形式的事后补录，违者追责\n'
         '② 制定《数据完整性管理程序》，涵盖原始记录、电子数据、审计追踪\n'
         '③ 所有批记录和辅助记录须当日填写，QA 每日抽查\n'
         '④ 对现有库存批记录完整性进行回顾，提出整改方案\n'
         '⑤ 开展全员数据诚信培训（1 次/月）',
         '毕海（QA）\n全员配合', '2026-05-01\n（①②即日起执行）'),
        ('C-02', 'A-02\n建立 RI 活\n性检测能力\n并纳入质量\n标准',
         '① 立即协调研发部荧光仪，临时用于 RI 活性检测\n'
         '② 任宏杰负责制定 RI 活性效力测定方案，完成方法验证\n'
         '③ 采购/租赁正式 RI 活性检测设备（二袁负责对接采购）\n'
         '④ 将 RI 活性纳入原酶及成品质量标准，修订 QS 文件\n'
         '⑤ 对历史存档批次进行补充 RI 活性检测（留样检测）',
         '任宏杰（研发）\n二袁（采购）\n新 QA 负责人', '2026-07-15\n（设备临时方案\n2026-05-15）'),
        ('C-03', 'A-03\n建立清洁\n验证体系',
         '① 梳理 100+ 品种的共线生产矩阵，按风险分级\n'
         '② 制定《清洁验证主计划》（CVP），明确残留限度计算方法（基于 HBEL/PDE）\n'
         '③ 优先完成高风险品种组合的棉签回收率验证和中转容器清洁验证\n'
         '④ 建立清洁验证记录模板及日常监控程序',
         '毕海（QA）\n新 QA 负责人\n生产部', '2026-08-31\n（CVP 文件\n2026-06-01）'),
    ]

    action_data = [action_header]
    for defect, action, measures, owner, date in crit_actions:
        action_data.append([
            Paragraph(f'<b>{defect}</b>', S['cell_crit']),
            Paragraph(action, S['cell_bold']),
            Paragraph(measures, S['cell']),
            Paragraph(owner, S['cell_bold']),
            Paragraph(date, S['cell_bold']),
        ])
    ca_t = Table(action_data, colWidths=[1.5*cm, 2.2*cm, 8.3*cm, 2.5*cm, 3.0*cm])
    ca_t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (-1,0), RED_CRIT),
        ('TEXTCOLOR',     (0,0), (-1,0), WHITE),
        ('FONTNAME',      (0,0), (-1,0), 'STHeiti'),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [BG_CRIT, WHITE]),
        ('GRID',          (0,0), (-1,-1), 0.4, GRAY_LINE),
        ('TOPPADDING',    (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING',   (0,0), (-1,-1), 5),
        ('RIGHTPADDING',  (0,0), (-1,-1), 5),
        ('VALIGN',        (0,0), (-1,-1), 'TOP'),
    ]))
    story.append(ca_t)
    story.append(Spacer(1, 0.35*cm))

    # ── 4.2 重大缺陷行动项 ────────────────────────────────────────────────────
    story.append(Paragraph('4.2 重大缺陷整改行动项', S['subsection']))

    maj_actions = [
        ('M-01\nM-02', 'A-04\n重建文件\n管理体系',
         '① 全面梳理现有文件，废止不可执行的模板，重新编写贴合公司实际的 SOP\n'
         '② 建立文件发放、接收、销毁的纸质/电子台账，保留完整签字痕迹\n'
         '③ 统一文件格式、纸张规格，消除低级错误\n'
         '④ 新 QA 负责人牵头，每季度开展文件适用性审查',
         '新 QA 负责人\n毕海', '2026-06-30'),
        ('M-03', 'A-05\n建立偏差/\nOOS/OOT\n管理程序',
         '① 编写《偏差管理规程》《OOS 调查程序》《OOT 趋势分析程序》\n'
         '② 对历史生产数据进行回顾，识别并补充登记已发生的偏差/OOS\n'
         '③ 对 QA/QC 人员开展专项培训',
         '新 QA 负责人', '2026-06-15'),
        ('M-04', 'A-06\n完善工艺\n验证报告',
         '① 补充工艺验证报告中关键工艺参数（KPP）、收率、内毒素去除效率数据\n'
         '② 制定固定纯化标准（收峰标准），与工艺规程保持一致\n'
         '③ 执行至少 3 批次连续工艺验证，确保批次间一致性',
         '任宏杰（研发）\n生产部\n新 QA 负责人', '2026-08-31'),
        ('M-05', 'A-07\n启动稳定性\n研究',
         '① 制定《原酶稳定性研究方案》：检测项目简化为酶活性 + 内毒素，避免过度冗余\n'
         '② 参照 ICH Q1A 条件（2-8℃实时 + 25℃加速），启动长期稳定性研究\n'
         '③ 酶与 buffer 分开单独开展稳定性研究\n'
         '④ 稳定性数据支持质量标准中的效期和储存条件设定',
         '任宏杰（研发）\n新 QA 负责人', '2026-07-31\n（方案批准）\n2026-08-01\n（入样启动）'),
        ('M-06', 'A-08\n更换 QA 负\n责人，优化\n质量团队',
         '① 即日起免除 QA 负责人左胜职务，由毕海临时代管 QA 事务\n'
         '② 毕海组织面试不少于 20 名 QA 候选人（林毅/董方辅助推荐）\n'
         '③ 优先从具备酶行业知识的研发骨干中培养，要求懂酶 + 懂法规\n'
         '④ 确定新 QA 负责人后，明确其职责边界（对接客户、向研发/生产提需求）\n'
         '⑤ 加强 QA/QC 人员的持续培训与考核机制',
         '毕海（牵头）\n任宏杰（配合）', '面试启动：即日\n2026-06-01\n（到岗目标）'),
        ('M-07', 'A-09\n补齐关键\n设备',
         '① 二袁对接采购：灭菌锅、干燥箱、砝码、设备标识\n'
         '② 研发部荧光仪优先调配至生产端用于 RI 活性检测\n'
         '③ 所有设备需完成校准/验证后方可投入使用',
         '二袁（采购）\n任宏杰（设备调配）', '2026-05-31'),
        ('M-08', 'A-10\n酶与 buffer\n分离管理',
         '① 将酶与 buffer 分开存储、分开建立质量标准\n'
         '② 分别开展稳定性研究（纳入 A-07 统筹）\n'
         '③ 修订相关 SOP 和批记录模板',
         '新 QA 负责人\n生产部', '2026-06-30'),
    ]

    ma_header = [
        Paragraph('关联\n缺陷', S['cell_header']),
        Paragraph('行动项', S['cell_header']),
        Paragraph('具体措施', S['cell_header']),
        Paragraph('责任人', S['cell_header']),
        Paragraph('预估完成\n日期', S['cell_header']),
    ]
    ma_data = [ma_header]
    for defect, action, measures, owner, date in maj_actions:
        ma_data.append([
            Paragraph(f'<b>{defect}</b>', S['cell_maj']),
            Paragraph(action, S['cell_bold']),
            Paragraph(measures, S['cell']),
            Paragraph(owner, S['cell_bold']),
            Paragraph(date, S['cell_bold']),
        ])
    ma_t = Table(ma_data, colWidths=[1.5*cm, 2.2*cm, 8.3*cm, 2.5*cm, 3.0*cm])
    ma_t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (-1,0), ORANGE_MAJ),
        ('TEXTCOLOR',     (0,0), (-1,0), WHITE),
        ('FONTNAME',      (0,0), (-1,0), 'STHeiti'),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [BG_MAJ, WHITE]),
        ('GRID',          (0,0), (-1,-1), 0.4, GRAY_LINE),
        ('TOPPADDING',    (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING',   (0,0), (-1,-1), 5),
        ('RIGHTPADDING',  (0,0), (-1,-1), 5),
        ('VALIGN',        (0,0), (-1,-1), 'TOP'),
    ]))
    story.append(ma_t)
    story.append(Spacer(1, 0.35*cm))

    # ── 4.3 一般缺陷行动项 ────────────────────────────────────────────────────
    story.append(Paragraph('4.3 一般缺陷整改行动项', S['subsection']))

    min_actions = [
        ('N-01', 'A-11\n现场卫生\n整改',
         '① 即日安排人员清理下水管道锈蚀，必要时更换管道\n② 建立日常现场卫生检查制度（每日巡检记录）',
         '生产部\n毕海（督导）', '2026-05-15'),
        ('N-02', 'A-12\n消除文件\n低级错误',
         '① 系统排查现有文件中的替换页、纸张颜色不一致问题，统一重印\n② 制定文件印制和替换标准操作程序',
         '新 QA 负责人', '2026-06-30'),
        ('N-03', 'A-13\n完善批记\n录数据项',
         '① 修订批记录模板，新增计算过程记录栏\n② 对现有操作人员开展批记录填写规范培训',
         '新 QA 负责人\n生产部', '2026-06-30'),
    ]

    mi_header = [
        Paragraph('关联\n缺陷', S['cell_header']),
        Paragraph('行动项', S['cell_header']),
        Paragraph('具体措施', S['cell_header']),
        Paragraph('责任人', S['cell_header']),
        Paragraph('预估完成\n日期', S['cell_header']),
    ]
    mi_data = [mi_header]
    for defect, action, measures, owner, date in min_actions:
        mi_data.append([
            Paragraph(f'<b>{defect}</b>', S['cell_min']),
            Paragraph(action, S['cell_bold']),
            Paragraph(measures, S['cell']),
            Paragraph(owner, S['cell_bold']),
            Paragraph(date, S['cell_bold']),
        ])
    mi_t = Table(mi_data, colWidths=[1.5*cm, 2.2*cm, 8.3*cm, 2.5*cm, 3.0*cm])
    mi_t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (-1,0), colors.HexColor('#9B7D11')),
        ('TEXTCOLOR',     (0,0), (-1,0), WHITE),
        ('FONTNAME',      (0,0), (-1,0), 'STHeiti'),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [BG_MIN, WHITE]),
        ('GRID',          (0,0), (-1,-1), 0.4, GRAY_LINE),
        ('TOPPADDING',    (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
        ('LEFTPADDING',   (0,0), (-1,-1), 5),
        ('RIGHTPADDING',  (0,0), (-1,-1), 5),
        ('VALIGN',        (0,0), (-1,-1), 'TOP'),
    ]))
    story.append(mi_t)
    story.append(Spacer(1, 0.5*cm))

    # ═══════════════════════════════════════════════════════════════════════════
    # 五、整改里程碑时间轴汇总
    # ═══════════════════════════════════════════════════════════════════════════
    story.append(section_header('五、整改里程碑时间轴汇总'))
    story.append(Spacer(1, 0.2*cm))

    timeline_header = [
        Paragraph('里程碑节点', S['cell_header']),
        Paragraph('主要行动项', S['cell_header']),
        Paragraph('责任人', S['cell_header']),
        Paragraph('目标日期', S['cell_header']),
    ]
    timeline_data = [timeline_header,
        [Paragraph('★ 立即启动\n（≤ 1 周）', S['cell_crit']),
         Paragraph('A-01 发布数据诚信禁令；A-08 免除左胜职务、毕海代管QA；A-09 研发设备调配至生产；A-11 现场清洁启动', S['cell']),
         Paragraph('毕海、任宏杰、二袁', S['cell_bold']),
         Paragraph('2026-04-22', S['cell_bold'])],
        [Paragraph('第一阶段\n（1个月内）', S['cell_crit']),
         Paragraph('A-01 数据诚信制度落地；A-08 启动QA招聘（面试≥20人）；A-09 设备采购启动；A-11 现场卫生完成', S['cell']),
         Paragraph('毕海（主）\n二袁（采购）', S['cell_bold']),
         Paragraph('2026-05-15', S['cell_bold'])],
        [Paragraph('第二阶段\n（2个月内）', S['cell_maj']),
         Paragraph('A-02 RI临时检测方案上线；A-05 偏差/OOS/OOT程序建立；A-08 新QA负责人到岗；A-03 清洁验证主计划发布', S['cell']),
         Paragraph('新QA负责人\n任宏杰', S['cell_bold']),
         Paragraph('2026-06-15', S['cell_bold'])],
        [Paragraph('第三阶段\n（3个月内）', S['cell_maj']),
         Paragraph('A-04 文件体系重建完成；A-06 工艺验证报告补充完成；A-07 稳定性研究入样启动；A-10 酶/buffer分离管理完成；A-12/13 批记录规范化', S['cell']),
         Paragraph('新QA负责人\n任宏杰\n生产部', S['cell_bold']),
         Paragraph('2026-06-30', S['cell_bold'])],
        [Paragraph('第四阶段\n（4~5个月内）', S['cell_maj']),
         Paragraph('A-02 RI活性正式纳入质量标准并完成方法验证；A-06 3批次工艺验证完成；A-09 正式设备到位并完成验证', S['cell']),
         Paragraph('任宏杰\n新QA负责人', S['cell_bold']),
         Paragraph('2026-07-31', S['cell_bold'])],
        [Paragraph('第五阶段\n（≤6个月）', S['cell_bold']),
         Paragraph('A-03 清洁验证关键品种完成；A-02 RI活性检测体系完全建立；准备接受申基/客户复查审计', S['cell']),
         Paragraph('毕海\n新QA负责人\n全员', S['cell_bold']),
         Paragraph('2026-08-31', S['cell_bold'])],
    ]
    tl_t = Table(timeline_data, colWidths=[2.8*cm, 9.2*cm, 2.8*cm, 2.7*cm])
    tl_t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (-1,0), DARK_BLUE),
        ('TEXTCOLOR',     (0,0), (-1,0), WHITE),
        ('FONTNAME',      (0,0), (-1,0), 'STHeiti'),
        ('ROWBACKGROUNDS', (0,1), (-1,-1), [BG_CRIT, BG_CRIT, BG_MAJ, BG_MAJ, BG_MAJ, LIGHT_BLUE]),
        ('GRID',          (0,0), (-1,-1), 0.4, GRAY_LINE),
        ('TOPPADDING',    (0,0), (-1,-1), 5),
        ('BOTTOMPADDING', (0,0), (-1,-1), 5),
        ('LEFTPADDING',   (0,0), (-1,-1), 6),
        ('RIGHTPADDING',  (0,0), (-1,-1), 6),
        ('VALIGN',        (0,0), (-1,-1), 'TOP'),
    ]))
    story.append(tl_t)
    story.append(Spacer(1, 0.5*cm))

    # ═══════════════════════════════════════════════════════════════════════════
    # 六、决议与签字
    # ═══════════════════════════════════════════════════════════════════════════
    story.append(section_header('六、会议决议与签署'))
    story.append(Spacer(1, 0.2*cm))

    decisions = [
        '本次会议就申基供应商审计发现的全部问题达成整改共识，与会人员承诺按照上述行动计划推进落实。',
        '4000 万元订单执行不受本次审计直接影响，各方共担下游药企提考连带责任，应以主人翁心态推进整改。',
        '整改优先级排序：RI 活性检测体系建立 → 数据诚信管理 → 清洁验证 → QA 人员优化 → 文件体系重建 → 稳定性研究。',
        '所有整改措施的完成情况须由新任 QA 负责人进行验证和记录，整改完成后须准备向申基/客户提交整改报告并接受复查审计。',
        '毕海为本次整改的首要协调人，任宏杰负责研发端支持，二袁负责客户沟通与采购事务。',
    ]
    for i, d in enumerate(decisions, 1):
        story.append(Paragraph(f'{i}. {d}', S['body']))
    story.append(Spacer(1, 0.5*cm))

    sign_data = [
        [Paragraph('主持人', S['cell_bold']),
         Paragraph('任宏杰　　　　签名：_______________', S['cell']),
         Paragraph('日期：_____________', S['cell'])],
        [Paragraph('质量代表', S['cell_bold']),
         Paragraph('毕海　　　　　签名：_______________', S['cell']),
         Paragraph('日期：_____________', S['cell'])],
        [Paragraph('商务/对外', S['cell_bold']),
         Paragraph('二袁　　　　　签名：_______________', S['cell']),
         Paragraph('日期：_____________', S['cell'])],
        [Paragraph('参会确认', S['cell_bold']),
         Paragraph('说话人 C　　　签名：_______________', S['cell']),
         Paragraph('日期：_____________', S['cell'])],
        [Paragraph('参会确认', S['cell_bold']),
         Paragraph('说话人 E　　　签名：_______________', S['cell']),
         Paragraph('日期：_____________', S['cell'])],
    ]
    sign_t = Table(sign_data, colWidths=[2.8*cm, 9*cm, 5.7*cm])
    sign_t.setStyle(TableStyle([
        ('BACKGROUND',    (0,0), (0,-1), BG_HEAD),
        ('GRID',          (0,0), (-1,-1), 0.4, GRAY_LINE),
        ('TOPPADDING',    (0,0), (-1,-1), 8),
        ('BOTTOMPADDING', (0,0), (-1,-1), 8),
        ('LEFTPADDING',   (0,0), (-1,-1), 6),
        ('RIGHTPADDING',  (0,0), (-1,-1), 6),
        ('VALIGN',        (0,0), (-1,-1), 'MIDDLE'),
    ]))
    story.append(sign_t)
    story.append(Spacer(1, 0.4*cm))

    # ── 页脚说明 ──────────────────────────────────────────────────────────────
    story.append(HRFlowable(width=W, thickness=0.5, color=GRAY_LINE))
    story.append(Spacer(1, 0.1*cm))
    story.append(Paragraph(
        '本文件为愚公生物科技有限公司（百时美）内部保密文件，未经授权不得对外披露。\n'
        '文件编号：YG-QA-MOM-2026-001　版本：V1.0　生效日期：2026-04-15　制定部门：质量管理部',
        S['footer']))

    doc.build(story)
    print(f'PDF generated: {output_path}')

if __name__ == '__main__':
    output = '/Users/tankai/Documents/Projects/deep_research/愚公生物_申基审计整改会议纪要_2026-04-10.pdf'
    build_pdf(output)
