#!/usr/bin/env python3
"""
Gemini Flash Image 完整 Demo

覆盖 Google 官方所有参数：
  - image_size: 512 / 1K / 2K / 4K
  - aspect_ratio: 1:1 / 3:4 / 4:3 / 9:16 / 16:9 / 3:2 / 2:3 / 4:5 / 5:4 / 21:9
  - 文生图 / 图片编辑 / 多轮对话 / 中文 prompt / 风格控制
  - temperature 对比 / 纯文字问答

用法：
    NEWAPI_BASE_URL=https://your-api-gateway.example.com/v1 \\
    NEWAPI_API_KEY=sk-xxx \\
    python3 demo_gemini_image.py

可选参数：
    --model gemini-3.1-flash-image    指定模型
    --skip-edit                        跳过图片编辑测试
    --skip-hires                       跳过 2K/4K 测试 (耗时较长)
    --edit-image path/to/image.jpg     指定编辑用的源图
"""

import base64
import json
import os
import re
import sys
import urllib.error
import urllib.request
from pathlib import Path

BASE_URL = os.environ.get("NEWAPI_BASE_URL", "")
API_KEY = os.environ.get("NEWAPI_API_KEY", "")
MODEL = "gemini-3.1-flash-image"
OUTPUT_DIR = Path("gemini_image_output")


def api_chat(messages, temperature=0.8, extra_body=None, timeout=300):
    payload = {
        "model": MODEL,
        "messages": messages,
        "temperature": temperature,
        "max_tokens": 4000,
    }
    if extra_body:
        payload["extra_body"] = extra_body
    req = urllib.request.Request(
        f"{BASE_URL}/chat/completions",
        data=json.dumps(payload).encode("utf-8"),
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json",
        },
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        return json.loads(resp.read().decode("utf-8"))


def extract_image_url(content):
    match = re.search(r"!\[[^\]]*\]\((https?://[^\)]+)\)", content or "")
    return match.group(1) if match else None


def extract_b64_image(content):
    match = re.search(
        r"!\[[^\]]*\]\(data:(image/[a-zA-Z0-9.+-]+);base64,([A-Za-z0-9+/=]+)\)",
        content or "",
    )
    return match.groups() if match else (None, None)


def save_image(content, filename):
    filepath = OUTPUT_DIR / filename
    url = extract_image_url(content)
    if url:
        try:
            with urllib.request.urlopen(url, timeout=60) as r:
                img = r.read()
            filepath.write_bytes(img)
            return len(img), "url"
        except Exception:
            pass

    _, b64 = extract_b64_image(content)
    if b64:
        img = base64.b64decode(b64)
        filepath.write_bytes(img)
        return len(img), "base64"

    return 0, None


def get_content(resp):
    choices = resp.get("choices", [])
    return choices[0].get("message", {}).get("content", "") if choices else ""


def print_usage(resp):
    usage = resp.get("usage", {})
    if usage:
        print(f"    tokens: prompt={usage.get('prompt_tokens','?')} completion={usage.get('completion_tokens','?')}")


def encode_image_file(path):
    import mimetypes
    mime, _ = mimetypes.guess_type(path)
    if not mime:
        mime = "image/jpeg"
    with open(path, "rb") as f:
        b64 = base64.b64encode(f.read()).decode("ascii")
    return f"data:{mime};base64,{b64}"


# ============================================================
#  1. 基础文生图 (默认 1K)
# ============================================================
def test_basic():
    print("\n=== 1. 基础文生图 (默认 1K) ===")
    resp = api_chat([
        {"role": "user", "content": "Generate a cute orange cat sitting on a windowsill at sunset, photorealistic."}
    ])
    print_usage(resp)
    content = get_content(resp)
    sz, mode = save_image(content, "1_basic.jpg")
    if sz:
        print(f"  ✅ {sz} bytes ({mode}) → 1_basic.jpg")
    else:
        print(f"  ❌ 未提取到图片")
    return content


# ============================================================
#  2. 所有 image_size (512 / 1K / 2K / 4K)
# ============================================================
def test_image_sizes():
    print("\n=== 2. 所有分辨率 (512 / 1K / 2K / 4K) ===")
    for image_size in ["512", "1K", "2K", "4K"]:
        timeout = 120 if image_size in ("512", "1K") else 600
        print(f"  image_size={image_size}...")
        try:
            resp = api_chat(
                [{"role": "user", "content": "A simple red circle on white background"}],
                extra_body={"google": {"image_config": {"image_size": image_size}}},
                timeout=timeout,
            )
            print_usage(resp)
            content = get_content(resp)
            sz, mode = save_image(content, f"2_size_{image_size}.jpg")
            if sz:
                print(f"  ✅ {sz} bytes ({mode}) → 2_size_{image_size}.jpg")
            else:
                print(f"  ❌ 未提取到图片")
        except Exception as e:
            print(f"  ❌ {e}")


# ============================================================
#  3. 所有 aspect_ratio
# ============================================================
def test_aspect_ratios():
    print("\n=== 3. 所有比例 ===")
    ratios = ["1:1", "3:4", "4:3", "9:16", "16:9", "3:2", "2:3", "4:5", "5:4", "21:9"]
    for ratio in ratios:
        print(f"  aspect_ratio={ratio}...")
        try:
            resp = api_chat(
                [{"role": "user", "content": "A mountain landscape"}],
                extra_body={"google": {"image_config": {"aspect_ratio": ratio}}},
                timeout=120,
            )
            content = get_content(resp)
            tag = ratio.replace(":", "x")
            sz, mode = save_image(content, f"3_ratio_{tag}.jpg")
            if sz:
                print(f"  ✅ {sz} bytes → 3_ratio_{tag}.jpg")
            else:
                print(f"  ❌ 未提取到图片")
        except Exception as e:
            print(f"  ❌ {e}")


# ============================================================
#  4. 2K + 各种比例
# ============================================================
def test_2k_ratios():
    print("\n=== 4. 2K + 各种比例 ===")
    for ratio in ["1:1", "16:9", "9:16", "4:3", "3:4"]:
        print(f"  2K + {ratio}...")
        try:
            resp = api_chat(
                [{"role": "user", "content": "A beautiful landscape painting"}],
                extra_body={"google": {"image_config": {
                    "image_size": "2K",
                    "aspect_ratio": ratio,
                }}},
                timeout=600,
            )
            content = get_content(resp)
            tag = ratio.replace(":", "x")
            sz, mode = save_image(content, f"4_2k_{tag}.jpg")
            if sz:
                print(f"  ✅ {sz} bytes → 4_2k_{tag}.jpg")
            else:
                print(f"  ❌ 未提取到图片")
        except Exception as e:
            print(f"  ❌ {e}")


# ============================================================
#  5. 4K + 各种比例
# ============================================================
def test_4k_ratios():
    print("\n=== 5. 4K + 各种比例 ===")
    for ratio in ["1:1", "16:9", "9:16"]:
        print(f"  4K + {ratio}...")
        try:
            resp = api_chat(
                [{"role": "user", "content": "A detailed city skyline at night"}],
                extra_body={"google": {"image_config": {
                    "image_size": "4K",
                    "aspect_ratio": ratio,
                }}},
                timeout=600,
            )
            content = get_content(resp)
            tag = ratio.replace(":", "x")
            sz, mode = save_image(content, f"5_4k_{tag}.jpg")
            if sz:
                print(f"  ✅ {sz} bytes → 5_4k_{tag}.jpg")
            else:
                print(f"  ❌ 未提取到图片")
        except Exception as e:
            print(f"  ❌ {e}")


# ============================================================
#  6. 不同风格
# ============================================================
def test_styles():
    print("\n=== 6. 不同风格 ===")
    styles = [
        ("realistic", "A hyperrealistic photo of a red sports car on a mountain road at golden hour"),
        ("illustration", "A flat vector illustration of a cozy coffee shop, warm colors, minimal style"),
        ("pixel", "A pixel art scene of a medieval castle with a dragon, 16-bit retro game style"),
    ]
    for name, prompt in styles:
        print(f"  style={name}...")
        resp = api_chat([{"role": "user", "content": prompt}])
        content = get_content(resp)
        sz, _ = save_image(content, f"6_style_{name}.jpg")
        print(f"  {'✅' if sz else '❌'} {sz} bytes")


# ============================================================
#  7. 中文 prompt
# ============================================================
def test_chinese():
    print("\n=== 7. 中文 Prompt ===")
    resp = api_chat([
        {"role": "user", "content": "画一只戴着圣诞帽的柴犬，坐在雪地里，背景是温馨的木屋和圣诞树，卡通风格。"}
    ])
    content = get_content(resp)
    sz, _ = save_image(content, "7_chinese.jpg")
    print(f"  {'✅' if sz else '❌'} {sz} bytes")


# ============================================================
#  8. Temperature 对比
# ============================================================
def test_temperature():
    print("\n=== 8. Temperature 对比 ===")
    for temp in [0.2, 1.0, 1.8]:
        print(f"  temperature={temp}...")
        resp = api_chat(
            [{"role": "user", "content": "A simple red apple on a white background"}],
            temperature=temp,
        )
        content = get_content(resp)
        sz, _ = save_image(content, f"8_temp_{temp}.jpg")
        print(f"  {'✅' if sz else '❌'} {sz} bytes")


# ============================================================
#  9. 多轮对话 (生成 → 修改)
# ============================================================
def test_multiturn():
    print("\n=== 9. 多轮对话 (生成 → 修改) ===")
    messages = [
        {"role": "user", "content": "Draw a simple blue circle on a white background."}
    ]
    print("  第 1 轮: 生成蓝色圆...")
    resp1 = api_chat(messages)
    content1 = get_content(resp1)
    sz, _ = save_image(content1, "9_turn1.jpg")
    print(f"  ✅ 第 1 轮: {sz} bytes")

    messages.append({"role": "assistant", "content": content1})
    messages.append({"role": "user", "content": "Now change the blue circle to red and add a green triangle next to it."})
    print("  第 2 轮: 改成红色 + 加三角形...")
    resp2 = api_chat(messages)
    content2 = get_content(resp2)
    sz, _ = save_image(content2, "9_turn2.jpg")
    print(f"  {'✅' if sz else '❌'} 第 2 轮: {sz} bytes")


# ============================================================
#  10. 图片编辑
# ============================================================
def test_edit(image_path):
    print("\n=== 10. 图片编辑 ===")
    if not os.path.isfile(image_path):
        print(f"  ⏭️ 跳过: 图片不存在 {image_path}")
        return False

    data_url = encode_image_file(image_path)
    print(f"  源图: {image_path}")

    resp = api_chat([
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Add a pair of cool sunglasses to the main subject. Keep everything else unchanged."},
                {"type": "image_url", "image_url": {"url": data_url}},
            ],
        }
    ])
    content = get_content(resp)
    sz, mode = save_image(content, "10_edit.jpg")
    if sz:
        print(f"  ✅ {sz} bytes ({mode}) → 10_edit.jpg")
        return True
    else:
        print(f"  ❌ 未提取到编辑后的图片")
        return False


# ============================================================
#  11. 纯文字问答
# ============================================================
def test_text_only():
    print("\n=== 11. 纯文字问答 ===")
    resp = api_chat([
        {"role": "user", "content": "What are the primary colors? Answer in one sentence."}
    ])
    content = get_content(resp)
    print(f"  回复: {content[:200]}")
    return True


# ============================================================
#  主流程
# ============================================================
def main():
    global MODEL

    if not API_KEY or not BASE_URL:
        print("请设置 NEWAPI_BASE_URL 和 NEWAPI_API_KEY", file=sys.stderr)
        print(
            "示例: NEWAPI_BASE_URL='https://your-api-gateway.example.com/v1' "
            "NEWAPI_API_KEY='sk-xxx' python3 demo_gemini_image.py",
            file=sys.stderr,
        )
        sys.exit(1)

    args = sys.argv[1:]
    skip_edit = "--skip-edit" in args
    skip_hires = "--skip-hires" in args
    edit_image = ""

    i = 0
    while i < len(args):
        if args[i] == "--model" and i + 1 < len(args):
            MODEL = args[i + 1]
            i += 2
        elif args[i] == "--edit-image" and i + 1 < len(args):
            edit_image = args[i + 1]
            i += 2
        else:
            i += 1

    OUTPUT_DIR.mkdir(exist_ok=True)
    print(f"模型: {MODEL}")
    print(f"Base URL: {BASE_URL}")
    print(f"输出目录: {OUTPUT_DIR}")

    results = {}

    # 基础测试
    tests = [
        ("1_basic", test_basic),
        ("6_styles", test_styles),
        ("7_chinese", test_chinese),
        ("8_temperature", test_temperature),
        ("9_multiturn", test_multiturn),
        ("11_text_only", test_text_only),
    ]

    for name, fn in tests:
        try:
            fn()
            results[name] = "✅"
        except Exception as e:
            results[name] = f"❌ {e}"

    # 分辨率测试
    if not skip_hires:
        for name, fn in [
            ("2_image_sizes", test_image_sizes),
            ("3_aspect_ratios", test_aspect_ratios),
            ("4_2k_ratios", test_2k_ratios),
            ("5_4k_ratios", test_4k_ratios),
        ]:
            try:
                fn()
                results[name] = "✅"
            except Exception as e:
                results[name] = f"❌ {e}"
    else:
        results["2-5_hires"] = "⏭️ 跳过 (--skip-hires)"

    # 图片编辑
    if not skip_edit:
        if not edit_image:
            candidate = OUTPUT_DIR / "1_basic.jpg"
            if candidate.exists():
                edit_image = str(candidate)
        if edit_image:
            try:
                ok = test_edit(edit_image)
                results["10_edit"] = "✅" if ok else "❌"
            except Exception as e:
                results["10_edit"] = f"❌ {e}"
        else:
            results["10_edit"] = "⏭️ 跳过（无源图，用 --edit-image 指定）"

    # 汇总
    print(f"\n{'='*50}")
    print(f"  {MODEL} 测试结果")
    print(f"{'='*50}")
    passed = sum(1 for v in results.values() if v == "✅")
    for k, v in sorted(results.items()):
        print(f"  {k}: {v}")
    print(f"\n  通过: {passed}/{len(results)}")


if __name__ == "__main__":
    main()
