from pathlib import Path
from copy import copy
import json,hashlib
from openpyxl import Workbook,load_workbook
from openpyxl.styles import Font,PatternFill,Alignment
from openpyxl.comments import Comment
from openpyxl.utils import get_column_letter
from openpyxl.worksheet.pagebreak import Break
from openpyxl.worksheet.datavalidation import DataValidation
from openpyxl.drawing.image import Image

P=Path(__file__).resolve().parent;R=P.parents[3];O=P/'output';D=O/'build';D.mkdir(exist_ok=True)
C=json.loads((O/'content.json').read_text());PROJ=json.loads((O/'projects-classified.json').read_text())
NAVY='193A4D';BLUE='216F92';GRAY='536574';LIGHT='EDF4F7';ORANGE='A36027';TEAL='227B70';INPUT='FFF6DC'
SCENES=['政企','学校','医疗','社区康养','部队','竞技体育']

def cell(s,r,a,rr,b,text='',bg=None,color=GRAY,bold=False,comment=None):
 s.merge_cells(start_row=r,start_column=a,end_row=rr,end_column=b);c=s.cell(r,a,text)
 c.font=Font(name='微软雅黑',size=12,color=color,bold=bold);c.alignment=Alignment(vertical='center',wrap_text=True,indent=1)
 if bg:
  for row in s.iter_rows(min_row=r,max_row=rr,min_col=a,max_col=b):
   for z in row:z.fill=PatternFill('solid',fgColor=bg)
 if comment:c.comment=Comment(comment,'来源/填写规则')
 return c

def create(w,name):
 s=w.create_sheet(name);s.sheet_view.showGridLines=False;s.sheet_view.zoomScale=85
 for n in range(1,13):s.column_dimensions[get_column_letter(n)].width=13
 s.freeze_panes='A5';s.sheet_properties.pageSetUpPr.fitToPage=True
 s.page_setup.orientation='landscape';s.page_setup.paperSize=s.PAPERSIZE_A4;s.page_setup.fitToWidth=1;s.page_setup.fitToHeight=0
 s.page_margins.left=s.page_margins.right=.25;s.page_margins.top=s.page_margins.bottom=.25
 return s

def page(s,title,subtitle):
 k=getattr(s,'pages',0);s.pages=k+1;r=1+28*k
 for n in range(r,r+28):s.row_dimensions[n].height=20
 if k:s.row_breaks.append(Break(id=r-1))
 cell(s,r,1,r+1,12,title,NAVY,'FFFFFF',True);cell(s,r+2,1,r+3,12,subtitle,LIGHT)
 cell(s,r+27,1,r+27,12,'康比特数字技术中心｜主汇报4页、6分钟；细节在项目台账与每周填报',color=GRAY)
 s.print_area=f'A1:L{r+27}';return r

def inputcell(s,r,a,rr,b,value,placeholder,template,comment=None):
 return cell(s,r,a,rr,b,placeholder if template else value,INPUT if template else None,'0000FF' if template else GRAY,comment=comment)

def table(w,name,headers,rows,widths):
 s=w.create_sheet(name);s.freeze_panes='C2';s.sheet_view.showGridLines=False
 s.append(headers)
 for row in rows:s.append(row)
 for i,x in enumerate(widths,1):s.column_dimensions[get_column_letter(i)].width=x
 for c in s[1]:c.font=Font(name='微软雅黑',size=12,bold=True,color='FFFFFF');c.fill=PatternFill('solid',fgColor=BLUE);c.alignment=Alignment(wrap_text=True,vertical='center')
 s.row_dimensions[1].height=38
 for row in s.iter_rows(min_row=2):
  s.row_dimensions[row[0].row].height=68
  for c in row:
   c.font=Font(name='微软雅黑',size=12);c.alignment=Alignment(vertical='top',wrap_text=True)
 s.auto_filter.ref=s.dimensions
 return s

for template in [False,True]:
 w=Workbook();w.remove(w.active)
 s=create(w,'01汇报重点');r=page(s,'方向｜以营养健康形成差异化，用六场景沉淀可复制产品','固定模板｜填写本周周期与三条结论；项目底账为9/21基线' if template else C['meeting']+'｜复盘9/14—20，安排9/21—27')
 s.unmerge_cells('A3:L4')
 inputcell(s,3,1,4,12,C['meeting']+'｜复盘9/14—20，安排9/21—27','填写：会议日期 / 地点 / 回顾周期 / 下周周期',template)
 for i,(label,value,note) in enumerate(C['metrics']):
  a=1+i*4;b=a+3;cell(s,6,a,6,b,label,BLUE,'FFFFFF',True)
  inputcell(s,7,a,9,b,value,'填写：目标 / 实际或预计 / 口径',template,note)
 cell(s,11,1,12,12,'= "组合底账："&COUNTA(项目台账!A2:A1000)&"条记录；已归六场景"&COUNTIF(项目台账!B2:B1000,"<>待归类")-COUNTBLANK(项目台账!B2:B1000)&"条"',LIGHT,BLUE,True)
 # Avoid open-ended COUNTA/COUNTBLANK ambiguity by exact populated-range formulas below.
 s['A11']='="组合底账："&COUNTA(项目台账!A2:A35)&"条记录；其中待归类"&COUNTIF(项目台账!B2:B35,"待归类")&"条（非独立项目数）"'
 s['A11'].font=Font(name='微软雅黑',size=12,color=BLUE,bold=True);s['A11'].alignment=Alignment(vertical='center',wrap_text=True)
 for i,(title,body) in enumerate(C['conclusions']):
  q=14+i*4;cell(s,q,1,q,3,title,BLUE,'FFFFFF',True)
  inputcell(s,q,4,q+2,12,body,'填写：本周改变了什么 → 对业务的意义 → 下一验证',template)
 cell(s,26,1,27,12,'每周聚焦一条方向，用本周事实和下一步验证支持结论。' if template else '会议判断：从卖设备走向营养健康服务；AI营养师平台化是目标，先验证用户持续使用与前后反馈。',LIGHT,TEAL)

 s=create(w,'02六场景')
 for n in range(2):
  r=page(s,f'六场景｜项目、软件、硬件与本周变化（{n+1}/2）','软硬件是关联项目的范围汇总，不是每个项目的统一标配；名称/数量与完整映射见项目台账。')
  for j,c in enumerate(C['scenes'][n*3:n*3+3]):
   q=r+4+j*7
   cell(s,q,1,q,8,c['name']+'｜'+c['direction'].replace('方向：',''),BLUE,'FFFFFF',True)
   f='=COUNTIF(项目台账!B2:B1000,"'+c['name']+'")&"条记录"'
   cell(s,q,9,q,12,f,BLUE,'FFFFFF',True)
   cell(s,q+1,1,q+2,12,'项目｜'+c['projects'],LIGHT,comment='9/21项目总表；跨业务营养样板补充。其他售前机会不计入底账数量。')
   cell(s,q+3,1,q+4,6,'软件｜'+c['software'],comment='功能状态按项目核对，不代表全部已上线。')
   cell(s,q+3,7,q+4,12,'硬件｜'+c['hardware'],comment='型号数量缺失写待补；哈尔滨数量来自合同，不代表本周新增安装。')
   inputcell(s,q+5,1,q+6,12,'本周｜'+c['change']+'\n'+c['next'],'本周变化：____；验证/差距：____；下一步：____',template,'每场景只填写一条主要变化。最多两行；细节放每周填报。')

 s=create(w,'03复盘与资源');r=page(s,'复盘｜做对了什么、需要改进什么、资源投向哪里','从案例得出决策；建议动作需责任团队确认后排期，不代替客户或管理层承诺。')
 bands=[(1,2,'工作主线'),(3,6,'做对了什么'),(7,10,'需要改进什么'),(11,12,'协同团队')]
 for a,b,t in bands:cell(s,5,a,5,b,t,BLUE,'FFFFFF',True)
 for i,row in enumerate(C['review']):
  q=6+i*4
  cell(s,q,1,q+3,2,row[0],LIGHT,BLUE,True)
  inputcell(s,q,3,q+3,6,row[1],'填写：事实与可复用成果',template)
  inputcell(s,q,7,q+3,10,row[2],'填写：主要缺口与纠偏',template)
  inputcell(s,q,11,q+3,12,row[3],'填写团队',template)
 inputcell(s,23,1,25,12,'下周建议优先级：①验证营养闭环、明确学校/政企标准范围；②落实十一现场与研发资源；③筛出软件增项、设备增订机会。','填写：最多3项优先动作 + 负责人 + 日期 + 验证结果',template)
 cell(s,26,1,27,12,'管理层需确认：先做哪些样板、投入多少人、哪些定制收费、哪些机会暂缓。',LIGHT,ORANGE,True)

 rows=[]
 for i in PROJ:
  rows.append([i['name'],i['scene'],i['group'],i['type'],i['software'],i['hardware'],i['stage'],i['status'],i['owner'],i['gap'],i['evidence'],'在线总表第'+str(i['source_row'])+'条'])
 ps=table(w,'项目台账',['项目/合同记录','主场景','原业务组别','原项目类型','软件功能清单','硬件设备清单','源表阶段','源表状态','交付负责人','待核项','证据/时点','源记录位置'],rows,[30,15,20,15,60,65,15,15,20,55,45,18])
 dv=DataValidation(type='list',formula1='"'+','.join(SCENES+['待归类'])+'"');ps.add_data_validation(dv);dv.add('B2:B1000')
 ps['A1'].comment=Comment('冻结范围：32条团餐业务中心记录，加哈尔滨社区与山西运动员餐厅；未去重为独立项目。原表另23条不混入。新增记录追加在末尾，不能覆盖既有行。','范围')
 week=C['weekly'] if not template else [['','','','','','','','','','','','','',''] for _ in range(12)]
 ws=table(w,'每周填报',['场景','项目/模块','工作类型','软件/硬件对象','上期状态','本周事实/变化','证据状态','共性能力/方向','验证指标','差距/边界','下周可验收成果','责任团队','计划日期','来源'],week,[15,25,17,25,24,60,20,28,30,35,40,25,20,45])
 val=DataValidation(type='list',formula1='"'+','.join(SCENES+['全部','待归类'])+'"');ws.add_data_validation(val);val.add('A2:A1000')
 if template:
  for row in ws.iter_rows(min_row=2):
   for c in row:c.fill=PatternFill('solid',fgColor=INPUT);c.font=Font(name='微软雅黑',size=12,color='0000FF')

 money=json.loads((O/'financial.json').read_text())
 if template:
  for row in money:
   for ix in range(1,6):
    if not (isinstance(row[ix],str) and row[ix].startswith('=')):row[ix]=None
   row[6]='本周待填写';row[7]='填写期间、指标性质、来源；不同口径不混算'
 ms=table(w,'经营口径',['指标/范围','目标（金额万元）','已报值（金额万元/另注明）','预计（万元）','已报比例/计算比例','预计比例','来源','口径说明'],money,[28,23,30,22,25,23,27,65])
 for r in range(2,11):
  for col in ['E','F']:ms[f'{col}{r}'].number_format='0.0%'
 ms['A12']='金额、收入、签约、发货、回款不可互换；原文与手记冲突保留两值。';ms['A12'].font=Font(name='微软雅黑',size=12)
 guide=[
 ['每周只做四步','复制本模板，以会议日期命名；在每周填报逐项记事实；提炼到主汇报浅黄色格；校对经营口径后导出前4页。'],
 ['固定主汇报','01三条方向与经营；02六场景各一条变化，软件/硬件分栏；03做对/改进/资源。共4页，建议1+3+2分钟。'],
 ['范围与场景','主场景按终端用户与服务场景；渠道公司不能直接归政企。每个项目记录只归一类，采购/实施重复待合同核对。'],
 ['项目底账','项目台账为9/21冻结基线。新项目在末尾追加；软件功能与硬件型号/数量分别填。空白写待补，不继承其他项目清单。'],
 ['状态与新增','复制后清空本周变化、经营数字、日期与行动；旧内容只作上期基线。实施完成、现场验证、验收、增订、回款分别填。'],
 ['软件字段','功能/版本、设计/开发/测试/上线/验证、通用/定制、支持接口、本周变化、证据。场景范围汇总不等于每项目标配。'],
 ['硬件字段','设备类型/型号、数量与单位、来源版本、到货/安装/联调、备件/供应商、本周变化。合同清单不是安装完成证明。'],
 ['每周填报示例','软件：政企营养/开发完成→现场验证→复测前后反馈；硬件：体检机/接入→真实用户使用→数据与设备验收。'],
 ['汇总方法','本模板不自动生成管理结论；浅黄色格可直接改写，或把每周填报交给Codex按此固定结构更新。原始事实与来源不能省略。'],
 ['统计公式','场景条数自动按项目台账B列计算；条数只是记录数。主范围公式、汇总条件和去重口径随新增记录检查。'],
 ['健康与经营','体检—饮食—复测链路不等于证明健康改善；平台化与会员服务是待验证方向。营养品推荐及营养服务不能直接写已形成收入。'],
 ['导出检查','主报打印范围：01一页、02两页、03一页。其他Sheet用于编辑备查；导出PDF只选前4页。字体微软雅黑12，蓝字黄底为周更新格。']]
 table(w,'填写说明',['项目','规则'],guide,[25,110])
 # Exact formulas use all potential rows and ignore empty cells; captions remain stable.
 w['01汇报重点']['A11']='="组合底账："&COUNTA(项目台账!A2:A1000)&"条记录；其中待归类"&COUNTIF(项目台账!B2:B1000,"待归类")&"条（非独立项目数）"'
 for s in w:
  for row in s:
   for c in row:
    f=copy(c.font);f.name='微软雅黑';f.sz=12;c.font=f
 w.active=0
 name='六场景周报_固定填写模板.xlsx' if template else '数科事业部周报_20260921_六场景方向版.xlsx'
 w.save(D/name);print(name,len(w.worksheets),sum(len(s._images) for s in w))
