#!/usr/bin/env python3
"""
GPT Image 2 开发者文档 & Demo

================================================================
  快速开始
================================================================

  # 安装
  pip install openai

  # 最简示例 - 文生图
  from openai import OpenAI
  client = OpenAI(api_key="sk-xxx", base_url="https://your-api-gateway.example.com/v1")

  response = client.images.generate(
      model="gpt-image-2",
      prompt="A cute cat at the beach",
      size="1536x1024",
      n=1,
  )
  print(response.data[0].url)    # URL 有效期 24 小时

  # cURL - 文生图
  curl -X POST "https://your-api-gateway.example.com/v1/images/generations" \\
    -H "Authorization: Bearer sk-xxx" \\
    -H "Content-Type: application/json" \\
    -d '{
      "model": "gpt-image-2",
      "prompt": "A cute cat at the beach",
      "size": "1536x1024",
      "n": 1
    }'

================================================================
  响应格式
================================================================

  成功响应:
  {
    "data": [{"url": "https://..."}],
    "created": 1234567890
  }

  错误响应:
  {
    "error": {
      "message": "错误描述",
      "type": "error_type",
      "code": "error_code"
    }
  }

  图片以 URL 返回，有效期 24 小时，请及时下载。

================================================================
  参数说明
================================================================

  必填参数:
    model           "gpt-image-2"
    prompt          图片描述 (中英文均可)

  可选参数:
    size            图片尺寸，像素格式 WxH 或 "auto"
                    支持: 1536x1024, 1024x1536,
                          2048x2048, 2048x1152,
                          3840x2160, 2160x3840, auto
                    注意: 传 1024x1024 实际输出 1254x1254
    quality         "low" / "medium" / "high" / "auto" (默认 auto)
                    low=最快, high=最细腻
    output_format   "png" / "jpeg" / "webp" (默认 png)
    output_compression  0-100, 仅 jpeg/webp 有效
    background      "opaque" / "auto" (不支持 transparent)
    moderation      "auto" / "low" (默认 auto, low=更宽松)
    n               仅支持 1

================================================================
  文生图示例
================================================================

  # 基础 (3:2 横屏)
  response = client.images.generate(
      model="gpt-image-2",
      prompt="A mountain landscape at sunset",
      size="1536x1024",
      n=1,
  )

  # 竖屏 (2:3)
  response = client.images.generate(
      model="gpt-image-2",
      prompt="A portrait photo",
      size="1024x1536",
      n=1,
  )

  # 2K 高清
  response = client.images.generate(
      model="gpt-image-2",
      prompt="A mountain landscape at sunset",
      size="2048x2048",
      quality="high",
      n=1,
  )

  # 4K 超清 (16:9 横屏)
  response = client.images.generate(
      model="gpt-image-2",
      prompt="A cinematic mountain landscape",
      size="3840x2160",
      n=1,
  )

  # 指定输出格式和压缩
  response = client.images.generate(
      model="gpt-image-2",
      prompt="A product photo",
      size="1536x1024",
      output_format="webp",
      output_compression=50,
      n=1,
  )

================================================================
  图片编辑 (单图参考 / 图生图)
================================================================

  接口: /v1/images/edits (multipart/form-data)
  图片: 仅支持文件上传，不支持 base64 或 URL

  # Python SDK
  response = client.images.edit(
      model="gpt-image-2",
      image=open("input.png", "rb"),
      prompt="Change the background to a snowy mountain",
      n=1,
  )

  # cURL
  curl -X POST "https://your-api-gateway.example.com/v1/images/edits" \\
    -H "Authorization: Bearer sk-xxx" \\
    -F "model=gpt-image-2" \\
    -F "prompt=Change the background to snow" \\
    -F "image=@input.png" \\
    -F "n=1"

================================================================
  Mask 蒙版编辑 (局部修改)
================================================================

  使用 mask 指定编辑区域 (PNG, alpha 通道标记):
    不透明区域 = 编辑区
    透明区域 = 保留原图

  # Python SDK
  response = client.images.edit(
      model="gpt-image-2",
      image=open("input.png", "rb"),
      mask=open("mask.png", "rb"),
      prompt="Place a golden crown here",
      n=1,
  )

  # cURL
  curl -X POST "https://your-api-gateway.example.com/v1/images/edits" \\
    -H "Authorization: Bearer sk-xxx" \\
    -F "model=gpt-image-2" \\
    -F "prompt=Place a golden crown here" \\
    -F "image=@input.png" \\
    -F "mask=@mask.png" \\
    -F "n=1"

================================================================
  多图参考融合
================================================================

  传多张图片作为参考，生成融合后的新图。
  支持至少 16 张参考图，实际上限取决于总文件大小。
  通过 multipart/form-data 传多个 image 字段:

  # cURL
  curl -X POST "https://your-api-gateway.example.com/v1/images/edits" \\
    -H "Authorization: Bearer sk-xxx" \\
    -F "model=gpt-image-2" \\
    -F "prompt=Merge these images into a new artwork" \\
    -F "image=@ref1.png" \\
    -F "image=@ref2.png" \\
    -F "n=1"

  # Python (urllib, SDK 不直接支持多文件)
  见下方 test_multi_ref() 函数示例

================================================================
  错误处理
================================================================

  import urllib.error

  try:
      response = client.images.generate(
          model="gpt-image-2",
          prompt="...",
          size="1536x1024",
          n=1,
      )
      print(response.data[0].url)
  except Exception as e:
      print(f"生成失败: {e}")
      # 常见错误:
      # - 400: 参数错误 (不支持的 size/background 等)
      # - 429: 并发限制，请稍后重试
      # - 500: 服务异常
      # - 504: 生成超时，请重试

================================================================
  运行本 Demo
================================================================

  NEWAPI_BASE_URL=https://your-api-gateway.example.com/v1 \\
  NEWAPI_API_KEY=sk-xxx \\
  python3 demo_gpt_image_2.py

  可选参数:
    --skip-edit     跳过编辑测试
    --skip-hires    跳过高分辨率测试 (耗时较长)
"""

import base64
import json
import os
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 = "gpt-image-2"
OUTPUT_DIR = Path("gpt_image_2_output")


def api_request(path, data, timeout=600):
    if isinstance(data, dict):
        data = json.dumps(data).encode("utf-8")
    req = urllib.request.Request(
        f"{BASE_URL}{path}",
        data=data,
        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 save_image(data_item, filename):
    filepath = OUTPUT_DIR / filename
    if data_item.get("b64_json"):
        img_bytes = base64.b64decode(data_item["b64_json"])
        filepath.write_bytes(img_bytes)
        print(f"  保存 base64 → {filename} ({len(img_bytes)} bytes)")
    elif data_item.get("url"):
        url = data_item["url"]
        print(f"  URL: {url[:80]}...")
        req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
        with urllib.request.urlopen(req, timeout=60) as r:
            img_bytes = r.read()
        filepath.write_bytes(img_bytes)
        print(f"  下载 → {filename} ({len(img_bytes)} bytes)")
    else:
        print(f"  ⚠️ 无图片数据")


# ============================================================
#  1. 基础文生图
# ============================================================
def test_basic():
    print("\n=== 1. 基础文生图 ===")
    resp = api_request("/images/generations", {
        "model": MODEL,
        "prompt": "A cute orange cat wearing sunglasses at the beach, photorealistic",
        "size": "1536x1024",
        "n": 1,
    })
    save_image(resp["data"][0], "01_basic.png")
    return resp["data"][0].get("url", "")


# ============================================================
#  2. Quality 参数
# ============================================================
def test_quality():
    print("\n=== 2. Quality 参数 ===")
    for q in ["low", "medium", "high", "auto"]:
        print(f"  quality={q}...")
        resp = api_request("/images/generations", {
            "model": MODEL,
            "prompt": "a red rose, studio lighting",
            "size": "1536x1024",
            "quality": q,
            "n": 1,
        })
        save_image(resp["data"][0], f"02_quality_{q}.png")


# ============================================================
#  3. 所有分辨率
# ============================================================
def test_sizes():
    print("\n=== 3. 所有分辨率 ===")
    sizes = [
        ("1536x1024", "1K 3:2 landscape"),
        ("1024x1536", "1K 2:3 portrait"),
        ("2048x2048", "2K 1:1"),
        ("2048x1152", "2K 16:9 landscape"),
        ("3840x2160", "4K 16:9 landscape"),
        ("2160x3840", "4K 9:16 portrait"),
        ("auto",      "auto"),
    ]
    for size, desc in sizes:
        print(f"  {desc} ({size})...")
        try:
            resp = api_request("/images/generations", {
                "model": MODEL,
                "prompt": "a mountain landscape at sunset",
                "size": size,
                "n": 1,
            })
            save_image(resp["data"][0], f"03_size_{size}.png")
        except Exception as e:
            print(f"  ❌ {e}")


# ============================================================
#  4. Output format + compression
# ============================================================
def test_output_format():
    print("\n=== 4. Output format + compression ===")
    for fmt in ["png", "jpeg", "webp"]:
        print(f"  output_format={fmt}...")
        params = {
            "model": MODEL,
            "prompt": "a green apple",
            "size": "1536x1024",
            "output_format": fmt,
            "n": 1,
        }
        if fmt in ("jpeg", "webp"):
            params["output_compression"] = 50
        resp = api_request("/images/generations", params)
        save_image(resp["data"][0], f"04_format.{fmt}")


# ============================================================
#  5. Background
# ============================================================
def test_background():
    print("\n=== 5. Background ===")
    for bg in ["opaque", "auto"]:
        print(f"  background={bg}...")
        resp = api_request("/images/generations", {
            "model": MODEL,
            "prompt": "a sneaker product photo, studio lighting",
            "size": "1536x1024",
            "background": bg,
            "n": 1,
        })
        save_image(resp["data"][0], f"05_bg_{bg}.png")


# ============================================================
#  6. Moderation
# ============================================================
def test_moderation():
    print("\n=== 6. Moderation ===")
    for mod in ["auto", "low"]:
        print(f"  moderation={mod}...")
        resp = api_request("/images/generations", {
            "model": MODEL,
            "prompt": "a warrior in armor, dramatic lighting",
            "size": "1536x1024",
            "moderation": mod,
            "n": 1,
        })
        save_image(resp["data"][0], f"06_mod_{mod}.png")


# ============================================================
#  7. Base64 输出
# ============================================================
def test_b64():
    print("\n=== 7. Base64 输出 ===")
    resp = api_request("/images/generations", {
        "model": MODEL,
        "prompt": "a simple blue circle on white background",
        "size": "1536x1024",
        "response_format": "b64_json",
        "n": 1,
    })
    save_image(resp["data"][0], "07_b64.png")


# ============================================================
#  8. 图片编辑 (单图参考)
# ============================================================
def test_edit(source_url):
    print("\n=== 8. 图片编辑 ===")
    dl_req = urllib.request.Request(source_url, headers={"User-Agent": "Mozilla/5.0"})
    with urllib.request.urlopen(dl_req, timeout=60) as r:
        source_bytes = r.read()
    print(f"  源图: {len(source_bytes)} bytes")

    boundary = "----FormBoundary7MA4YWxkTrZu0gW"
    body = b""
    for name, value in [("model", MODEL), ("prompt", "Add a tiny red hat on the cat"), ("n", "1")]:
        body += f"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"\r\n\r\n{value}\r\n".encode()
    body += f"--{boundary}\r\nContent-Disposition: form-data; name=\"image\"; filename=\"source.png\"\r\nContent-Type: image/png\r\n\r\n".encode()
    body += source_bytes + b"\r\n"
    body += f"--{boundary}--\r\n".encode()

    req = urllib.request.Request(
        f"{BASE_URL}/images/edits", data=body,
        headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": f"multipart/form-data; boundary={boundary}"},
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=600) as resp:
        result = json.loads(resp.read().decode("utf-8"))
    save_image(result["data"][0], "08_edit.png")


# ============================================================
#  9. Mask 蒙版编辑
# ============================================================
def test_mask_edit(source_url):
    print("\n=== 9. Mask 蒙版编辑 ===")
    import struct
    import zlib

    dl_req = urllib.request.Request(source_url, headers={"User-Agent": "Mozilla/5.0"})
    with urllib.request.urlopen(dl_req, timeout=60) as r:
        source_bytes = r.read()

    w, h = 256, 256
    rows = []
    for y in range(h):
        row = b"\x00"
        for x in range(w):
            if 64 <= x < 192 and 64 <= y < 192:
                row += b"\xff\xff\xff\xff"
            else:
                row += b"\x00\x00\x00\x00"
        rows.append(row)
    raw = b"".join(rows)

    def png_chunk(ctype, data):
        c = ctype + data
        return struct.pack(">I", len(data)) + c + struct.pack(">I", zlib.crc32(c) & 0xFFFFFFFF)

    ihdr = struct.pack(">IIBBBBB", w, h, 8, 6, 0, 0, 0)
    mask_bytes = b"\x89PNG\r\n\x1a\n"
    mask_bytes += png_chunk(b"IHDR", ihdr)
    mask_bytes += png_chunk(b"IDAT", zlib.compress(raw))
    mask_bytes += png_chunk(b"IEND", b"")
    print(f"  mask: {w}x{h} PNG ({len(mask_bytes)} bytes)")

    boundary = "----FormBoundary7MA4YWxkTrZu0gW"
    body = b""
    for name, value in [("model", MODEL), ("prompt", "Place a golden crown in the masked area"), ("n", "1")]:
        body += f"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"\r\n\r\n{value}\r\n".encode()
    body += f"--{boundary}\r\nContent-Disposition: form-data; name=\"image\"; filename=\"source.png\"\r\nContent-Type: image/png\r\n\r\n".encode()
    body += source_bytes + b"\r\n"
    body += f"--{boundary}\r\nContent-Disposition: form-data; name=\"mask\"; filename=\"mask.png\"\r\nContent-Type: image/png\r\n\r\n".encode()
    body += mask_bytes + b"\r\n"
    body += f"--{boundary}--\r\n".encode()

    req = urllib.request.Request(
        f"{BASE_URL}/images/edits", data=body,
        headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": f"multipart/form-data; boundary={boundary}"},
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=600) as resp:
        result = json.loads(resp.read().decode("utf-8"))
    save_image(result["data"][0], "09_mask_edit.png")


# ============================================================
#  10. 多图参考融合
# ============================================================
def test_multi_ref(source_url):
    print("\n=== 10. 多图参考融合 ===")
    dl_req = urllib.request.Request(source_url, headers={"User-Agent": "Mozilla/5.0"})
    with urllib.request.urlopen(dl_req, timeout=60) as r:
        source_bytes = r.read()
    print(f"  参考图: {len(source_bytes)} bytes")

    boundary = "----FormBoundary7MA4YWxkTrZu0gW"
    body = b""
    for name, value in [("model", MODEL), ("prompt", "Combine these reference images into a creative new composition"), ("n", "1")]:
        body += f"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"\r\n\r\n{value}\r\n".encode()
    body += f"--{boundary}\r\nContent-Disposition: form-data; name=\"image\"; filename=\"ref1.png\"\r\nContent-Type: image/png\r\n\r\n".encode()
    body += source_bytes + b"\r\n"
    body += f"--{boundary}\r\nContent-Disposition: form-data; name=\"image\"; filename=\"ref2.png\"\r\nContent-Type: image/png\r\n\r\n".encode()
    body += source_bytes + b"\r\n"
    body += f"--{boundary}--\r\n".encode()

    req = urllib.request.Request(
        f"{BASE_URL}/images/edits", data=body,
        headers={"Authorization": f"Bearer {API_KEY}", "Content-Type": f"multipart/form-data; boundary={boundary}"},
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=600) as resp:
        result = json.loads(resp.read().decode("utf-8"))
    save_image(result["data"][0], "10_multi_ref.png")


# ============================================================
#  主流程
# ============================================================
def main():
    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_gpt_image_2.py",
            file=sys.stderr,
        )
        sys.exit(1)

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

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

    results = {}

    # 1. 基础
    try:
        source_url = test_basic()
        results["01_basic"] = "✅"
    except Exception as e:
        results["01_basic"] = f"❌ {e}"
        source_url = ""

    # 2. Quality
    try:
        test_quality()
        results["02_quality"] = "✅"
    except Exception as e:
        results["02_quality"] = f"❌ {e}"

    # 3. 尺寸
    if not skip_hires:
        try:
            test_sizes()
            results["03_sizes"] = "✅"
        except Exception as e:
            results["03_sizes"] = f"❌ {e}"
    else:
        results["03_sizes"] = "⏭️ 跳过 (--skip-hires)"

    # 4. Output format
    try:
        test_output_format()
        results["04_format"] = "✅"
    except Exception as e:
        results["04_format"] = f"❌ {e}"

    # 5. Background
    try:
        test_background()
        results["05_background"] = "✅"
    except Exception as e:
        results["05_background"] = f"❌ {e}"

    # 6. Moderation
    try:
        test_moderation()
        results["06_moderation"] = "✅"
    except Exception as e:
        results["06_moderation"] = f"❌ {e}"

    # 7. B64
    try:
        test_b64()
        results["07_b64"] = "✅"
    except Exception as e:
        results["07_b64"] = f"❌ {e}"

    # 8-10 编辑测试
    if not skip_edit and source_url:
        for name, fn in [("08_edit", test_edit), ("09_mask", test_mask_edit), ("10_multi_ref", test_multi_ref)]:
            try:
                fn(source_url)
                results[name] = "✅"
            except Exception as e:
                results[name] = f"❌ {e}"
    elif skip_edit:
        results["08_edit"] = results["09_mask"] = results["10_multi_ref"] = "⏭️ 跳过"
    else:
        results["08_edit"] = results["09_mask"] = results["10_multi_ref"] = "⏭️ 跳过（无源图）"

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


if __name__ == "__main__":
    main()
