from pathlib import Path
import json,shutil,importlib.util
from reportlab.pdfgen import canvas
from reportlab.lib.colors import HexColor
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.platypus import Paragraph
from reportlab.lib.styles import ParagraphStyle
from xml.sax.saxutils import escape
from pdf2image import convert_from_path

HERE=Path(__file__).resolve().parent;ROOT=HERE.parents[6]
OUT=ROOT/'oss/local-deliverables/2026-09-24-AI运动营养师小程序页面版'
DATA=json.loads((HERE/'content.json').read_text())
pdfmetrics.registerFont(TTFont('YaHei','/Users/jack/Library/Fonts/msyh.ttc'))
pdfmetrics.registerFont(TTFont('YaHeiBold','/Users/jack/Library/Fonts/msyhbd.ttc'))
W,H=595.276,841.89;BLUE='#287BF2';INK='#152735';GRAY='#617789';LIGHT='#F2F7FD'

class Surface:
 def __init__(self,c,h):self.c=c;self.h=h
 def rect(self,x,y,w,h,fill,r=0,stroke=None):
  c=self.c;c.setFillColor(HexColor(fill));c.setStrokeColor(HexColor(stroke or fill));c.setLineWidth(.6)
  c.roundRect(x,self.h-y-h,w,h,r,fill=1,stroke=bool(stroke))
 def text(self,t,x,y,w,size=12,color=INK,bold=False,leading=None,align=0):
  p=Paragraph(escape(t).replace('\n','<br/>'),ParagraphStyle('p',fontName='YaHeiBold' if bold else 'YaHei',fontSize=size,leading=leading or size*1.5,textColor=HexColor(color),wordWrap='CJK',alignment=align))
  _,ph=p.wrap(w,1000);p.drawOn(self.c,x,self.h-y-ph);return ph
 def line(self,x1,y1,x2,y2,color='#DDE7F2'):
  c=self.c;c.setStrokeColor(HexColor(color));c.setLineWidth(.6);c.line(x1,self.h-y1,x2,self.h-y2)
 def image(self,p,x,y,w,h):self.c.drawImage(str(p),x,self.h-y-h,width=w,height=h,mask='auto')
 def circle(self,x,y,r,fill,stroke=None):
  self.c.setFillColor(HexColor(fill));self.c.setStrokeColor(HexColor(stroke or fill));self.c.setLineWidth(1.5);self.c.circle(x,self.h-y,r,fill=1,stroke=bool(stroke))

def capsule(s,y=62):
 s.rect(288,y-15,72,30,'#FFFFFF',15)
 for x in [302,310,318]:s.circle(x,y,2,'#152735')
 s.circle(344,y,8,'#FFFFFF','#152735');s.circle(344,y,3,'#152735')

def screen_ai(s):
 s.rect(0,0,375,812,'#EEF3FF')
 s.text('09:41',21,13,80,12,bold=True);capsule(s)
 s.image(OUT/'assets/ai-male-bulking.png',7,48,110,110)
 s.text('Hi',130,58,80,29,bold=True)
 s.text('增重目标进行中',128,113,222,20,bold=True)
 s.text('今日训练推荐',18,171,258,22,bold=True)
 s.rect(18,210,339,104,'#FFFFFF',13)
 s.text('徒手深蹲加臀桥训练',33,222,307,20,bold=True)
 s.text('训练时间  25分钟',33,257,303,15,GRAY)
 s.text('活动热量  160 kcal',33,282,300,15,GRAY)
 s.rect(236,329,121,44,BLUE,14)
 s.text('午餐吃点啥',245,339,104,17,'#FFFFFF')
 s.rect(18,389,339,207,'#FFFFFF',13)
 s.text('AI饮食建议',32,405,290,14,'#547B70',True)
 s.text('午餐这样搭配',32,433,294,23,bold=True)
 s.rect(32,477,310,59,'#F5F9F9',9)
 s.text('主食 + 优质蛋白 + 蔬菜',43,493,291,17,bold=True)
 s.text('结合个人情况，选择合适搭配',33,551,302,14,GRAY)
 s.rect(18,610,339,59,'#FFF9E9',11)
 s.text('今天少选',33,618,300,15,bold=True)
 s.text('油炸食物  /  含糖饮品',33,642,300,14,'#957127')
 s.rect(18,682,135,33,'#FFFFFF',13);s.text('历史记录',35,688,104,14,'#376C9A',True)
 s.rect(164,682,121,33,'#FFFFFF',13);s.text('新聊天',187,688,95,14,'#376C9A',True)
 s.rect(18,727,339,48,'#FFFFFF',17)
 s.circle(39,751,13,'#FFFFFF',BLUE);s.rect(36,743,6,13,BLUE,3);s.line(39,756,39,761,BLUE)
 s.text('有什么问题需要问我吗～',59,742,228,13,GRAY)
 s.rect(295,743,21,16,'#FFFFFF',2,BLUE);s.rect(299,739,10,5,BLUE,1);s.circle(305.5,751,4,'#FFFFFF',BLUE)
 s.circle(340,750,14,BLUE);s.text('›',332,736,20,20,'#FFFFFF',True)
 for i,t in enumerate(['首页','订餐','AI','订单','我的']):s.text(t,i*75,787,75,13,BLUE,i==2,align=1)

def screen_archive(s):
 s.rect(0,0,375,812,'#F2F7FD')
 s.text('09:41',21,13,80,12,bold=True)
 s.text('‹',18,58,28,25)
 s.text('健康档案',82,63,210,22,bold=True,align=1)
 capsule(s,73)
 s.rect(0,111,375,42,'#FFFFFF',18)
 s.text('基本信息',8,122,69,12)
 s.rect(80,113,76,37,BLUE,18);s.text('体重数据',85,122,71,12,'#FFFFFF',True)
 s.text('健康数据',166,122,71,12);s.text('饮食管理',242,122,71,12);s.text('运动燃脂',320,123,54,11)
 def head(t,y):
  s.rect(18,y+6,4,20,BLUE,2);s.text(t,32,y,320,22,bold=True)
 head('体重数据',169)
 s.rect(18,211,339,79,'#FFFFFF',13)
 s.text('体重',35,221,230,17);s.text('70.0 kg',35,246,250,25,bold=True);s.text('›',325,236,23,27,GRAY)
 for x,title,val in [(18,'BMI','22.9'),(194,'体脂率','20.0%')]:
  s.rect(x,303,163,87,'#FFFFFF',12);s.text(title,x+16,312,141,17);s.text(val,x+16,343,141,25,bold=True)
 head('健康数据',412)
 s.rect(18,452,339,61,'#FFFFFF',13);s.text('健康状态及日常习惯',35,470,295,18,bold=True)
 head('饮食管理',537)
 s.rect(18,576,339,74,'#FFF8ED',13);s.text('热量摄入',35,585,245,16);s.text('1800 千卡',35,610,245,23,bold=True);s.text('›',326,600,23,27,GRAY)
 head('运动燃脂',672)
 s.rect(18,712,339,76,'#FFFFFF',13);s.text('热量消耗',35,722,245,16);s.text('240 千卡',35,747,245,23,bold=True);s.text('›',326,735,23,27,GRAY)

def phone(s,draw,x,y,w=188):
 h=w*812/375
 s.rect(x-4,y-4,w+8,h+8,'#162332',18)
 c=s.c;c.saveState();c.translate(x,s.h-y-h);c.scale(w/375,w/375)
 path=c.beginPath();path.roundRect(0,0,375,812,26);c.clipPath(path,stroke=0)
 draw(Surface(c,812));c.restoreState()
 return h

def page(d):
 target=OUT/'01-小程序页面展示版';target.mkdir(exist_ok=True)
 f=target/(d['file']+'_小程序页面版.pdf')
 c=canvas.Canvas(str(f),pagesize=(W,H),initialFontName='YaHei',initialFontSize=10)
 c.setTitle(d['title']+' '+d['file']+' 小程序页面版');c.setAuthor('康比特');s=Surface(c,H)
 s.rect(0,0,W,H,'#FFFFFF')
 s.image(OUT/'assets/cpt-logo.png',32,25,98,15.24)
 s.text('康比特AI智慧健康站',342,25,222,10,INK,True,align=2)
 s.text('健康体测一体机 + AI运动营养师' + (' · 渠道演示版' if d['id']=='02' else ''),32,61,531,10,GRAY)
 title='专业营养，\n就在你的小程序里。' if d['id']=='01' else '让一次体测，\n延伸为看得见的日常服务。'
 s.text(title,32,90,531,27,INK,True,leading=37)
 subtitle='问问AI，看见建议；打开档案，查看身体与吃动记录。' if d['id']=='01' else '用两张页面，讲清交付内容与后续服务触点。'
 s.text(subtitle,32,176,531,11,GRAY)
 s.rect(0,215,W,444,'#EAF1F9')
 phone(s,screen_ai,65,229);phone(s,screen_archive,342,229)
 s.text('AI运动营养师',65,644,188,10,BLUE,True,align=1)
 s.text('健康档案',342,644,188,10,BLUE,True,align=1)
 if d['id']=='01':
  left=[('AI饮食问答','从“午餐吃点啥”开始，查看饮食搭配建议。'),('目标与训练卡','当前目标、训练时间与活动热量集中展示。')]
  right=[('身体记录一处看','体重、BMI、体脂率及健康数据入口。'),('吃动信息有处查','热量摄入、消耗可查看，连接后续记录。')]
 else:
  left=[('演示一个日常问题','给客户看AI建议卡、历史记录与新聊天。'),('展示目标与训练卡','把手机端的服务内容讲清楚、演示出来。')]
  right=[('让检测有后续触点','以身体记录作为复测沟通的依据。'),('让吃动服务可展示','围绕摄入与消耗信息组织日常交流。')]
 for x,items in [(32,left),(315,right)]:
  for j,(title,body) in enumerate(items):
   y=681+j*43;s.text(title,x,y,248,11,INK,True);s.text(body,x,y+18,248,8.5,GRAY,leading=12)
 s.line(32,772,563,772)
 s.text('营养垂类大模型  |  25年专业积累 · 100支国家队服务经验',32,778,531,9,BLUE,True,leading=12)
 support='多源数据连接  |  百款硬件及华为运动健康、苹果健康生态（按支持清单接入）' if d['id']=='01' else '设备销售 + 营养品复购  |  健身瑜伽 · 健康管理 · 企业员工福利'
 s.text(support,32,794,531,8,GRAY,leading=10)
 note='依据实屏重绘、节选展示，数据均为示例。训练卡为目标导向推荐；功能与接入以交付配置为准，健康建议不替代诊疗。'
 if d['id']=='02':note+='合作按正式协议执行。'
 s.text(note,32,811,531,6.4,GRAY,leading=8)
 c.showPage();c.save()
 convert_from_path(str(f),dpi=200)[0].save(f.with_suffix('.png'));print(f)

def main():
 (OUT/'assets').mkdir(parents=True,exist_ok=True)
 shutil.copy2(ROOT/'oss/local-deliverables/2026-09-23-AI智慧健康站一页纸-v2-微软雅黑/qa/cpt-logo.png',OUT/'assets/cpt-logo.png')
 for name,draw in [('AI运动营养师_依据实屏重绘',screen_ai),('健康档案_示例数据重绘',screen_archive)]:
  p=OUT/'assets'/f'{name}.pdf';c=canvas.Canvas(str(p),pagesize=(375,812),initialFontName='YaHei',initialFontSize=12)
  draw(Surface(c,812));c.showPage();c.save();convert_from_path(str(p),dpi=180)[0].save(p.with_suffix('.png'))
 for d in DATA:page(d)
 spec=importlib.util.spec_from_file_location('v2',HERE.parent/'v2/build.py');v2=importlib.util.module_from_spec(spec);spec.loader.exec_module(v2)
 v2.OUT=OUT/'03-纯文字与可编辑版';v2.QA=v2.OUT/'qa';v2.HERE=HERE;v2.DATA=DATA;v2.QA.mkdir(parents=True,exist_ok=True);v2.build_text()

if __name__=='__main__':main()
