# -*- coding: utf-8 -*-
from pathlib import Path
import csv, json, datetime
from openpyxl import Workbook, load_workbook
from openpyxl.styles import Alignment, Font, PatternFill
from openpyxl.utils import get_column_letter

project=Path('/Users/bot1/Volumes/root_for_ai/AI工作区/通用_法务合同台账_合同存档_20260607_1036')
deliv=project/'deliverables'
docs=project/'docs'
base_csv=deliv/'合同总表_20260607.csv'
base_xlsx=deliv/'合同总表_20260607.xlsx'
md_path=deliv/'合同总表_20260607.md'
analysis_path=deliv/'新增合同法务辅助分析_20260607_第12-16份.md'
explain_path=deliv/'合同梳理说明_20260607.md'
manifest_path=docs/'source_files_manifest.json'
ocr_summary_path=docs/'ocr_summary.json'

cols=['序号','归档编号','签约/生效时间（OCR）','合同名称','合同类型','项目/IP','甲方','乙方','其他方/合作方','合约有效期/授权期限','授权/合作范围','金额/费用/分成','争议解决','风险/待核对','原始文件','OCR文本','SHA256','页数','OCR字符数']

def read_csv_rows(path):
    if not path.exists():
        return []
    with path.open('r', encoding='utf-8-sig', newline='') as f:
        return list(csv.DictReader(f))

def write_csv_rows(path, rows):
    with path.open('w', encoding='utf-8-sig', newline='') as f:
        w=csv.DictWriter(f, fieldnames=cols, extrasaction='ignore')
        w.writeheader()
        for r in rows:
            w.writerow({c:r.get(c,'') for c in cols})

rows=read_csv_rows(base_csv)
# normalize columns and remove previous retry entries for seq 12-16
norm=[]
for r in rows:
    rr={c:r.get(c,'') for c in cols}
    try:
        seq=int(str(rr.get('序号','')).strip())
    except Exception:
        seq=None
    if seq in range(12,17):
        continue
    norm.append(rr)

manifest=json.loads(manifest_path.read_text(encoding='utf-8'))
manifest_by_seq={int(m['seq']):m for m in manifest}
ocr_list=json.loads(ocr_summary_path.read_text(encoding='utf-8')) if ocr_summary_path.exists() else []
ocr_by_pdf={o.get('pdf'):o for o in ocr_list}

def meta(seq):
    m=manifest_by_seq[seq]
    pdf=Path(m['stored_pdf']).name
    return m, ocr_by_pdf.get(pdf,{})

def row(seq, archive_id, name, shop, plan, price, exposure, risk_extra):
    m,o=meta(seq)
    return {
        '序号':str(seq),
        '归档编号':archive_id,
        '签约/生效时间（OCR）':'协议自双方签章纸质/电子件时生效；如购买流程先于签章流程，则购买流程完成时视为生效；签章时间栏为空/手写日期未识别，需人工复核',
        '合同名称':name,
        '合同类型':'电视淘宝屏销宝技术服务协议/年度框架投放协议',
        '项目/IP':f'电视淘宝“屏销宝”2026年年框服务；店铺：{shop}；套餐：{plan}',
        '甲方':'杭州华数智屏信息技术有限公司（电视淘宝指定授权主体，OCR识别）',
        '乙方':'杭州万物有灵文化科技有限公司（客户/用户，OCR识别疑似，需核对主体全称）',
        '其他方/合作方':'淘宝/天猫平台、电视淘宝联盟网站/服务平台；品牌/商标授权见附件3',
        '合约有效期/授权期限':'服务投放周期：2026-01-01至2026-12-31；实际投放时间以双方指定联系人邮件/钉钉沟通为准；签章时间需人工核对',
        '授权/合作范围':f'电视淘宝依托屏销宝技术能力提供营销推广、商品/服务发布、匹配销售对象、形成交易行为等技术服务；用户授权电视淘宝在中国境内品牌/联合推广技术服务中使用其商标、商号、标识及说明文字；店铺：{shop}',
        '金额/费用/分成':f'技术服务套餐合同价/实际合同价：人民币{price}元（含6%增值税，OCR识别需核对）；付款方式：2025-12-31前一次性支付；承诺曝光量级约{exposure}万次（未含加赠流量，按淘宝后台统计，数据发布后30天内无异议视为认可）',
        '争议解决':'中华人民共和国大陆地区法律；协商不成，由杭州华数智屏信息技术有限公司住所地人民法院管辖',
        '风险/待核对':risk_extra,
        '原始文件':m['stored_pdf'],
        'OCR文本':o.get('ocr_text',f'work/extracted_text_ocr/{Path(m["stored_pdf"]).stem}.txt'),
        'SHA256':m['sha256'],
        '页数':str(m['pages']),
        'OCR字符数':str(o.get('chars',''))
    }

common_risk='中高风险：电视淘宝可修订协议/规则且继续使用视为接受；未达承诺曝光主要补齐曝光而非直接退款/赔偿；电视淘宝责任上限仅2万元，明显低于服务费；用户违约或被投诉时可能被下线/终止且不退费；电视淘宝可提前10日单方无理由中止/终止；曝光量以淘宝后台为准且30日无异议即认可；签章日期、客户主体、金额/曝光量需人工复核。'
new_rows=[
    row(12,'LC-20260607-012','电视淘宝屏销宝技术服务协议（菁华旗舰店2026年框60）','菁华旗舰店','2026年年框合作方案V2', '600,000.00', '1,306', common_risk+' 本份为菁华旗舰店60万V2方案。'),
    row(13,'LC-20260607-013','电视淘宝屏销宝技术服务协议（纺优美旗舰店2026年框30｜to电淘版）','arfum 纺优美旗舰店','2026年年框合作方案V1', '300,000.00', '627', common_risk+' 本份与第14份业务内容高度相似，疑似同一纺优美30万V1方案的不同扫描/签署版本，需确认最终有效版本，避免重复付款/重复入账。'),
    row(14,'LC-20260607-014','电视淘宝屏销宝技术服务协议（纺优美旗舰店2026年框30）','arfum 纺优美旗舰店','2026年年框合作方案V1', '300,000.00', '627', common_risk+' 本份与第13份业务内容高度相似，疑似同一纺优美30万V1方案的不同扫描/签署版本，需确认最终有效版本，避免重复付款/重复入账。'),
    row(15,'LC-20260607-015','电视淘宝屏销宝技术服务协议（LA官方旗舰店2026年框70｜to电淘版）','la 官方旗舰店','2026年年框合作方案V2', '700,000.00', '1,522', common_risk+' 本份为LA官方旗舰店70万V2方案，与第16份60万V2方案金额/曝光量不同，需确认哪一份为最终执行版本或是否存在追加/变更关系。'),
    row(16,'LC-20260607-016','电视淘宝屏销宝技术服务协议（LA官方旗舰店2026年框60）','la 官方旗舰店','2026年年框合作方案V2', '600,000.00', '1,306', common_risk+' 本份为LA官方旗舰店60万V2方案，与第15份70万V2方案金额/曝光量不同，需确认哪一份为最终执行版本或是否存在追加/变更关系。'),
]
all_rows=norm+new_rows
all_rows.sort(key=lambda r:int(str(r.get('序号','0')).strip() or 0))
write_csv_rows(base_csv, all_rows)

src_cols=['序号','展示文件名','原始缓存文件名','存档PDF','原生文本','OCR文本','SHA256','页数','原生文本字符数','OCR字符数','添加时间']
src_rows=[]
for m in sorted(manifest, key=lambda x:int(x.get('seq',0))):
    pdf=Path(m.get('stored_pdf','')).name
    o=ocr_by_pdf.get(pdf,{})
    src_rows.append({'序号':m.get('seq'),'展示文件名':m.get('display_name'),'原始缓存文件名':m.get('original'),'存档PDF':m.get('stored_pdf'),'原生文本':m.get('stored_text'),'OCR文本':o.get('ocr_text',''),'SHA256':m.get('sha256'),'页数':m.get('pages'),'原生文本字符数':m.get('chars'),'OCR字符数':o.get('chars',''),'添加时间':m.get('added_at')})

field_desc=[
    {'字段':'签约/生效时间（OCR）','说明':'根据PDF文本层/OCR识别；签署页、盖章页、手写日期一律需人工复核。'},
    {'字段':'合约有效期/授权期限','说明':'记录合同期限、代理/授权期限、续约或终止规则；如合同仅约定生效条件则说明。'},
    {'字段':'金额/费用/分成','说明':'仅摘录主交易口径；银行账号、税号等敏感信息不进入总表。'},
    {'字段':'风险/待核对','说明':'法务辅助风险提示，不替代执业律师意见；高风险合同建议律师最终确认。'},
    {'字段':'SHA256','说明':'用于校验原始PDF未被篡改或重复上传。'},
]
now=datetime.datetime.now().isoformat(timespec='seconds')
process_rows=[
    {'时间':now,'事项':'追加处理第12-16份合同','说明':'5份PDF均为扫描/弱文本层文件，使用PyMuPDF渲染页面并用macOS Vision OCR识别；已同步更新Excel/CSV/Markdown。'},
    {'时间':now,'事项':'可靠性提示','说明':'OCR可能误识别手写日期、印章、公司名、表格金额、曝光量小数点/千分位；台账中已标注需人工复核项。'},
    {'时间':now,'事项':'敏感信息处理','说明':'台账和分析摘要不列银行账号、联系人电话、税号等敏感信息；原始PDF/OCR文本按合同原件保存以便内部复核。'},
]

def add_sheet(wb, title, rows, fieldnames):
    ws=wb.create_sheet(title)
    ws.append(fieldnames)
    for r in rows:
        ws.append([r.get(c,'') for c in fieldnames])
    return ws

wb=Workbook()
wb.remove(wb.active)
add_sheet(wb,'合同总表',all_rows,cols)
add_sheet(wb,'源文件清单',src_rows,src_cols)
add_sheet(wb,'字段说明',field_desc,['字段','说明'])
add_sheet(wb,'处理说明',process_rows,['时间','事项','说明'])
for ws in wb.worksheets:
    ws.freeze_panes='A2'
    for cell in ws[1]:
        cell.font=Font(bold=True, color='FFFFFF')
        cell.fill=PatternFill('solid', fgColor='4F81BD')
        cell.alignment=Alignment(horizontal='center', vertical='center', wrap_text=True)
    for row_cells in ws.iter_rows(min_row=2):
        for cell in row_cells:
            cell.alignment=Alignment(vertical='top', wrap_text=True)
    for col in range(1, ws.max_column+1):
        letter=get_column_letter(col)
        max_len=0
        for cell in ws[letter]:
            val='' if cell.value is None else str(cell.value)
            max_len=max(max_len, min(len(val), 60))
        ws.column_dimensions[letter].width=max(10, min(max_len+2, 45))
    ws.auto_filter.ref=ws.dimensions
wb.save(base_xlsx)

md=['# 合同总表（截至2026-06-07）\n','> 说明：本表根据PDF文本层及OCR整理；扫描件中的签署日期、印章、手写内容、金额/曝光量表格均需以原件人工复核为准。银行账号、电话、税号等敏感信息不在本表列示。\n']
md.append('| ' + ' | '.join(cols) + ' |')
md.append('| ' + ' | '.join(['---']*len(cols)) + ' |')
def esc(x):
    s='' if x is None else str(x)
    return s.replace('|','｜').replace('\n','<br>')
for r in all_rows:
    md.append('| ' + ' | '.join(esc(r.get(c,'')) for c in cols) + ' |')
md_path.write_text('\n'.join(md)+'\n', encoding='utf-8')

analysis=f'''# 新增合同法务辅助分析（第12-16份：电视淘宝屏销宝年框）

处理时间：{now}

> 说明：以下为基于扫描件/OCR文本的法务辅助分析，不替代执业律师意见。签署日期、印章、客户主体、金额、曝光量等字段需以PDF原件人工复核为准。本文不列示银行账号、联系人电话、税号等敏感字段。

## 一、本批合同概况

本批5份均为《电视淘宝“屏销宝”技术服务协议-年框版》，服务方/电视淘宝指定授权主体为杭州华数智屏信息技术有限公司，客户主体OCR疑似为杭州万物有灵文化科技有限公司，均需人工核对签章页。

| 编号 | 店铺/品牌 | 套餐 | 合同价（OCR） | 承诺曝光量级（OCR） | 服务周期 |
|---|---|---|---:|---:|---|
| LC-20260607-012 | 菁华旗舰店 | V2 | 600,000.00元 | 约1,306万次 | 2026-01-01至2026-12-31 |
| LC-20260607-013 | arfum 纺优美旗舰店 | V1 | 300,000.00元 | 约627万次 | 2026-01-01至2026-12-31 |
| LC-20260607-014 | arfum 纺优美旗舰店 | V1 | 300,000.00元 | 约627万次 | 2026-01-01至2026-12-31 |
| LC-20260607-015 | la 官方旗舰店 | V2 | 700,000.00元 | 约1,522万次 | 2026-01-01至2026-12-31 |
| LC-20260607-016 | la 官方旗舰店 | V2 | 600,000.00元 | 约1,306万次 | 2026-01-01至2026-12-31 |

## 二、总体结论

**风险等级：中高。** 合同属于电视淘宝屏销宝年度技术服务/广告投放框架，金额较高且多数为一次性预付。文本对电视淘宝免责、单方调整、终止和责任上限较有利，对客户方存在明显履约保障不足风险。

## 三、主要风险点

1. **付款前置、退款/赔偿保障弱。** 附件约定在2025-12-31前一次性付款，但若电视淘宝未达承诺曝光，文本主要约定“后续活动补齐”，未明确现金退款、违约金或服务费减免机制。
2. **责任上限明显偏低。** 协议9.4约定任何情形下赔偿限额最高不超过2万元，远低于30万、60万、70万元的服务费。
3. **电视淘宝可单方调整规则。** 协议允许电视淘宝随时修订协议内容和技术服务规则，用户继续使用即视为接受。
4. **电视淘宝可提前10日单方无理由中止/终止。** 对客户年度投放计划稳定性不利，应要求明确已付款服务的退款/替代资源/赔偿安排。
5. **曝光数据确认窗口短。** 曝光量以淘宝后台统计为准，数据发布后30天内无异议即视为真实有效；需要建立每期投放数据留痕和复核机制。
6. **客户侧责任较重。** 商品/服务合法性、知识产权、第三方投诉、店铺资质、商标授权等均主要由客户承担，需确保品牌/商标授权链路完整。
7. **版本/重复性需核对。** 第13与第14份均为纺优美旗舰店30万V1方案，业务内容高度相似；第15与第16份均为LA官方旗舰店V2方案但金额/曝光量不同（70万/1,522万 vs 60万/1,306万），需确认最终有效版本、是否存在替换/补充关系，避免重复付款或台账重复确认。

## 四、建议处理

1. 签署或付款前，要求补充确认：未达曝光时的退款/抵扣/补投期限、补投资源规格、逾期补投违约责任。
2. 将2万元责任上限排除适用于：已收服务费退还、故意/重大过失、知识产权侵权、数据造假、未按约提供核心资源等情形。
3. 对电视淘宝单方终止条款增加：已付款未履行部分按比例退还，且不影响客户要求补偿实际损失。
4. 建立投放验收表：每期活动名称、投放时间、资源位、曝光量、后台截图、异议截止日、对账确认人。
5. 对纺优美两份、LA两份做版本确认：标注“最终签署版/作废版/补充版”，并在付款审批中只依据最终有效版本。
6. 涉及品牌商标授权附件时，核对商标权属或转授权文件，避免因品牌标识授权不足引发赔偿。

## 五、需人工复核项

- 签章页客户主体全称、签章日期；
- 各附件金额、曝光量小数点/千分位，尤其“1.306/1.522”应按原件确认为1,306万/1,522万或其他口径；
- 第13/14、第15/16之间是否为重复扫描、替换版本或不同执行合同；
- 是否已经实际付款及发票状态。
'''
analysis_path.write_text(analysis, encoding='utf-8')

marker='## 2026-06-07 追加处理：第12-16份合同（电视淘宝屏销宝年框）'
old=explain_path.read_text(encoding='utf-8') if explain_path.exists() else '# 合同梳理说明\n'
append=f'''\n\n{marker}\n\n- 新增并处理5份电视淘宝“屏销宝”技术服务协议年框合同：`12_菁华电淘2026年框60.pdf`、`13_纺优美to电淘.pdf`、`14_纺优美电淘2026年框30.pdf`、`15_LA to电淘.pdf`、`16_LA电淘2026年框60.pdf`。\n- 五份PDF均为扫描/弱文本层文件，已使用macOS Vision OCR生成可检索文本。\n- 合同总表已更新至16条记录，本批分析见：`deliverables/新增合同法务辅助分析_20260607_第12-16份.md`。\n- 注意：本批金额、曝光量、签章日期、客户主体均为OCR识别，需以PDF原件人工复核；衍生台账未列银行账号、电话、税号等敏感字段。\n'''
if marker in old:
    old=old[:old.index(marker)].rstrip()+append
else:
    old=old.rstrip()+append
explain_path.write_text(old+'\n',encoding='utf-8')

# Update README summary lightly.
readme=project/'README.md'
rt=readme.read_text(encoding='utf-8') if readme.exists() else '# 法务合同台账与合同存档\n'
line='- 当前台账：截至2026-06-07已整理16份合同；第12-16份为电视淘宝屏销宝2026年框合同，OCR字段需人工复核。'
if '- 当前台账：' in rt:
    parts=[]
    for l in rt.splitlines():
        if l.startswith('- 当前台账：'):
            parts.append(line)
        else:
            parts.append(l)
    rt='\n'.join(parts)+'\n'
else:
    rt=rt.rstrip()+'\n'+line+'\n'
readme.write_text(rt,encoding='utf-8')

print('UPDATED_12_16')
print('rows', len(all_rows))
print('source_manifest_count', len(manifest))
print('ocr_summary_count', len(ocr_list))
print('xlsx', base_xlsx, base_xlsx.stat().st_size)
print('csv', base_csv, base_csv.stat().st_size)
print('md', md_path, md_path.stat().st_size)
print('analysis', analysis_path, analysis_path.stat().st_size)
for r in all_rows[-5:]:
    print(r['序号'], r['归档编号'], r['合同名称'], r['金额/费用/分成'][:100])
