# -*- coding: utf-8 -*-
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
from playwright.sync_api import sync_playwright
import json, shutil

BASE = Path('/Users/qianliyun/Documents/aiwork/电商详情页/20260511_转良运冰箱贴_详情页_v01')
MAKE = BASE / '04_页面制作'
OUT = BASE / '05_导出长图'
HTML = MAKE / 'detail.html'
OUT.mkdir(parents=True, exist_ok=True)

VERSION = 'v02'
W = 1440
CHUNK_H = 2200
long_path = OUT / f'转良运冰箱贴_详情页_{VERSION}.png'
preview_path = OUT / f'转良运冰箱贴_详情页_{VERSION}_preview.jpg'
first_path = OUT / f'转良运冰箱贴_详情页_{VERSION}_firstscreen.jpg'
last_path = OUT / f'转良运冰箱贴_详情页_{VERSION}_末屏检查.jpg'
index_path = OUT / f'转良运冰箱贴_详情页_{VERSION}_分屏检查索引.jpg'
manifest_path = MAKE / f'export_manifest_{VERSION}.json'
tile_dir = OUT / f'_tiles_{VERSION}'
if tile_dir.exists():
    shutil.rmtree(tile_dir)
tile_dir.mkdir(parents=True, exist_ok=True)

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True, executable_path='/Applications/Google Chrome.app/Contents/MacOS/Google Chrome')
    page = browser.new_page(viewport={'width': W, 'height': CHUNK_H}, device_scale_factor=1)
    page.goto(HTML.as_uri(), wait_until='networkidle')
    page.evaluate("document.fonts && document.fonts.ready")
    metrics = page.evaluate('''() => {
      const pageEl = document.querySelector('.page');
      const sections = [...document.querySelectorAll('section')].map((s,i)=>({
        i, name: s.dataset.name || '', cls: String(s.className),
        top: s.offsetTop, height: s.offsetHeight, bottom: s.offsetTop + s.offsetHeight,
        scrollHeight: s.scrollHeight
      }));
      const abs = [...document.querySelectorAll('*')]
        .filter(el => getComputedStyle(el).position === 'absolute')
        .map(el => { const r = el.getBoundingClientRect(); const pr = pageEl.getBoundingClientRect(); return {tag: el.tagName, cls: String(el.className), top: Math.round(r.top - pr.top), bottom: Math.round(r.bottom - pr.top)}; });
      return {width: pageEl.offsetWidth, pageHeight: pageEl.scrollHeight, docHeight: document.documentElement.scrollHeight, sections, absoluteElements: abs};
    }''')
    total_h = int(metrics['pageHeight'])

    # Chrome/Playwright full_page screenshot can repeat/mis-stitch very tall pages on this setup.
    # Export by viewport tiles and stitch with Pillow so the final 16k+px bottom area is faithful.
    tiles = []
    y = 0
    i = 1
    while y < total_h:
        h = min(CHUNK_H, total_h - y)
        page.set_viewport_size({'width': W, 'height': h})
        page.evaluate('(yy) => window.scrollTo(0, yy)', y)
        page.wait_for_timeout(120)
        actual = page.evaluate('() => window.scrollY')
        tile_path = tile_dir / f'tile_{i:02d}_{y}_{y+h}.png'
        page.screenshot(path=str(tile_path), full_page=False)
        tiles.append({'index': i, 'top': y, 'height': h, 'bottom': y + h, 'path': str(tile_path), 'actual_scrollY': actual})
        y += h
        i += 1
    browser.close()

# Stitch tiles
canvas = Image.new('RGB', (W, int(metrics['pageHeight'])), '#ffffff')
for t in tiles:
    tile = Image.open(t['path']).convert('RGB')
    # Browser viewport screenshot should be W*h. Crop just in case OS/browser adds a row.
    tile = tile.crop((0, 0, W, t['height']))
    canvas.paste(tile, (0, t['top']))
canvas.save(long_path, optimize=True)
im = canvas

# Preview: width 360
preview_w = 360
preview_h = max(1, int(im.height * preview_w / im.width))
im.resize((preview_w, preview_h), Image.Resampling.LANCZOS).save(preview_path, quality=88, optimize=True)

# First screen
im.crop((0, 0, W, min(CHUNK_H, im.height))).save(first_path, quality=92, optimize=True)

# Last two screens / bottom check crop
last_top = max(0, im.height - CHUNK_H * 2)
im.crop((0, last_top, W, im.height)).save(last_path, quality=92, optimize=True)

# Fixed-height screen index for checking. Avoid creating useless tiny residual panels.
thumb_w = 360
label_h = 54
font_path = '/System/Library/Fonts/PingFang.ttc'
try:
    font = ImageFont.truetype(font_path, 24)
except Exception:
    font = ImageFont.load_default()
chunks = []
y = 0
while y < im.height:
    h = min(CHUNK_H, im.height - y)
    if chunks and h < 180:
        prev_y, prev_h = chunks[-1]
        chunks[-1] = (prev_y, prev_h + h)
        break
    chunks.append((y, h))
    y += h
thumbs = []
for idx, (top, h) in enumerate(chunks, 1):
    crop = im.crop((0, top, W, top + h))
    th_h = max(1, int(h * thumb_w / W))
    th = crop.resize((thumb_w, th_h), Image.Resampling.LANCZOS)
    lab = Image.new('RGB', (thumb_w, label_h), '#151515')
    d = ImageDraw.Draw(lab)
    d.text((12, 12), f'{idx:02d}  {top}-{top+h}  h={h}', fill='#F3D28A', font=font)
    thumbs.append((lab, th))
cols = 2
pad = 24
col_w = thumb_w
row_items = []
for i in range(0, len(thumbs), cols):
    row = thumbs[i:i+cols]
    row_h = max(l.height + t.height for l, t in row)
    row_items.append((row, row_h))
idx_w = cols * col_w + (cols + 1) * pad
idx_h = pad + sum(rh + pad for _, rh in row_items)
idx_img = Image.new('RGB', (idx_w, idx_h), '#EEE0C8')
ypos = pad
for row, rh in row_items:
    xpos = pad
    for lab, th in row:
        idx_img.paste(lab, (xpos, ypos))
        idx_img.paste(th, (xpos, ypos + label_h))
        xpos += col_w + pad
    ypos += rh + pad
idx_img.save(index_path, quality=90, optimize=True)

manifest = {
    'version': VERSION,
    'html': str(HTML),
    'export_method': 'viewport tiles stitched by Pillow; avoids Chrome full_page repeat/mis-stitch on very tall screenshots',
    'long_path': str(long_path),
    'preview_path': str(preview_path),
    'firstscreen_path': str(first_path),
    'lastscreen_check_path': str(last_path),
    'index_path': str(index_path),
    'size': {'width': im.width, 'height': im.height},
    'last_check_crop': {'top': last_top, 'bottom': im.height},
    'chunks': [{'index': i+1, 'top': top, 'height': h, 'bottom': top+h} for i, (top, h) in enumerate(chunks)],
    'tiles': tiles,
    'browser_metrics': metrics,
}
manifest_path.write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding='utf-8')
print(json.dumps(manifest, ensure_ascii=False, indent=2))
