#!/usr/bin/env python3
"""
提示词迭代测试脚本
直接调用视觉模型测试 identify_pieces 的提示词效果
"""
import base64, json, os, sys

# 加载环境
sys.path.insert(0, os.path.dirname(__file__))
from dotenv import load_dotenv
load_dotenv()

from openai import OpenAI
from app.core.config import settings

# ==================== 配置 ====================

GARMENT_IMG = "debug_images/1770525898_input_0.jpg"  # 成衣照片
CANVAS_IMG = "debug_images/1770525898_input_1.jpg"   # 裁片轮廓

LAYERS_INFO = """组 1 (group, 4120x5280px, 位于路径: /组 1)
图层 1 (LayerKind.NORMAL, 4120x5280px, 位于路径: /组 1/图层 1)
组 2 (group, 3999x4561px, 位于路径: /组 2)
图层 2 (LayerKind.NORMAL, 3999x4561px, 位于路径: /组 2/图层 2)
组 3 (group, 3529x333px, 位于路径: /组 3)
图层 3 (LayerKind.NORMAL, 3529x333px, 位于路径: /组 3/图层 3)
组 4 (group, 2423x2004px, 位于路径: /组 4)
图层 4 (LayerKind.NORMAL, 2423x2004px, 位于路径: /组 4/图层 4)
组 5 (group, 2422x2003px, 位于路径: /组 5)
图层 1 拷贝 (LayerKind.NORMAL, 2422x2003px, 位于路径: /组 5/图层 1 拷贝)"""

# ==================== 提示词版本 ====================

def make_prompt_v2(layers_info: str) -> str:
    """v2 提示词：新增 pattern_mode + 不可见部位推理"""
    # 动态提取图层名
    layer_names = []
    for line in layers_info.strip().split('\n'):
        name = line.split('(')[0].strip()
        if name:
            layer_names.append(name)
    ex1 = layer_names[0] if len(layer_names) > 0 else "图层名1"
    ex2 = layer_names[1] if len(layer_names) > 1 else "图层名2"
    ex3 = layer_names[2] if len(layer_names) > 2 else "图层名3"

    return f"""你需要完成三个任务：

**任务1：识别裁片部位**
Image 2 是 PS 文档中的裁片轮廓图。以下是图层信息：
{layers_info}

请根据形状、大小识别每个**图层组**是什么服装部位（前片、后片、袖子、领口等）。
⚠️ mapping 的 key 必须是上面图层信息中的**实际图层组名称**（如 "{ex1}"、"{ex2}" 等），不要自己编名字！

**任务2：判断花型覆盖模式 pattern_mode**
Image 1 是成衣照片。请判断这件衣服的花型属于哪种模式：

- "all_over"：所有裁片来自同一块面料（碎花/格子/渐变等整体花型覆盖全身）
- "placement"：各裁片独立处理（有的印花有的纯色，如卡通卫衣）
- "color_block"：全部纯色拼接

如果是 all_over，还需要提供 fabric_info：
- fabric_type: 面料印花类型（数码印花/提花/丝网印/...）
- pattern_direction: 花型方向（均匀铺满/上下渐变/左右渐变/斜向）
- pattern_elements: 花型元素描述
- color_transition: 色彩过渡描述
- density: 花型密度描述

**任务3：分析每个裁片的图案类型**
对每个裁片分析：
- type: solid / fill_pattern / theme_pattern / mixed_pattern / all_over
- solid 和 theme_pattern 必须给 color（hex值）
- 如果某个裁片在照片中**看不到**（如后片），设置 visible: false，并根据前片推理：
  - 全身花型 → 后片与前片相同
  - 局部印花 → 后片通常为底色纯色
  - 给出推理理由 inference_reason 和置信度 confidence（high/medium/low）
- 如果左右对称（如左右袖），标记 mirror_of 字段

用 JSON 回答（注意：key 用实际图层名）：
```json
{{
  "pattern_mode": "all_over",
  "fabric_info": {{
    "fabric_type": "数码印花",
    "pattern_direction": "上下渐变",
    "pattern_elements": "白色百合花朵 + 灰白格纹底",
    "color_transition": "从黑色渐变到灰白色",
    "density": "上部花朵密集大朵，下部稀疏小朵"
  }},
  "mapping": {{
    "{ex1}": "左袖",
    "{ex2}": "前片"
  }},
  "analysis": [
    {{"layer": "{ex1}", "piece": "左袖", "type": "all_over", "visible": true, "description": "黑底白花渐变"}},
    {{"layer": "{ex2}", "piece": "前片", "type": "all_over", "visible": true, "description": "上半黑底大花+下半灰白格纹"}},
    {{"layer": "{ex3}", "piece": "后片", "type": "all_over", "visible": false, "inferred_from": "{ex2}", "inference_reason": "全身花型面料，后片花型与前片相同", "confidence": "high"}}
  ],
  "symmetric_pairs": [["{ex1}", "其他对称片"]],
  "base_color": "#1A1A1A"
}}
```

⚠️ 重要：
- mapping 和 analysis 中的 layer 必须用**实际图层组名称**
- 看不到的部位设 visible: false 并说明推理依据
- 左右对称的片标记 symmetric_pairs
只返回 JSON，不要其他文字。"""


# ==================== 调用模型 ====================

def call_vision(prompt: str, garment_b64: str, canvas_b64: str, model: str = None):
    """调用视觉模型"""
    use_model = model or settings.AI_VISION_MODEL
    client = OpenAI(
        api_key=settings.AI_API_KEY,
        base_url=settings.AI_BASE_URL or "https://api.openai.com/v1",
    )

    content = [
        {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{garment_b64}"}},
        {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{canvas_b64}"}},
        {"type": "text", "text": prompt},
    ]

    print(f"\n{'='*60}")
    print(f"模型: {use_model}")
    print(f"提示词长度: {len(prompt)} 字符")
    print(f"{'='*60}\n")

    completion = client.chat.completions.create(
        model=use_model,
        messages=[{"role": "user", "content": content}],
    )

    return completion.choices[0].message.content or ""


def parse_result(raw: str) -> dict:
    """解析 JSON 结果"""
    clean = raw.strip()
    if "```" in clean:
        clean = clean.split("```")[1]
        if clean.startswith("json"):
            clean = clean[4:]
        clean = clean.strip()
    try:
        return json.loads(clean)
    except json.JSONDecodeError as e:
        print(f"⚠️ JSON 解析失败: {e}")
        print(f"原始回复:\n{raw[:500]}")
        return {}


def evaluate_result(result: dict) -> list:
    """评估结果质量，返回问题列表"""
    issues = []

    if not result:
        issues.append("❌ 解析失败，无结果")
        return issues

    # 检查 pattern_mode
    mode = result.get("pattern_mode")
    if not mode:
        issues.append("❌ 缺少 pattern_mode")
    elif mode not in ("all_over", "placement", "color_block"):
        issues.append(f"⚠️ pattern_mode 值异常: {mode}")

    # 检查 mapping 是否用了真实图层名
    mapping = result.get("mapping", {})
    for key in mapping:
        if key.startswith("M-") or key.startswith("Layer"):
            issues.append(f"❌ mapping key '{key}' 不是真实图层名（应为 '组 X' 格式）")

    # 检查 analysis
    analysis = result.get("analysis", [])
    if not analysis:
        issues.append("❌ 缺少 analysis")
    else:
        has_invisible = any(a.get("visible") == False for a in analysis)
        if not has_invisible:
            issues.append("⚠️ 没有标记不可见部位（后片通常看不到）")

        has_mirror = any("mirror_of" in a for a in analysis)
        symmetric = result.get("symmetric_pairs", [])
        if not has_mirror and not symmetric:
            issues.append("⚠️ 没有标记对称片（左右袖应该对称）")

        for a in analysis:
            if a.get("type") in ("solid", "theme_pattern") and not a.get("color"):
                issues.append(f"⚠️ {a.get('layer')} 类型为 {a['type']} 但缺少 color")

    # 检查 fabric_info（仅 all_over）
    if mode == "all_over":
        fi = result.get("fabric_info")
        if not fi:
            issues.append("❌ all_over 模式缺少 fabric_info")
        else:
            for key in ("fabric_type", "pattern_direction", "pattern_elements", "color_transition", "density"):
                if not fi.get(key):
                    issues.append(f"⚠️ fabric_info 缺少 {key}")

    if not issues:
        issues.append("✅ 完美！所有字段齐全")

    return issues


# ==================== 主流程 ====================

def main():
    # 修复 Windows GBK 输出
    import io
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')

    # 加载测试图片
    print("[1] Loading test images...")
    with open(GARMENT_IMG, "rb") as f:
        garment_b64 = base64.b64encode(f.read()).decode()
    with open(CANVAS_IMG, "rb") as f:
        canvas_b64 = base64.b64encode(f.read()).decode()
    print(f"  garment: {len(garment_b64)//1024}KB, canvas: {len(canvas_b64)//1024}KB")

    # 生成提示词
    prompt = make_prompt_v2(LAYERS_INFO)

    # 调用模型
    print("\n[2] Calling vision model...")
    raw = call_vision(prompt, garment_b64, canvas_b64)

    # 解析
    print("\n[3] Model response:")
    print(raw[:2000])
    print("\n" + "="*60)

    result = parse_result(raw)

    if result:
        print("\n[4] Parsed result:")
        print(json.dumps(result, ensure_ascii=False, indent=2))

    # 评估
    print("\n[5] Quality check:")
    issues = evaluate_result(result)
    for issue in issues:
        print(f"  {issue}")

    # ==================== 阶段 2: Gemini 生图 ====================
    if result and result.get("pattern_mode") == "all_over" and result.get("fabric_info"):
        print("\n" + "="*60)
        print("[6] Testing Gemini image generation (all_over fabric)...")

        fi = result["fabric_info"]
        gen_prompt = (
            f"Please generate a high-resolution flat fabric pattern image based on this garment photo.\n"
            f"\n"
            f"Requirements:\n"
            f"1. Output a RECTANGULAR flat fabric image (not garment shape, just the fabric laid flat)\n"
            f"2. Fabric type: {fi.get('fabric_type', 'digital print')}\n"
            f"3. Pattern elements: {fi.get('pattern_elements', 'floral')}\n"
            f"4. Pattern direction: {fi.get('pattern_direction', 'uniform')}\n"
            f"5. Color transition: {fi.get('color_transition', 'none')}\n"
            f"6. Pattern density: {fi.get('density', 'medium')}\n"
            f"7. Remove all garment wrinkles/shadows - show flat fabric only\n"
            f"8. The pattern must be clear, colors accurate, suitable for garment piece overlay\n"
            f"9. Aspect ratio: 3:4\n"
            f"10. The image should look like a piece of fabric photographed from directly above on a flat surface"
        )

        print(f"  Prompt: {gen_prompt[:200]}...")
        print(f"  Model: gemini-3-pro-image-preview")

        # 用 Gemini OpenAI 兼容方式生图
        from openai import OpenAI as GeminiClient
        gemini = GeminiClient(
            api_key=settings.GEMINI_API_KEY,
            base_url=f"{settings.GEMINI_BASE_URL}/v1",
        )

        content_parts = [
            {"type": "text", "text": gen_prompt},
            {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{garment_b64}"}},
        ]

        print("  Calling Gemini 3 Pro Image...")
        import time
        t0 = time.time()

        completion = gemini.chat.completions.create(
            model="gemini-3-pro-image-preview",
            messages=[{"role": "user", "content": content_parts}],
        )

        elapsed = time.time() - t0
        resp_content = completion.choices[0].message.content or ""
        print(f"  Response length: {len(resp_content)} chars, time: {elapsed:.1f}s")

        # 提取图片
        import re
        match = re.search(r'!\[.*?\]\((data:image/(\w+);base64,([^)]+))\)', resp_content)
        if not match:
            match = re.search(r'(data:image/(\w+);base64,([A-Za-z0-9+/=]+))', resp_content)

        if match:
            img_format = match.group(2)
            img_b64 = match.group(3)
            # 修复 padding
            padding = 4 - len(img_b64) % 4
            if padding != 4:
                img_b64 += '=' * padding

            # 保存
            out_path = f"debug_images/test_fabric_allover.{img_format}"
            with open(out_path, "wb") as f:
                f.write(base64.b64decode(img_b64))
            print(f"  [OK] Fabric image saved: {out_path} ({len(img_b64)//1024}KB)")
        else:
            print(f"  [FAIL] No image in response")
            if resp_content:
                print(f"  Response preview: {resp_content[:300]}")

    print(f"\n{'='*60}")
    print("Done.")


if __name__ == "__main__":
    main()
