# -*- coding: utf-8 -*-
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont, ImageFilter, ImageOps
import textwrap, math, json, shutil

BASE = Path('/Users/qianliyun/Documents/aiwork/电商详情页/20260511_转良运冰箱贴_详情页_v01')
RAW = BASE/'00_原始资料'
IMG = RAW/'images'
BRAND = RAW/'brand'
FONT_DIR = RAW/'fonts'
MAKE = BASE/'04_页面制作'
OUT = BASE/'05_导出长图'
SEG = OUT/'分段图_v01'
for d in [MAKE, OUT, SEG]: d.mkdir(parents=True, exist_ok=True)

W = 1440
M = 96
CARD_R = 42

# Fonts
font_title_path = FONT_DIR/'喜鹊招牌体_简繁体.ttf'
font_body_path = FONT_DIR/'喜鹊聚珍体.ttf'
fallback = '/System/Library/Fonts/PingFang.ttc'

def font(size, title=False):
    p = font_title_path if title and font_title_path.exists() else font_body_path
    if not p.exists(): p = fallback
    try: return ImageFont.truetype(str(p), size)
    except Exception: return ImageFont.truetype(fallback, size)

F = {
    'hero': font(104, True),
    'h1': font(78, True),
    'h2': font(56, True),
    'body': font(48, False),
    'small': font(40, False),
    'tag': font(38, True),
    'num': font(32, True),
}

COL = {
    'red':'#7C241E','red2':'#9E3A2E','green':'#1F5E4C','green2':'#386F5F','gold':'#C7963D','gold2':'#E0C181','cream':'#F7EBD6','paper':'#F3E4C8','ink':'#362B22','muted':'#7B6043','dark':'#221A17'
}

def hex_to_rgb(h):
    h=h.lstrip('#'); return tuple(int(h[i:i+2],16) for i in (0,2,4))

def text_size(draw, text, ft):
    b = draw.textbbox((0,0), text, font=ft)
    return b[2]-b[0], b[3]-b[1]

def wrap_cn(text, max_w, ft):
    lines=[]
    for para in text.split('\n'):
        cur=''
        for ch in para:
            test=cur+ch
            if text_size(ImageDraw.Draw(Image.new('RGB',(1,1))), test, ft)[0] <= max_w:
                cur=test
            else:
                if cur: lines.append(cur)
                cur=ch
        if cur: lines.append(cur)
    return lines

def draw_multiline(draw, xy, text, ft, fill, max_w, line_gap=14, align='left'):
    x,y=xy
    lines=wrap_cn(text, max_w, ft)
    for line in lines:
        tw,th=text_size(draw,line,ft)
        xx=x if align=='left' else x+(max_w-tw)//2
        draw.text((xx,y), line, font=ft, fill=fill)
        y += th + line_gap
    return y

def gradient_bg(h, top, bottom):
    top=hex_to_rgb(top); bottom=hex_to_rgb(bottom)
    im=Image.new('RGB',(W,h),top)
    px=im.load()
    for y in range(h):
        t=y/max(h-1,1)
        c=tuple(int(top[i]*(1-t)+bottom[i]*t) for i in range(3))
        for x in range(W): px[x,y]=c
    return im

def add_pattern(im, color='#C7963D', alpha=38, step=120):
    overlay=Image.new('RGBA', im.size, (0,0,0,0))
    d=ImageDraw.Draw(overlay)
    c=hex_to_rgb(color)+(alpha,)
    w,h=im.size
    for x in range(-step, w+step, step):
        for y in range(-step, h+step, step):
            d.arc([x,y,x+70,y+70], 0, 270, fill=c, width=3)
            d.rectangle([x+38,y+38,x+58,y+58], outline=c, width=2)
    return Image.alpha_composite(im.convert('RGBA'), overlay).convert('RGB')

def rounded(im, radius=42):
    mask=Image.new('L', im.size, 0)
    d=ImageDraw.Draw(mask)
    d.rounded_rectangle([0,0,im.size[0],im.size[1]], radius=radius, fill=255)
    out=Image.new('RGBA', im.size, (0,0,0,0))
    out.paste(im.convert('RGBA'), (0,0), mask)
    return out

def fit_img(path, size, mode='cover'):
    im=Image.open(path).convert('RGB')
    if mode=='contain':
        bg=Image.new('RGB', size, '#F8F0E2')
        im.thumbnail(size, Image.Resampling.LANCZOS)
        bg.paste(im, ((size[0]-im.width)//2,(size[1]-im.height)//2))
        return bg
    return ImageOps.fit(im, size, method=Image.Resampling.LANCZOS, centering=(0.5,0.5))

def photo_section(path, title, body, theme='paper', crop_h=1500, note=None):
    bg_col = COL['paper'] if theme=='paper' else (COL['red'] if theme=='red' else COL['green'])
    fg = COL['ink'] if theme=='paper' else '#FFF7E7'
    sub = COL['muted'] if theme=='paper' else '#EED7A6'
    h = 260 + crop_h + 190
    im = Image.new('RGB',(W,h), bg_col)
    im = add_pattern(im, color=COL['gold'], alpha=22 if theme=='paper' else 34, step=150)
    d=ImageDraw.Draw(im)
    d.text((M,72), title, font=F['h1'], fill=fg)
    y = draw_multiline(d, (M,160), body, F['body'], sub, W-2*M, line_gap=16)
    ph = fit_img(path, (W-2*M, crop_h), 'cover')
    im.paste(rounded(ph, CARD_R), (M, 250), rounded(ph, CARD_R))
    if note:
        d.rounded_rectangle([M, 250+crop_h+42, W-M, 250+crop_h+126], radius=28, fill='#FFFFFF' if theme=='paper' else '#F8E4BD')
        draw_multiline(d,(M+38,250+crop_h+61), note, F['small'], COL['ink'], W-2*M-76, line_gap=8)
    return im

def text_section(title, eyebrow, body, theme='cream', h=900):
    im=gradient_bg(h, COL['cream'] if theme=='cream' else COL['green'], COL['paper'] if theme=='cream' else '#173E34')
    im=add_pattern(im, color=COL['gold'], alpha=30, step=140)
    d=ImageDraw.Draw(im)
    y=100
    d.text((M,y), eyebrow, font=F['tag'], fill=COL['gold'])
    y+=72
    y=draw_multiline(d,(M,y),title,F['h1'], COL['red'] if theme=='cream' else '#FFF5DD', W-2*M, line_gap=10)
    y+=36
    draw_multiline(d,(M,y),body,F['body'], COL['ink'] if theme=='cream' else '#F4DEB4', W-2*M, line_gap=18)
    return im

def concat(sections):
    total=sum(img.height for _, img in sections)
    canvas=Image.new('RGB',(W,total),COL['cream'])
    y=0; bounds=[]
    for name, s in sections:
        canvas.paste(s,(0,y))
        bounds.append({'name':name,'top':y,'height':s.height})
        y+=s.height
    return canvas,bounds

# Prepare paths
p = lambda n: IMG/n
img01=p('产品图_01_img_84fadbc7dc2a.jpg')
img02=p('产品图_02_img_2a453fbe6f27.jpg')
img03=p('产品图_03_img_818b9a15c181.jpg')
img04=p('产品图_04_img_43c89ae7bfd9.jpg')
img05=p('产品图_05_img_141f94da24b0.jpg')
img06=p('产品图_06_img_83bfc15dd82e.jpg')
img07=p('产品图_07_img_6ff338a5fffd.jpg')
img08=p('产品图_08_img_306059e7b41d.jpg')
img09=p('产品图_09_img_70beef335659.jpg')
img10=p('产品图_10_img_311647a40c43.jpg')
img11=p('产品图_11_img_9dd14758e19b.jpg')
img12=p('产品图_12_img_a5f1b539be3f.jpg')
img13=p('产品图_13_img_c86945d3bcec.jpg')

sections=[]
# top brand strip
strip_path=BRAND/'top_strip_1_01.jpg'
if strip_path.exists():
    strip=Image.open(strip_path).convert('RGB')
    ratio=W/strip.width
    strip=strip.resize((W, int(strip.height*ratio)), Image.Resampling.LANCZOS)
else:
    strip=Image.new('RGB',(W,120),COL['dark'])
sections.append(('00_top_brand', strip))

# Hero: vertical, text top, image large; real logo only
hero_h=1880
hero=gradient_bg(hero_h, '#641C18', '#173E34')
hero=add_pattern(hero, color=COL['gold'], alpha=42, step=170)
d=ImageDraw.Draw(hero)
# logo
logo_path=BRAND/'liangzhu_logo_thirdrow_first_ai_hires.png'
if logo_path.exists():
    logo=Image.open(logo_path).convert('RGBA')
    logo.thumbnail((190,190), Image.Resampling.LANCZOS)
    hero.paste(logo, (W-M-logo.width,70), logo)
d.text((M,92), '一转良运', font=F['hero'], fill='#FFF4D8')
d.text((M,220), '好事发生', font=F['hero'], fill='#E7C276')
y=360
draw_multiline(d,(M,y),'良渚文明｜可转可贴的好运小物\n把五千年前的文明祝福，轻轻转到今天的生活里。',F['body'],'#F5DEC0',W-2*M-220,line_gap=18)
ph=fit_img(img10,(W-2*M,1180),'cover')
hero.paste(rounded(ph,46),(M,610),rounded(ph,46))
# small bottom tag
d.rounded_rectangle([M, 1810, W-M, 1872], radius=31, fill=hex_to_rgb('#F3E4C8'))
d.text((M+42,1818),'红绿双款 · 圆转如意 · 礼赠有心',font=F['small'], fill=COL['red'])
sections.append(('01_hero', hero))

sections.append(('02_story', text_section('五千年玉礼，转入日常好运', 'DESIGN INSPIRATION', '灵感取自良渚玉琮与神人兽面纹，将五千年前的文明符号，化作可转、可贴、可送的日常好运小物。\n\n它不只是冰箱贴，也是一枚可以被轻轻拨动的祝福：转一转，愿好事发生；贴一贴，把良运留在身边。', 'cream', 980)))

sections.append(('03_double_color', photo_section(img01, '双款好运', '红色喜庆，绿色典雅。两种色彩承接不同心意，自用、送礼都合适。', 'paper', 1500, '红绿双款同框展示，正式页保持单列大图，手机端浏览更清楚。')))
sections.append(('04_color_choice', photo_section(img03, '红与绿，各有好寓意', '一屏一张大图展示，不做密集横向卡片；让颜色、造型与礼赠氛围先被看见。', 'green', 1420)))
sections.append(('05_green', photo_section(img04, '绿色款｜典雅如玉', '玉石绿沉静耐看，金色纹样点亮细节，适合偏雅致的日常空间。', 'paper', 1500)))
sections.append(('06_red', photo_section(img07, '红色款｜喜庆有礼', '陶红色温暖醒目，节庆、乔迁、伴手礼都带着好寓意。', 'red', 1500)))
sections.append(('07_detail_red', photo_section(img06, '金纹入画', '细密金色纹样与层叠外形共同呈现，近看更显精致。', 'paper', 1540, '工艺细节使用真实照片放大展示，不用 AI 参考图替代。')))
sections.append(('08_detail_green', photo_section(img08, '良渚神纹', '取意良渚文明纹样，把古意融入可贴、可转、可收藏的日常小物。', 'green', 1540)))
sections.append(('09_magnet', photo_section(img05, '强磁稳贴', '背部磁吸结构清楚可见，适合吸附在冰箱、柜门等金属表面。', 'paper', 1500, '功能说明以真实图片为准，不额外夸大材质或参数。')))
sections.append(('10_hand_green', photo_section(img09, '手持尺寸感', '小巧精致，拿在手里刚刚好；放在厨房、书桌、办公区，都能点亮一角。', 'green', 1500)))
sections.append(('11_hand_red', photo_section(img11, '礼赠有心', '一枚好运小物，轻巧不失仪式感；送朋友、送家人，也送自己一份好寓意。', 'paper', 1500)))
sections.append(('12_package', photo_section(img02, '精装有礼', '独立包装呈现，到手更有仪式感。适合收藏，也适合作为良渚文化的小小伴手礼。', 'red', 1500)))
sections.append(('13_close', photo_section(img13, '一枚好运小物，收藏一份良渚祝福', '可转、可贴、可送，把文明纹样变成日常里的小小仪式感。', 'paper', 1500)))
sections.append(('14_params', text_section('产品信息', 'PRODUCT INFO', '产品名称：转良运冰箱贴\n产品款式：红色款 / 绿色款\n产品用途：冰箱贴、金属面装饰、文创礼赠\n设计灵感：良渚玉琮与神人兽面纹\n温馨提示：尺寸、材质、重量等参数如需上线展示，需以后续确认资料为准，不在本版中臆造。', 'cream', 860)))

long_img, bounds = concat(sections)
long_path = OUT/'转良运冰箱贴_详情页_完整长图_1440_v01.png'
long_img.save(long_path, optimize=True)

# Split by section, not fixed height
seg_files=[]
for i,b in enumerate(bounds,1):
    crop=long_img.crop((0,b['top'],W,b['top']+b['height']))
    fp=SEG/f'转良运冰箱贴_详情页_分段_{i:02d}_{b["name"]}_1440_v01.png'
    crop.save(fp, optimize=True)
    seg_files.append(str(fp))

# Thumbnail index for verification
thumb_w=360
scale=thumb_w/W
thumbs=[]
for i,b in enumerate(bounds,1):
    crop=long_img.crop((0,b['top'],W,b['top']+b['height']))
    th=crop.resize((thumb_w, max(1,int(crop.height*scale))), Image.Resampling.LANCZOS)
    label_h=42
    lab=Image.new('RGB',(thumb_w,label_h),'#1F1A16')
    dd=ImageDraw.Draw(lab)
    dd.text((10,8),f'{i:02d} {b["name"]}  {crop.height}px',font=font(16),fill='#F2D8A5')
    thumbs.append((lab,th))
idx_h=sum(l.height+t.height+12 for l,t in thumbs)+20
idx=Image.new('RGB',(thumb_w+40,idx_h),'#EEE0C8')
y=10
for lab,th in thumbs:
    idx.paste(lab,(20,y)); y+=lab.height
    idx.paste(th,(20,y)); y+=th.height+12
index_path=OUT/'转良运冰箱贴_详情页_分段缩略索引_v01.jpg'
idx.save(index_path, quality=90)

# Write HTML reference with vertical structure
html_path=MAKE/'detail_vertical_v01.html'
rel=lambda path: Path(path).relative_to(MAKE).as_posix() if Path(path).is_absolute() else str(path)
# safer use relative to html directory
html_imgs = [
('00_top_brand', BRAND/'top_strip_1_01.jpg'),('01_hero', img10),('03_double_color', img01),('04_color_choice', img03),('05_green', img04),('06_red', img07),('07_detail_red', img06),('08_detail_green', img08),('09_magnet', img05),('10_hand_green', img09),('11_hand_red', img11),('12_package', img02),('13_close', img13)]
style = '''<style>
@font-face{font-family:ZhaoPai;src:url('../00_原始资料/fonts/喜鹊招牌体_简繁体.ttf')}@font-face{font-family:JuZhen;src:url('../00_原始资料/fonts/喜鹊聚珍体.ttf')}*{box-sizing:border-box}body{margin:0;background:#f3e4c8;color:#362b22;font-family:JuZhen,serif}.page{width:1440px;margin:0 auto}.section{padding:96px;background:#f3e4c8}.section.dark{background:#1f5e4c;color:#fff4d8}.section.red{background:#7c241e;color:#fff4d8}.brand{padding:0}.brand img{width:100%;display:block}.logo{position:absolute;right:96px;top:70px;width:180px}.hero{position:relative;background:linear-gradient(#641c18,#173e34);color:#fff4d8;padding:92px 96px 70px}.hero h1{font-family:ZhaoPai;font-size:104px;line-height:1.15;margin:0;color:#fff4d8}.hero h1 span{color:#e7c276}.hero p,.section p{font-size:38px;line-height:1.65;margin:24px 0}.section h2{font-family:ZhaoPai;font-size:78px;line-height:1.15;margin:0 0 24px;color:#7c241e}.dark h2,.red h2{color:#fff4d8}.section img.photo,.hero img.photo{width:100%;border-radius:42px;display:block;margin-top:46px}.eyebrow{font-family:ZhaoPai;color:#c7963d;font-size:34px;margin-bottom:24px}.note{background:#fff7e7;color:#362b22;padding:28px 38px;border-radius:28px;font-size:30px;margin-top:42px}.info p{white-space:pre-line}</style>'''
body=['<!doctype html><html><head><meta charset="utf-8"><title>转良运冰箱贴详情页 v01</title>'+style+'</head><body><main class="page">']
body.append('<section class="section brand"><img src="../00_原始资料/brand/top_strip_1_01.jpg"></section>')
body.append('<section class="hero"><img class="logo" src="../00_原始资料/brand/liangzhu_logo_thirdrow_first_ai_hires.png"><h1>一转良运<br><span>好事发生</span></h1><p>良渚文明｜可转可贴的好运小物<br>把五千年前的文明祝福，轻轻转到今天的生活里。</p><img class="photo" src="../00_原始资料/images/产品图_10_img_311647a40c43.jpg"></section>')
# concise html modules
mods=[('cream','五千年玉礼，转入日常好运','灵感取自良渚玉琮与神人兽面纹，将五千年前的文明符号，化作可转、可贴、可送的日常好运小物。',''),('cream','双款好运','红色喜庆，绿色典雅。两种色彩承接不同心意，自用、送礼都合适。','产品图_01_img_84fadbc7dc2a.jpg'),('dark','红与绿，各有好寓意','单列大图展示，不做密集横向卡片；让颜色、造型与礼赠氛围先被看见。','产品图_03_img_818b9a15c181.jpg'),('cream','绿色款｜典雅如玉','玉石绿沉静耐看，金色纹样点亮细节。','产品图_04_img_43c89ae7bfd9.jpg'),('red','红色款｜喜庆有礼','陶红色温暖醒目，节庆、乔迁、伴手礼都带着好寓意。','产品图_07_img_6ff338a5fffd.jpg'),('cream','金纹入画','细密金色纹样与层叠外形共同呈现，近看更显精致。','产品图_06_img_83bfc15dd82e.jpg'),('dark','良渚神纹','取意良渚文明纹样，把古意融入日常小物。','产品图_08_img_306059e7b41d.jpg'),('cream','强磁稳贴','背部磁吸结构清楚可见，适合吸附在冰箱、柜门等金属表面。','产品图_05_img_141f94da24b0.jpg'),('dark','手持尺寸感','小巧精致，拿在手里刚刚好。','产品图_09_img_70beef335659.jpg'),('cream','礼赠有心','送朋友、送家人，也送自己一份好寓意。','产品图_11_img_9dd14758e19b.jpg'),('red','精装有礼','独立包装呈现，到手更有仪式感。','产品图_02_img_2a453fbe6f27.jpg'),('cream','一枚好运小物，收藏一份良渚祝福','可转、可贴、可送，把文明纹样变成日常里的小小仪式感。','产品图_13_img_c86945d3bcec.jpg')]
for theme,title,txt,img in mods:
    cls='section '+({'dark':'dark','red':'red'}.get(theme,''))
    body.append(f'<section class="{cls}"><h2>{title}</h2><p>{txt}</p>')
    if img: body.append(f'<img class="photo" src="../00_原始资料/images/{img}">')
    body.append('</section>')
body.append('<section class="section info"><div class="eyebrow">PRODUCT INFO</div><h2>产品信息</h2><p>产品名称：转良运冰箱贴\n产品款式：红色款 / 绿色款\n产品用途：冰箱贴、金属面装饰、文创礼赠\n设计灵感：良渚玉琮与神人兽面纹\n温馨提示：尺寸、材质、重量等参数如需上线展示，需以后续确认资料为准，不在本版中臆造。</p></section>')
body.append('</main></body></html>')
html_path.write_text('\n'.join(body), encoding='utf-8')

manifest={'long_path':str(long_path),'index_path':str(index_path),'segment_dir':str(SEG),'segments':seg_files,'bounds':bounds,'html_path':str(html_path),'size':long_img.size}
(MAKE/'build_manifest_v01.json').write_text(json.dumps(manifest,ensure_ascii=False,indent=2),encoding='utf-8')
print(json.dumps(manifest,ensure_ascii=False,indent=2))
