from pathlib import Path
import json, re, zipfile, sys, types
from docx import Document
from docx.shared import Pt, Cm, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_CELL_VERTICAL_ALIGNMENT
from docx.oxml import OxmlElement
from docx.oxml.ns import qn

PROJECT = Path('/Users/bot1/Volumes/root_for_ai/AI工作区/百丽鞋履品牌_敦煌美术研究所报价单_20260617_1449')
DELIVER = PROJECT / 'deliverables'
WORK = PROJECT / 'work'
ASSETS = WORK / 'assets'
DELIVER.mkdir(parents=True, exist_ok=True)
WORK.mkdir(parents=True, exist_ok=True)
ASSETS.mkdir(parents=True, exist_ok=True)

OLD_LOGO = Path('/Users/bot1/Volumes/root_for_ai/AI工作区/GAP_报价单_敦煌美术研究所两档方案_20260608_2010/work/assets/logo_p4_standard_tight.png')
LOGO = ASSETS / '敦煌美术研究所_标准logo.png'
if OLD_LOGO.exists() and not LOGO.exists():
    LOGO.write_bytes(OLD_LOGO.read_bytes())

DOCX_OUT = DELIVER / '百丽旗下鞋履品牌_敦煌美术研究所IP联名授权报价单_清货期定义修正版_20260617.docx'
PDF_OUT = DELIVER / '百丽旗下鞋履品牌_敦煌美术研究所IP联名授权报价单_清货期定义修正版_20260617.pdf'
README = PROJECT / 'README.md'

PRIMARY = '17365D'
MUTED = '5A6678'
META_BG = 'EDF2F8'
FIRST_COL_BG = 'F7F9FC'
BORDER = 'D8DEE8'
BORDER_META = 'C9D2E3'
TEXT = '333333'
NOTE = '4D5A6B'
QUOTE_DATE_SLASH = '2026/6/17'
QUOTE_DATE_CN = '2026年6月17日'

# ---------------- DOCX helpers ----------------
def font_run(run, size=10, bold=False, color=TEXT):
    run.font.name = '微软雅黑'
    run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
    run.font.size = Pt(size)
    run.bold = bold
    run.font.color.rgb = RGBColor.from_string(color)
    return run


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_text(cell, text, bold=False, color=TEXT, size=8.8, align=None):
    cell.text = ''
    p = cell.paragraphs[0]
    p.paragraph_format.space_before = Pt(0)
    p.paragraph_format.space_after = Pt(0)
    if align is not None:
        p.alignment = align
    for idx, part in enumerate(str(text).split('\n')):
        if idx:
            p.add_run().add_break()
        run = p.add_run(part)
        font_run(run, size=size, bold=bold, color=color)
    cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER


def set_borders(table, color=BORDER, sz='6'):
    tbl = table._tbl
    tblPr = tbl.tblPr
    borders = tblPr.first_child_found_in('w:tblBorders')
    if borders is None:
        borders = OxmlElement('w:tblBorders')
        tblPr.append(borders)
    for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
        tag = 'w:' + edge
        element = borders.find(qn(tag))
        if element is None:
            element = OxmlElement(tag)
            borders.append(element)
        element.set(qn('w:val'), 'single')
        element.set(qn('w:sz'), sz)
        element.set(qn('w:space'), '0')
        element.set(qn('w:color'), color)


def add_heading(doc, text, level=1):
    p = doc.add_paragraph()
    p.paragraph_format.space_before = Pt(12 if level == 1 else 8)
    p.paragraph_format.space_after = Pt(6)
    r = p.add_run(text)
    font_run(r, size=13.5 if level == 1 else 11.5, bold=True, color=PRIMARY if level == 1 else '3C4A5E')
    return p


def add_note(doc, text):
    p = doc.add_paragraph()
    p.paragraph_format.left_indent = Cm(0.15)
    p.paragraph_format.space_after = Pt(4)
    p.paragraph_format.line_spacing = 1.12
    r = p.add_run(text)
    font_run(r, size=8.8, color=NOTE)
    return p


def add_table(doc, headers, rows, header_dark=True, first_col_shade=True, header_size=8.6, body_size=8.0, center_cols=None):
    center_cols = set(center_cols or [])
    table = doc.add_table(rows=1, cols=len(headers))
    table.alignment = WD_TABLE_ALIGNMENT.CENTER
    set_borders(table)
    for i, h in enumerate(headers):
        set_cell_shading(table.rows[0].cells[i], PRIMARY if header_dark else META_BG)
        set_cell_text(table.rows[0].cells[i], h, bold=True, color='FFFFFF' if header_dark else PRIMARY, size=header_size, align=WD_ALIGN_PARAGRAPH.CENTER)
    for row_data in rows:
        cells = table.add_row().cells
        for i, val in enumerate(row_data):
            set_cell_text(cells[i], val, bold=(i == 0), size=body_size, align=WD_ALIGN_PARAGRAPH.CENTER if i in center_cols else None)
            if i == 0 and first_col_shade:
                set_cell_shading(cells[i], FIRST_COL_BG)
    return table

# ---------------- content ----------------
summary_rows = [
    ['方案A｜保底 + 超额分成', '自双方约定的产品正式销售日起6个月；开发期另行协商，不计入授权期', '3个月；仅限销售授权期内已上架产品，可进行库存促销，不得新增设计、生产、上架或开展额外活动', '20–25张授权图库；可开发5–6款联名鞋履；可开发5个礼赠周边细分品类；双方社交媒体同步宣发；敦煌美术研究所微博、公众号、视频号、快手、抖音、小红书账号矩阵联动', '保底授权费20万元；超出保底销售额后按6%提成'],
    ['方案B｜40万一口价', '自双方约定的产品正式销售日起6个月；开发期另行协商，不计入授权期', '3个月；仅限销售授权期内已上架产品，可进行库存促销，不得新增设计、生产、上架或开展额外活动', '20–25张授权图库；可开发5–6款联名鞋履；可开发5个礼赠周边细分品类；双方社交媒体同步宣发；敦煌美术研究所微博、公众号、视频号、快手、抖音、小红书账号矩阵联动', '一口价授权费40万元'],
]
base_rows = [
    ('授权期限', '自双方约定的产品正式销售日起计算6个月', '自双方约定的产品正式销售日起计算6个月'),
    ('开发期', '由双方另行协商确认，不计入6个月授权期', '由双方另行协商确认，不计入6个月授权期'),
    ('授权起算', '以双方约定的产品正式销售时间为准；如双方约定产品于2027年1月1日正式销售，则授权期为2027年1月1日至2027年6月30日', '以双方约定的产品正式销售时间为准；如双方约定产品于2027年1月1日正式销售，则授权期为2027年1月1日至2027年6月30日'),
    ('清货期', '3个月；仅限继续销售授权期内已经上架销售的联名产品', '3个月；仅限继续销售授权期内已经上架销售的联名产品'),
    ('清货期边界', '清货期内不得再进行新产品设计、生产或上架销售；不得开展新增联名活动、特别定制快闪或其他额外营销活动；可围绕既有库存进行常规促销、折扣销售', '清货期内不得再进行新产品设计、生产或上架销售；不得开展新增联名活动、特别定制快闪或其他额外营销活动；可围绕既有库存进行常规促销、折扣销售'),
    ('授权区域', '中国大陆地区（如需扩展港澳台或海外地区，需另行书面确认）', '中国大陆地区（如需扩展港澳台或海外地区，需另行书面确认）'),
    ('授权品类', '鞋履联名产品；可开发5–6款，同款不同颜色视为一款', '鞋履联名产品；可开发5–6款，同款不同颜色视为一款'),
    ('图库授权', '可使用20–25张敦煌美术研究所授权图库；品牌当前意向以羊年图库为主', '可使用20–25张敦煌美术研究所授权图库；品牌当前意向以羊年图库为主'),
    ('宣发联动', '双方社交媒体同步宣发；敦煌美术研究所微博、公众号、视频号、快手、抖音、小红书账号矩阵配合联动发布', '双方社交媒体同步宣发；敦煌美术研究所微博、公众号、视频号、快手、抖音、小红书账号矩阵配合联动发布'),
    ('周边开发', '可开发5个礼赠周边细分品类，如小配饰、丝巾等常见文创产品品类；仅用于礼赠场景', '可开发5个礼赠周边细分品类，如小配饰、丝巾等常见文创产品品类；仅用于礼赠场景'),
]
rights_rows = [
    ('授权权益 / 基础授权', '在约定授权区域、授权期限、授权品类及销售渠道内，客户方可围绕百丽旗下鞋履品牌 × 敦煌美术研究所联名项目使用经双方确认的授权素材、联名名称及相关IP内容。具体授权边界、审核流程及使用规范以双方正式合同及官方审核确认为准。', '包含', '包含'),
    ('图库使用', '可从敦煌美术研究所授权图库中选择20–25张图进行联名产品、包装、吊牌、线上线下宣传及配套物料使用；品牌当前意向以羊年图库为主。具体图片清单、二次创作方式及使用边界以双方确认结果为准。', '20–25张', '20–25张'),
    ('鞋履产品开发', '围绕授权图库进行鞋履联名开发。联名产品的设计稿、包装、吊牌、物料及上市前宣传内容需按授权流程提交审核后使用。', '5–6款；同款不同颜色视为一款', '5–6款；同款不同颜色视为一款'),
    ('礼赠周边开发', '可开发5个礼赠周边细分品类，用于礼赠场景。礼赠周边需符合授权素材使用边界，并按审核流程确认；如涉及销售用途、独立上市或扩展品类，需另行书面确认。', '5个细分品类', '5个细分品类'),
    ('官方宣发支持', '敦煌美术研究所微博、公众号、视频号、快手、抖音、小红书账号可配合宣传联名合作内容，双方社交媒体同步宣发联动。具体平台组合、素材内容、发布时间、发布次数及内容口径以双方排期和审核确认为准。', '包含', '包含'),
    ('品牌渠道传播', '客户方可在经确认的线上线下销售渠道、品牌社交媒体、会员触点及门店物料中使用联名项目内容，具体露出方式以授权审核结果为准。', '包含', '包含'),
    ('Logo / 名称使用', '可在经确认的联名产品、包装吊牌、宣传物料、销售页面及项目传播中使用敦煌美术研究所名称、Logo或授权标识；具体露出形式以官方审核确认为准。', '包含', '包含'),
]
fee_rows = [
    ('方案A｜保底 + 超额分成', '保底授权费20万元', '超额分成比例6%', '包含5–6款联名鞋履开发权利及5个礼赠周边细分品类开发权利；保底销售额 = 20万元 ÷ 6% = 333.33万元；在保底销售额内不再另行支付分成，超出部分按6%支付提成；超额分成每月结算一次，销售额口径以合同约定为准'),
    ('方案B｜40万一口价', '一口价授权费40万元', '不另设超额分成', '授权期内按本报价单约定范围执行；包含5–6款联名鞋履开发权利及5个礼赠周边细分品类开发权利'),
]
notes = [
    '杭州鲜活万物品牌管理有限公司为敦煌美术研究所独家代理，本报价单由其作为本次授权合作商务报价主体出具。',
    '本报价为商务报价参考，最终合作范围、授权素材、审核流程、付款节点、开票信息、结算方式及双方责任边界以双方正式签署的合同或补充协议为准。',
    '授权期为6个月，自双方约定的产品正式销售日起算；开发期由双方另行协商确认，不计入6个月授权期。',
    '如双方约定产品于2027年1月1日正式销售，则授权期为2027年1月1日至2027年6月30日；清货期另设3个月。',
    '清货期仅用于销售授权期内已经上架销售的联名产品，不得在清货期内继续进行新产品设计、生产或上架销售。',
    '清货期内不得开展新增联名活动、特别定制快闪或其他额外营销活动；但可围绕既有库存进行常规促销、折扣销售。具体清货期销售渠道、促销形式及物料使用边界以正式合同约定为准。',
    '图库授权数量为20–25张，品牌当前意向以羊年图库为主；最终图片清单、设计修改方式、应用范围及审核标准以双方确认结果为准。',
    '方案A中，保底销售额按20万元 ÷ 6%计算，约为333.33万元；在保底销售额内不再额外支付分成，超出保底销售额部分按6%支付提成，并按月结算。',
    '方案A及方案B均包含5–6款联名鞋履开发权利，同款不同颜色视为一款；同时均包含5个礼赠周边细分品类开发权利，周边仅用于礼赠场景，如需销售或扩展品类需另行确认。',
    '双方社交媒体及敦煌美术研究所微博、公众号、视频号、快手、抖音、小红书账号矩阵联动需根据实际执行方案确认平台、排期、物料、审核标准及发布口径。',
    '所有涉及敦煌美术研究所名称、Logo、图库、授权标识及文化内容的使用，须按授权流程提交审核后使用；未经书面确认不得超范围、超品类、超期限或超区域使用。',
]

# ---------------- Build DOCX ----------------
doc = Document()
section = doc.sections[0]
section.top_margin = Cm(1.5)
section.bottom_margin = Cm(1.4)
section.left_margin = Cm(1.55)
section.right_margin = Cm(1.55)
styles = doc.styles
styles['Normal'].font.name = '微软雅黑'
styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
styles['Normal'].font.size = Pt(10)

if LOGO.exists():
    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    p.paragraph_format.space_after = Pt(4)
    p.add_run().add_picture(str(LOGO), width=Cm(3.6))

p = doc.add_paragraph(); p.alignment = WD_ALIGN_PARAGRAPH.CENTER; p.paragraph_format.space_after = Pt(2)
font_run(p.add_run('商务报价单'), size=24, bold=True, color=PRIMARY)
p = doc.add_paragraph(); p.alignment = WD_ALIGN_PARAGRAPH.CENTER; p.paragraph_format.space_after = Pt(10)
font_run(p.add_run('百丽旗下鞋履品牌 × 敦煌美术研究所 IP 联名授权合作'), size=12.5, color=MUTED)

meta = doc.add_table(rows=3, cols=4); meta.alignment = WD_TABLE_ALIGNMENT.CENTER; set_borders(meta, BORDER_META)
meta_data = [
    ('客户方', '百丽集团旗下鞋履品牌（待确认主体）', '报价方', '杭州鲜活万物品牌管理有限公司（敦煌美术研究所独家代理）'),
    ('项目名称', '百丽旗下鞋履品牌 × 敦煌美术研究所 IP 联名授权合作', '报价日期', QUOTE_DATE_SLASH),
    ('报价有效期', '自报价日起30天', '报价方案', '保底+分成 / 40万一口价双方案'),
]
for r_i, row in enumerate(meta.rows):
    for c_i, val in enumerate(meta_data[r_i]):
        if c_i % 2 == 0:
            set_cell_shading(row.cells[c_i], META_BG)
            set_cell_text(row.cells[c_i], val, bold=True, color=PRIMARY, size=9.2, align=WD_ALIGN_PARAGRAPH.CENTER)
        else:
            set_cell_text(row.cells[c_i], val, size=9.2)

doc.add_paragraph()
add_heading(doc, '一、报价方案总览')
add_table(doc, ['方案', '授权期限', '清货期', '核心权益', '授权费用'], summary_rows, header_size=8.7, body_size=8.2, center_cols=[1,2,4])

add_heading(doc, '二、授权基础条件')
add_table(doc, ['项目', '方案A｜保底 + 超额分成', '方案B｜40万一口价'], base_rows, header_dark=False, header_size=9.0, body_size=8.45)

add_heading(doc, '三、授权权益明细')
add_table(doc, ['权益模块', '具体内容', '方案A', '方案B'], rights_rows, header_size=8.6, body_size=8.0, center_cols=[2,3])

add_heading(doc, '四、费用及结算方式')
add_table(doc, ['合作方案', '授权费用', '分成 / 结算', '说明'], fee_rows, header_size=8.6, body_size=8.15, center_cols=[1,2])

add_heading(doc, '五、商务执行说明')
for i, note in enumerate(notes, 1):
    add_note(doc, f'{i}. {note}')

add_heading(doc, '六、确认栏')
confirm = doc.add_table(rows=4, cols=4); confirm.alignment = WD_TABLE_ALIGNMENT.CENTER; set_borders(confirm, BORDER_META)
confirm_rows = [
    ('报价方', '杭州鲜活万物品牌管理有限公司', '客户方', '百丽集团旗下鞋履品牌（待确认主体）'),
    ('联系人', '苏薇', '联系人', '待填写'),
    ('联系电话/邮箱', '15382320871', '联系电话/邮箱', '待填写'),
    ('确认日期', QUOTE_DATE_CN, '确认日期', '待填写'),
]
for r_i, row in enumerate(confirm.rows):
    for c_i, val in enumerate(confirm_rows[r_i]):
        if c_i % 2 == 0:
            set_cell_shading(row.cells[c_i], META_BG)
            set_cell_text(row.cells[c_i], val, bold=True, color=PRIMARY, size=8.8, align=WD_ALIGN_PARAGRAPH.CENTER)
        else:
            set_cell_text(row.cells[c_i], val, size=8.8)
footer = section.footer.paragraphs[0]
footer.alignment = WD_ALIGN_PARAGRAPH.CENTER
font_run(footer.add_run('本报价单为商务沟通文件｜最终权利义务以双方正式合同为准'), size=8, color='8A94A6')
doc.save(str(DOCX_OUT))

# ---------------- PDF ----------------
# Stub Pillow only if it is broken; ReportLab text-only still works. Logo is optional in PDF.
from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.lib.pagesizes import A4, landscape
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import mm
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak
try:
    from reportlab.platypus import Image as RLImage
except Exception:
    RLImage = None
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont

pdfmetrics.registerFont(UnicodeCIDFont('STSong-Light'))
FONT = 'STSong-Light'
PAGE_SIZE = landscape(A4)
primary = colors.HexColor('#17365D')
light = colors.HexColor('#EDF2F8')
light2 = colors.HexColor('#F7F9FC')
grid = colors.HexColor('#C9D2E3')
textc = colors.HexColor('#333333')
muted = colors.HexColor('#4D5A6B')

pdf_styles = getSampleStyleSheet()
pdf_styles.add(ParagraphStyle(name='CNTitle', fontName=FONT, fontSize=24, leading=30, alignment=TA_CENTER, textColor=primary, spaceAfter=4))
pdf_styles.add(ParagraphStyle(name='CNSubTitle', fontName=FONT, fontSize=12.5, leading=17, alignment=TA_CENTER, textColor=colors.HexColor('#5A6678'), spaceAfter=10))
pdf_styles.add(ParagraphStyle(name='CNHeading', fontName=FONT, fontSize=13.5, leading=18, textColor=primary, spaceBefore=9, spaceAfter=6))
pdf_styles.add(ParagraphStyle(name='CNCell', fontName=FONT, fontSize=7.6, leading=10.0, textColor=textc, alignment=TA_LEFT))
pdf_styles.add(ParagraphStyle(name='CNCellCenter', fontName=FONT, fontSize=7.6, leading=10.0, textColor=textc, alignment=TA_CENTER))
pdf_styles.add(ParagraphStyle(name='CNHeadCell', fontName=FONT, fontSize=8.0, leading=10.5, textColor=colors.white, alignment=TA_CENTER))
pdf_styles.add(ParagraphStyle(name='CNBlueHeadCell', fontName=FONT, fontSize=8.0, leading=10.5, textColor=primary, alignment=TA_CENTER))
pdf_styles.add(ParagraphStyle(name='CNNote', fontName=FONT, fontSize=8.2, leading=11.5, textColor=muted, leftIndent=4))
pdf_styles.add(ParagraphStyle(name='CNFooter', fontName=FONT, fontSize=7.2, leading=9, alignment=TA_CENTER, textColor=colors.HexColor('#8A94A6')))

def esc(s):
    return str(s).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('\n','<br/>')

def P(text, style='CNCell'):
    return Paragraph(esc(text), pdf_styles[style])

def table(data, widths, header=True, first_col_shade=False, header_dark=True, repeatRows=1, center_cols=None):
    center_cols = set(center_cols or [])
    converted = []
    for r, row in enumerate(data):
        converted_row = []
        for c, val in enumerate(row):
            if header and r == 0:
                converted_row.append(P(val, 'CNHeadCell' if header_dark else 'CNBlueHeadCell'))
            else:
                converted_row.append(P(val, 'CNCellCenter' if c in center_cols else 'CNCell'))
        converted.append(converted_row)
    t = Table(converted, colWidths=widths, repeatRows=repeatRows if header else 0, hAlign='CENTER')
    cmds = [
        ('GRID', (0,0), (-1,-1), 0.5, grid),
        ('VALIGN', (0,0), (-1,-1), 'MIDDLE'),
        ('LEFTPADDING', (0,0), (-1,-1), 4),
        ('RIGHTPADDING', (0,0), (-1,-1), 4),
        ('TOPPADDING', (0,0), (-1,-1), 4),
        ('BOTTOMPADDING', (0,0), (-1,-1), 4),
    ]
    if header:
        cmds += [('BACKGROUND', (0,0), (-1,0), primary if header_dark else light)]
    if first_col_shade:
        start = 1 if header else 0
        cmds += [('BACKGROUND', (0,start), (0,-1), light2)]
    t.setStyle(TableStyle(cmds))
    return t

pdf = SimpleDocTemplate(str(PDF_OUT), pagesize=PAGE_SIZE, leftMargin=14*mm, rightMargin=14*mm, topMargin=12*mm, bottomMargin=12*mm, title='百丽旗下鞋履品牌 敦煌美术研究所 IP 联名授权报价单')
story = []
# keep landscape PDF compact and aligned with previous quote style; DOCX contains logo.
story.append(Paragraph('商务报价单', pdf_styles['CNTitle']))
story.append(Paragraph('百丽旗下鞋履品牌 × 敦煌美术研究所 IP 联名授权合作', pdf_styles['CNSubTitle']))
meta_pdf = [
    [P('客户方','CNCellCenter'), P('百丽集团旗下鞋履品牌（待确认主体）'), P('报价方','CNCellCenter'), P('杭州鲜活万物品牌管理有限公司（敦煌美术研究所独家代理）')],
    [P('项目名称','CNCellCenter'), P('百丽旗下鞋履品牌 × 敦煌美术研究所 IP 联名授权合作'), P('报价日期','CNCellCenter'), P(QUOTE_DATE_SLASH)],
    [P('报价有效期','CNCellCenter'), P('自报价日起30天'), P('报价方案','CNCellCenter'), P('保底+分成 / 40万一口价双方案')],
]
meta_t = Table(meta_pdf, colWidths=[26*mm, 83*mm, 26*mm, 83*mm])
meta_t.setStyle(TableStyle([
    ('GRID',(0,0),(-1,-1),0.5,grid), ('VALIGN',(0,0),(-1,-1),'MIDDLE'),
    ('BACKGROUND',(0,0),(0,-1),light), ('BACKGROUND',(2,0),(2,-1),light),
    ('TEXTCOLOR',(0,0),(0,-1),primary), ('TEXTCOLOR',(2,0),(2,-1),primary),
    ('LEFTPADDING',(0,0),(-1,-1),5), ('RIGHTPADDING',(0,0),(-1,-1),5),
    ('TOPPADDING',(0,0),(-1,-1),5), ('BOTTOMPADDING',(0,0),(-1,-1),5),
]))
story += [meta_t, Spacer(1, 5*mm)]
story.append(Paragraph('一、报价方案总览', pdf_styles['CNHeading']))
story.append(table([
    ['方案', '授权期限', '清货期', '核心权益', '授权费用'],
] + summary_rows, [42*mm, 20*mm, 18*mm, 103*mm, 42*mm], first_col_shade=True, center_cols=[1,2,4]))
story.append(Paragraph('二、授权基础条件', pdf_styles['CNHeading']))
story.append(table([
    ['项目', '方案A｜保底 + 超额分成', '方案B｜40万一口价'],
] + [list(x) for x in base_rows], [35*mm, 92*mm, 92*mm], first_col_shade=True, header_dark=False))
story.append(PageBreak())
story.append(Paragraph('三、授权权益明细', pdf_styles['CNHeading']))
story.append(table([
    ['权益模块', '具体内容', '方案A', '方案B'],
] + [list(x) for x in rights_rows], [33*mm, 122*mm, 32*mm, 32*mm], first_col_shade=True, center_cols=[2,3]))
story.append(Paragraph('四、费用及结算方式', pdf_styles['CNHeading']))
story.append(table([
    ['合作方案', '授权费用', '分成 / 结算', '说明'],
] + [list(x) for x in fee_rows], [45*mm, 35*mm, 46*mm, 95*mm], first_col_shade=True, center_cols=[1,2]))
story.append(PageBreak())
story.append(Paragraph('五、商务执行说明', pdf_styles['CNHeading']))
for i, n in enumerate(notes, 1):
    story.append(Paragraph(f'{i}. {esc(n)}', pdf_styles['CNNote']))
    story.append(Spacer(1, 1.2*mm))
story.append(Paragraph('六、确认栏', pdf_styles['CNHeading']))
confirm_pdf = [
    [P('报价方','CNCellCenter'), P('杭州鲜活万物品牌管理有限公司'), P('客户方','CNCellCenter'), P('百丽集团旗下鞋履品牌（待确认主体）')],
    [P('联系人','CNCellCenter'), P('苏薇'), P('联系人','CNCellCenter'), P('待填写')],
    [P('联系电话/邮箱','CNCellCenter'), P('15382320871'), P('联系电话/邮箱','CNCellCenter'), P('待填写')],
    [P('确认日期','CNCellCenter'), P(QUOTE_DATE_CN), P('确认日期','CNCellCenter'), P('待填写')],
]
confirm_t = Table(confirm_pdf, colWidths=[28*mm, 82*mm, 28*mm, 82*mm])
confirm_t.setStyle(TableStyle([
    ('GRID',(0,0),(-1,-1),0.5,grid), ('VALIGN',(0,0),(-1,-1),'MIDDLE'),
    ('BACKGROUND',(0,0),(0,-1),light), ('BACKGROUND',(2,0),(2,-1),light),
    ('LEFTPADDING',(0,0),(-1,-1),5), ('RIGHTPADDING',(0,0),(-1,-1),5),
    ('TOPPADDING',(0,0),(-1,-1),5), ('BOTTOMPADDING',(0,0),(-1,-1),5),
]))
story.append(confirm_t)
story.append(Spacer(1, 4*mm))
story.append(Paragraph('本报价单为商务沟通文件｜最终权利义务以双方正式合同为准', pdf_styles['CNFooter']))
pdf.build(story)

README.write_text(f'''# 百丽旗下鞋履品牌 × 敦煌美术研究所 IP 联名授权报价单

创建时间：{QUOTE_DATE_CN}

## 交付文件

- `deliverables/{DOCX_OUT.name}`
- `deliverables/{PDF_OUT.name}`

## 本版核心口径

- 授权期：自双方约定的产品正式销售日起计算6个月；开发期由双方另行协商确认，不计入6个月授权期；清货期：3个月，仅限销售授权期内已上架产品，可进行库存促销，不得新增设计、生产、上架或开展额外活动。
- 合作内容：联名鞋履产品开发，使用敦煌美术研究所授权图库；双方社交媒体同步宣发，敦煌美术研究所微博、公众号、视频号、快手、抖音、小红书账号矩阵联动。
- 图库诉求：20–25张，品牌当前意向以羊年图库为主。
- 方案A：保底授权费20万元；超额分成6%；保底销售额为20万元 ÷ 6% = 333.33万元；保底销售额内不再额外付费，超出部分按6%支付提成；超额分成每月结算一次；包含5–6款联名鞋履及5个礼赠周边细分品类开发权利。
- 方案B：40万元一口价；同样包含5–6款联名鞋履及5个礼赠周边细分品类开发权利；同款不同颜色视为一款；礼赠周边如小配饰、丝巾等常见文创产品品类，用于礼赠。

## 说明

报价单沿用此前GAP × 敦煌美术研究所报价单的商务蓝色表格风格，并更新为百丽鞋履品牌项目口径。
''', encoding='utf-8')

# ---------------- verify ----------------
with zipfile.ZipFile(DOCX_OUT) as z:
    xml = z.read('word/document.xml').decode('utf-8', errors='ignore')
    docx_text = re.sub(r'<[^>]+>', '', xml)
    media_files = [n for n in z.namelist() if n.startswith('word/media/')]
required = [
    '商务报价单', '百丽旗下鞋履品牌', '敦煌美术研究所', '6个月', '3个月', '开发期由双方另行协商确认，不计入6个月授权期', '产品正式销售日起计算6个月', '2027年1月1日', '2027年6月30日', '仅限销售授权期内已上架产品', '不得新增设计、生产、上架或开展额外活动', '特别定制快闪', '常规促销、折扣销售', '20–25张', '羊年图库',
    '保底授权费20万元', '超额分成比例6%', '333.33万元', '每月结算一次',
    '40万元', '5–6款', '同款不同颜色视为一款', '5个礼赠周边细分品类', '小配饰', '丝巾',
    '双方社交媒体同步宣发', '微博', '公众号', '视频号', '快手', '抖音', '小红书', 'Logo / 名称使用'
]
missing = [x for x in required if x not in docx_text]
forbidden = ['之前说的50万', '调整为40万', '用户', '提示词', 'TODO', 'placeholder', '可以说', '建议这样写', '不含；如需开发另议', '5种礼赠周边', '开发期计入授权期']
found_forbidden = [x for x in forbidden if x.lower() in docx_text.lower()]
if missing:
    raise SystemExit('Missing in DOCX: ' + json.dumps(missing, ensure_ascii=False))
if found_forbidden:
    raise SystemExit('Forbidden text in DOCX: ' + json.dumps(found_forbidden, ensure_ascii=False))
if not PDF_OUT.exists() or PDF_OUT.read_bytes()[:4] != b'%PDF':
    raise SystemExit('PDF invalid')
print(json.dumps({
    'docx': str(DOCX_OUT),
    'docx_size': DOCX_OUT.stat().st_size,
    'docx_media_files': media_files,
    'pdf': str(PDF_OUT),
    'pdf_size': PDF_OUT.stat().st_size,
    'pdf_header': PDF_OUT.read_bytes()[:8].decode('latin1'),
    'missing': missing,
    'forbidden_found': found_forbidden,
    'logo_used': LOGO.exists(),
    'project': str(PROJECT)
}, ensure_ascii=False, indent=2))
