#!/usr/bin/env python3
"""
小红书内页渲染引擎 v2 — 海报构图思维
用法: python3 render_xhs_v2.py
产出: ~/.hermes/articles/YYYY-MM-DD/选题1-xxx/
"""

from PIL import Image, ImageDraw, ImageFont
import os
from datetime import datetime, timezone, timedelta

CST = timezone(timedelta(hours=8))
TODAY = datetime.now(CST)

# ═══ 画布配置 ═══
W, H = 1242, 1660
PAD_X = 80
CONTENT_W = W - 2 * PAD_X

# ═══ 色彩 ═══
DARK = (40, 90, 55)
ORANGE = (230, 126, 34)
TEXT = (30, 60, 40)
TINT = (220, 238, 226)
TINT_ORANGE = (252, 235, 215)
MID = (100, 155, 115)
WHITE = (255, 255, 255)

# ═══ 字体路径 ═══
FONT_DIR = os.path.expanduser("~/project/brand")
TITLE_FONT = os.path.join(FONT_DIR, "YouSheBiaoTiHei-2.ttf")
HEITI = "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc"
INNER_BG = os.path.expanduser("~/project/brand/内页.png")
COVER_BG = os.path.expanduser("~/project/brand/小红书封面.jpeg")

def _font(size, bold=False, title=False):
    path = TITLE_FONT if title else HEITI
    return ImageFont.truetype(path, size)

F_DISPLAY  = lambda: _font(96, title=True)
F_H1       = lambda: _font(60, title=True)
F_H2       = lambda: _font(38, bold=True)
F_BODY     = lambda: _font(32)
F_CAPTION  = lambda: _font(24)

XS, SM, MD, LG, XL, XXL = 8, 16, 24, 40, 64, 96

def _center_x(bb_w):
    return (W - bb_w) // 2

def _wrap(draw, text, font, max_w):
    lines, current = [], ""
    for ch in text:
        test = current + ch
        if draw.textbbox((0,0), test, font=font)[2] > max_w and current:
            lines.append(current); current = ch
        else:
            current = test
    if current: lines.append(current)
    return lines

def _draw_brand_bar(draw):
    font = _font(24)
    brand = "广东高职高考信息网"
    bb = draw.textbbox((0,0), brand, font=font)
    draw.rectangle([(0,0), (W,72)], fill=DARK)
    draw.text((_center_x(bb[2]-bb[0]), (72-(bb[3]-bb[1]))//2), brand, font=font, fill=WHITE)

def _draw_footer(draw):
    font = _font(22)
    text = f"广东高职高考信息网 | {TODAY.strftime('%Y.%m.%d')}"
    bb = draw.textbbox((0,0), text, font=font)
    draw.text((_center_x(bb[2]-bb[0]), H-55), text, font=font, fill=MID)

def _draw_page_title(draw, title, page_num, total_pages):
    font_h1, font_cap = F_H1(), F_CAPTION()
    if total_pages > 1:
        pn = f"{page_num}/{total_pages}"
        bb = draw.textbbox((0,0), pn, font=font_cap)
        draw.text((W-PAD_X-(bb[2]-bb[0]), 82), pn, font=font_cap, fill=MID)
    bb = draw.textbbox((0,0), title, font=font_h1)
    ty = 135
    draw.text((_center_x(bb[2]-bb[0]), ty), title, font=font_h1, fill=DARK)
    line_y = ty + (bb[3]-bb[1]) + 20
    draw.rectangle([(PAD_X, line_y), (W-PAD_X, line_y+3)], fill=DARK)
    draw.ellipse([(PAD_X+20, line_y-4), (PAD_X+34, line_y+7)], fill=ORANGE)
    return line_y + LG

def _block_label(draw, y, text, icon="●"):
    font = F_CAPTION()
    draw.ellipse([(PAD_X+4, y+6), (PAD_X+12, y+14)], fill=ORANGE)
    draw.text((PAD_X+20, y+4), f"{icon} {text}", font=font, fill=MID)
    return y + SM

def block_stat_cards(draw, y, cards):
    n = len(cards)
    card_w, card_h = (CONTENT_W - SM)//2, 170
    for i, card in enumerate(cards[:4]):
        col, row = i%2, i//2
        cx, cy = PAD_X+col*(card_w+SM), y+row*(card_h+SM)
        is_first = (i==0)
        bg = TINT_ORANGE if is_first else TINT
        border = ORANGE if is_first else DARK
        num_color = ORANGE if is_first else DARK
        draw.rounded_rectangle([(cx+4,cy+4),(cx+card_w+4,cy+card_h+4)], radius=14, fill=(210,228,205))
        draw.rounded_rectangle([(cx,cy),(cx+card_w,cy+card_h)], radius=14, fill=bg, outline=border, width=2)
        draw.rectangle([(cx+14,cy+10),(cx+card_w-14,cy+22)], fill=border)
        num = card.get("num","")
        font_d = F_DISPLAY()
        bb = draw.textbbox((0,0), num, font=font_d)
        draw.text((cx+(card_w-(bb[2]-bb[0]))//2, cy+35), num, font=font_d, fill=num_color)
        label = card.get("label","")
        if label:
            fl = _font(26)
            bb = draw.textbbox((0,0), label, font=fl)
            draw.text((cx+(card_w-(bb[2]-bb[0]))//2, cy+35+(bb[3]-bb[1])+6), label, font=fl, fill=TEXT)
        desc = card.get("desc","")
        if desc:
            fd = _font(22)
            bb = draw.textbbox((0,0), desc, font=fd)
            draw.text((cx+(card_w-(bb[2]-bb[0]))//2, cy+card_h-30), desc, font=fd, fill=MID)
    rows = (min(n,4)+1)//2
    return y + rows*(card_h+SM)

def block_steps(draw, y, steps):
    font_h, font_b, r, gap, cx = F_H2(), F_BODY(), 20, 80, PAD_X+60
    for i, step in enumerate(steps[:5], 1):
        cy = y + (i-1)*gap
        if i>1: draw.line([(cx, y+(i-2)*gap+r+3), (cx, cy-r-3)], fill=MID, width=1)
        fill = ORANGE if i%2==1 else DARK
        draw.ellipse([(cx-r,cy-r),(cx+r,cy+r)], fill=fill)
        ns = str(i)
        bb = draw.textbbox((0,0), ns, font=font_h)
        draw.text((cx-(bb[2]-bb[0])//2, cy-(bb[3]-bb[1])//2+2), ns, font=font_h, fill=WHITE)
        title = step.get("title","")
        bb_t = draw.textbbox((0,0), title, font=font_h)
        draw.text((cx+45, cy-(bb_t[3]-bb_t[1])//2), title, font=font_h, fill=TEXT)
        desc = step.get("desc","")
        if desc:
            bb_d = draw.textbbox((0,0), desc, font=font_b)
            draw.text((cx+45+(bb_t[2]-bb_t[0])+16, cy-(bb_d[3]-bb_d[1])//2), desc, font=font_b, fill=MID)
    return y + len(steps[:5])*gap

def block_checklist(draw, y, items):
    font_h, font_b, sq, gap, sx = F_H2(), F_BODY(), 24, 64, PAD_X+60
    for i, item in enumerate(items[:6]):
        cy = y + i*gap
        bg = TINT if i%2==0 else WHITE
        draw.rectangle([(PAD_X, cy-gap//2+4), (W-PAD_X, cy+gap//2-4)], fill=bg)
        outline = ORANGE if i%2==1 else DARK
        draw.rectangle([(sx-sq//2,cy-sq//2),(sx+sq//2,cy+sq//2)], fill=WHITE, outline=outline, width=3)
        check_color = ORANGE if i%2==1 else DARK
        draw.line([(sx-6,cy),(sx-2,cy+5),(sx+7,cy-6)], fill=check_color, width=3)
        title = item.get("title","")
        bb = draw.textbbox((0,0), title, font=font_h)
        draw.text((sx+40, cy-(bb[3]-bb[1])//2), title, font=font_h, fill=TEXT)
        desc = item.get("desc","")
        if desc:
            bb_d = draw.textbbox((0,0), desc, font=font_b)
            draw.text((sx+40+(bb[2]-bb[0])+16, cy-(bb_d[3]-bb_d[1])//2), desc, font=font_b, fill=MID)
    return y + len(items[:6])*gap

def block_insight(draw, y, text):
    font_b = F_BODY()
    lines = _wrap(draw, text, font_b, CONTENT_W-80)
    bh = len(lines)*44+40
    draw.rounded_rectangle([(PAD_X+10,y),(W-PAD_X-10,y+bh)], radius=12, fill=WHITE, outline=ORANGE, width=3)
    draw.ellipse([(PAD_X+30,y+16),(PAD_X+42,y+28)], fill=ORANGE)
    draw.text((PAD_X+48, y+12), "💡 核心洞察", font=F_CAPTION(), fill=ORANGE)
    dy = y+18
    for line in lines:
        draw.text((PAD_X+48, dy), line, font=font_b, fill=TEXT)
        dy += 44
    return y+bh+LG

def block_urgent(draw, y, text):
    """紧急提示条 — 全宽橙色底白字，大面积橙色（评分关键）"""
    font_b = F_BODY()
    lines = _wrap(draw, text, font_b, CONTENT_W-60)
    bh = len(lines)*48+48
    draw.rounded_rectangle([(PAD_X,y),(W-PAD_X,y+bh)], radius=12, fill=ORANGE)
    dy = y+24
    for line in lines:
        bb = draw.textbbox((0,0), line, font=font_b)
        draw.text((_center_x(bb[2]-bb[0]), dy), line, font=font_b, fill=WHITE)
        dy += 48
    return y+bh+LG

def render_page(page_def, output_dir, page_num, total_pages):
    base = Image.open(INNER_BG).convert("RGBA")
    draw = ImageDraw.Draw(base)
    _draw_brand_bar(draw)
    y = _draw_page_title(draw, page_def["title"], page_num, total_pages)
    for sec in page_def.get("sections", []):
        stype = sec.get("type","")
        if stype == "label":
            y = _block_label(draw, y, sec.get("text",""), sec.get("icon","●"))
            continue
        if stype == "stats":
            y = block_stat_cards(draw, y, sec.get("cards",[]))
            y += LG; continue
        if stype == "steps":
            y = _block_label(draw, y, "操作步骤", "📋")
            y = block_steps(draw, y, sec.get("steps",[]))
            y += LG; continue
        if stype == "checklist":
            y = _block_label(draw, y, "行动清单", "✅")
            y = block_checklist(draw, y, sec.get("items",[]))
            y += LG; continue
        if stype == "insight":
            y = block_insight(draw, y, sec.get("text",""))
            continue
        if stype == "urgent":
            y = block_urgent(draw, y, sec.get("text",""))
            continue
    _draw_footer(draw)
    base = base.convert("RGB")
    fname = f"inner-{page_num:02d}.png"
    filepath = os.path.join(output_dir, fname)
    base.save(filepath, "PNG", quality=95)
    print(f"  📄 内页{page_num}/{total_pages}: {filepath}")
    return filepath

def render_cover(topic, output_dir):
    base = Image.open(COVER_BG).convert("RGBA")
    draw = ImageDraw.Draw(base)
    font_title = _font(155, title=True)
    font_em = _font(145, title=True)
    font_tag = _font(50)
    lines = [
        (topic["行1"], font_title, WHITE),
        (topic["行2"], font_title, WHITE),
        (topic["行3"], font_em, ORANGE),
    ]
    y = 380
    for text, font, color in lines:
        bb = draw.textbbox((0,0), text, font=font)
        draw.text((_center_x(bb[2]-bb[0]), y), text, font=font, fill=color)
        y += (bb[3]-bb[1]) + 75
    tag = topic.get("标签","考生服务")
    if isinstance(tag, list): tag = tag[0] if tag else "考生服务"
    tag_text = f"【{tag}】"
    bb = draw.textbbox((0,0), tag_text, font=font_tag)
    draw.text((_center_x(bb[2]-bb[0]), y-75+100), tag_text, font=font_tag, fill=WHITE)
    base = base.convert("RGB")
    filepath = os.path.join(output_dir, "cover.png")
    base.save(filepath, "PNG", quality=95)
    print(f"  🖼️ 封面: {filepath}")
    return filepath


# ═══ 使用示例 ═══
if __name__ == "__main__":
    # 编辑以下内容来生成你的选题
    TODAY_STR = TODAY.strftime("%Y-%m-%d")
    OUTPUT = os.path.expanduser(f"~/.hermes/articles/{TODAY_STR}/选题1-示例")
    os.makedirs(OUTPUT, exist_ok=True)

    # 封面
    topic = {
        "行1": "封面标题行1",
        "行2": "封面标题行2",
        "行3": "橙色强调行3",
        "标签": "考生服务",
    }
    render_cover(topic, OUTPUT)

    # 内页
    pages = [
        {
            "title": "页面标题",
            "sections": [
                {"type": "label", "text": "数据来源说明"},
                {"type": "stats", "cards": [
                    {"num": "91%", "label": "数据1", "desc": "说明文字"},
                    {"num": "79%", "label": "数据2", "desc": "说明文字"},
                ]},
                {"type": "insight", "text": "洞察文字..."},
            ]
        },
        {
            "title": "第二页标题",
            "sections": [
                {"type": "steps", "steps": [
                    {"title": "第一步", "desc": "具体做法"},
                    {"title": "第二步", "desc": "具体做法"},
                ]},
                {"type": "urgent", "text": "紧急提示文字（橙色底白字，大面积橙色）"},
            ]
        },
    ]

    for i, page in enumerate(pages, 1):
        render_page(page, OUTPUT, i, len(pages))

    print(f"\n✅ 完成！输出: {OUTPUT}/")
