#!/usr/bin/env python3.12
"""按「禁人物」规则重新生成指定配图（2026-09-24 用户指出后新增）

与既有脚本的区别：
  1. prompt 里的人名**物理删除**（person_name_filter.sanitize）
  2. 叠加**明确无人指令**（不是"不出现肖像"这种软约束，而是要求空镜/无人场景）
     —— 因为「Patagonia 集市」「濮院秀场」这类标题即便无人名，模型也会自动画人群

用法：
  python3.12 scripts/regen_no_person.py json/0925data.json images/0925/ brand_hotspots_1 brand_hotspots_2 ...
"""
import io
import json
import os
import sys
import urllib.request

from PIL import Image

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from person_name_filter import sanitize  # noqa: E402

API_URL = "https://ark.cn-beijing.volces.com/api/v3/images/generations"
API_KEY = "ark-563a4210-d804-47fa-a37a-40962192119b-ce6db"
MODEL = "doubao-seedream-4-0-250828"

# 明确无人指令（2026-10-03 按 image-rules §3 改写）：
# ⚠️ 旧写法「不得出现任何人物、人脸、人影与人体局部」是**否定式清单**，实测会**召唤**剪影
#    （2026-10-02：4 版里 3 版出现背景人影）。改为**纯正面描述「无人空镜 + 物体清单」**。
NO_HUMAN_CLAUSE = (
    "。画面为无人空镜场景，只呈现产品、车辆、建筑、道具与品牌元素，"
    "写实静物商业摄影"
)


def build_prompt(title, summary):
    t = sanitize(title) or '该品牌与产品的营销活动现场'
    s = sanitize(summary or '')
    base = f"配图：{t}"
    if s:
        base += f"，{s[:60]}"
    return base + "，新闻配图风格，写实，横向构图，16:9，高清" + NO_HUMAN_CLAUSE


def generate(prompt):
    payload = json.dumps({
        "model": MODEL, "prompt": prompt, "size": "1280x720", "n": 1,
        "response_format": "url",
    }).encode('utf-8')
    req = urllib.request.Request(
        API_URL, data=payload,
        headers={'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json'})
    with urllib.request.urlopen(req, timeout=180) as r:
        data = json.loads(r.read().decode('utf-8'))
    url = data['data'][0]['url']
    raw = urllib.request.urlopen(url, timeout=120).read()
    return Image.open(io.BytesIO(raw)).convert('RGB')


def strip_bottom_watermark(im, px=60):
    """裁掉豆包自带水印（底部 px 像素）"""
    w, h = im.size
    if h > px:
        return im.crop((0, 0, w, h - px))
    return im


def main():
    if len(sys.argv) < 4:
        print(__doc__)
        return 1
    json_path, img_dir = sys.argv[1], sys.argv[2]
    targets = sys.argv[3:]

    data = json.load(open(json_path, encoding='utf-8'))
    # 兼容两种外层结构：dict（最终文件）与 list-of-sections（yes_data / origin_data）
    # 2026-09-26 修复：此前只支持 dict，传 yes_data（list）会
    # AttributeError: 'list' object has no attribute 'get'
    if isinstance(data, list):
        data = {s['name']: s['list'] for s in data
                if isinstance(s, dict) and 'name' in s}
    SEC = ['brand_hotspots', 'vehicle_hotspots', 'social_hotspots',
           'hyundai_buzz_topics_domestic', 'hyundai_buzz_topics_international']

    # 建立 文件名 -> item 映射
    idx = {}
    for k in SEC:
        for i, it in enumerate(data.get(k, [])):
            idx[f'{k}_{i}.jpg'] = (k, i, it)

    for name in targets:
        fname = name if name.endswith('.jpg') else name + '.jpg'
        if fname not in idx:
            print(f'  ❌ 未找到条目: {fname}')
            continue
        k, i, it = idx[fname]
        title, summary = it.get('title', ''), it.get('summary', '')
        prompt = build_prompt(title, summary)
        print(f'  [{k}[{i}]] {fname}')
        print(f'    prompt: {prompt[:110]}…')
        try:
            im = generate(prompt)
            im = strip_bottom_watermark(im)
            # 统一 750 宽（与既有成品一致）
            w, h = im.size
            if w != 750:
                im = im.resize((750, round(h * 750 / w)), Image.LANCZOS)
            out = os.path.join(img_dir, fname)
            im.save(out, 'JPEG', quality=90)
            print(f'    ✅ 已写入 {out} {im.size} {os.path.getsize(out)//1024}KB')
        except Exception as e:
            print(f'    ❌ 生成失败: {e}')
    return 0


if __name__ == '__main__':
    sys.exit(main())
