#!/usr/bin/env python3
"""
fal.ai 앵글 편집 테스트 — 기존 씬 이미지의 카메라 앵글을 변형.

fal-ai/qwen-image-edit-2511-multiple-angles 모델 사용.

실행: cd backend && .venv/bin/python tests/test_fal_angle_edit.py
"""

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

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
# 2026-04-27 hotfix: prod URL setdefault 제거. 단독 실행 시 prod DB가 의도하지 않은
# 변경에 노출될 위험이 있어 sqlite default로 변경. prod DB가 필요하면 명시적으로
# export DATABASE_URL=postgresql://... 후 실행할 것.
import tempfile as _tmp_default
os.environ.setdefault("DATABASE_URL", f"sqlite:///{_tmp_default.gettempdir()}/theroad_standalone.sqlite")

from app.core.config import settings

FAL_API_URL = "https://fal.run/fal-ai/qwen-image-edit-2511-multiple-angles"
OUTPUT_DIR = Path(settings.projects_dir) / "fal_angle_test"


def upload_image_to_fal(image_path: Path) -> str:
    """로컬 이미지를 fal CDN에 업로드하고 URL 반환."""
    import fal_client
    url = fal_client.upload_file(str(image_path))
    return url


def edit_angle_rest(image_url: str, horizontal: int = 0, vertical: int = 0, zoom: float = 1.0) -> dict:
    """REST API로 fal.ai 앵글 편집 호출."""
    body = {
        "image_urls": [image_url],
        "horizontal_angle": horizontal,
        "vertical_angle": vertical,
        "zoom": zoom,
    }

    req = urllib.request.Request(
        FAL_API_URL,
        data=json.dumps(body).encode(),
        headers={
            "Authorization": f"Key {settings.fal_key}",
            "Content-Type": "application/json",
        },
    )

    try:
        with urllib.request.urlopen(req, timeout=120) as resp:
            return json.loads(resp.read())
    except urllib.error.HTTPError as e:
        error_text = e.read().decode("utf-8", errors="replace")
        return {"error": f"HTTP {e.code}: {error_text[:300]}"}


def edit_angle_sdk(image_url: str, horizontal: int = 0, vertical: int = 0, zoom: float = 1.0) -> dict:
    """fal-client SDK로 앵글 편집."""
    import fal_client

    def on_update(update):
        if isinstance(update, fal_client.InProgress):
            for log in update.logs:
                print(f"    [fal] {log['message']}")

    result = fal_client.subscribe(
        "fal-ai/qwen-image-edit-2511-multiple-angles",
        arguments={
            "image_urls": [image_url],
            "horizontal_angle": horizontal,
            "vertical_angle": vertical,
            "zoom": zoom,
        },
        with_logs=True,
        on_queue_update=on_update,
    )
    return result


def download_image(url: str) -> bytes:
    """URL에서 이미지 다운로드."""
    with urllib.request.urlopen(url, timeout=60) as resp:
        return resp.read()


def find_existing_scene_images(limit: int = 3) -> list:
    """기존 생성된 씬 이미지 파일 찾기."""
    projects_dir = Path(settings.projects_dir)
    images = []
    for png in sorted(projects_dir.rglob("*/scene/*.png"))[:limit]:
        images.append(png)
    return images


def run():
    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

    print("=== fal.ai 앵글 편집 테스트 ===")
    print(f"FAL_KEY: {'설정됨' if settings.fal_key else '없음'}")
    print(f"모델: qwen-image-edit-2511-multiple-angles")
    print()

    if not settings.fal_key:
        print("❌ FAL_KEY가 설정되지 않았습니다. .env에 FAL_KEY를 추가하세요.")
        return

    # 기존 씬 이미지 찾기
    scene_images = find_existing_scene_images(2)
    if not scene_images:
        print("❌ 기존 씬 이미지가 없습니다. 먼저 이미지를 생성하세요.")
        return

    print(f"테스트할 이미지: {len(scene_images)}개")

    # 앵글 변형 설정
    angle_variations = [
        {"name": "원본", "h": 0, "v": 0, "zoom": 1.0},
        {"name": "좌측 30도", "h": 330, "v": 0, "zoom": 1.0},
        {"name": "우측 30도", "h": 30, "v": 0, "zoom": 1.0},
        {"name": "위에서 내려다봄", "h": 0, "v": 30, "zoom": 1.0},
        {"name": "아래에서 올려다봄", "h": 0, "v": -20, "zoom": 1.0},
        {"name": "좌측 위 대각선", "h": 335, "v": 20, "zoom": 1.2},
    ]

    results = []

    for img_idx, img_path in enumerate(scene_images):
        print(f"\n[{img_idx + 1}/{len(scene_images)}] {img_path.name}")

        # fal CDN에 업로드
        try:
            print("  업로드 중...")
            image_url = upload_image_to_fal(img_path)
            print(f"  업로드 완료: {image_url[:60]}...")
        except Exception as e:
            print(f"  ❌ 업로드 실패: {e}")
            continue

        img_results = {"source": str(img_path), "variations": []}

        for var in angle_variations:
            print(f"  앵글: {var['name']} (H={var['h']}°, V={var['v']}°, zoom={var['zoom']})")

            if var["h"] == 0 and var["v"] == 0 and var["zoom"] == 1.0:
                # 원본은 스킵
                img_results["variations"].append({
                    "name": var["name"],
                    "status": "original",
                    "path": str(img_path),
                })
                print(f"    → 원본 (스킵)")
                continue

            try:
                result = edit_angle_sdk(image_url, var["h"], var["v"], var["zoom"])

                if "error" in result:
                    print(f"    ❌ {result['error'][:100]}")
                    img_results["variations"].append({
                        "name": var["name"],
                        "status": "error",
                        "error": str(result["error"])[:200],
                    })
                    continue

                # 이미지 URL 추출
                images = result.get("images", result.get("data", {}).get("generated_images", []))
                if images:
                    img_url = images[0].get("url", "")
                    if img_url:
                        img_bytes = download_image(img_url)
                        out_name = f"img{img_idx}_{var['name'].replace(' ', '_')}.png"
                        out_path = OUTPUT_DIR / out_name
                        out_path.write_bytes(img_bytes)
                        print(f"    ✅ 성공 ({len(img_bytes) // 1024}KB) → {out_name}")
                        img_results["variations"].append({
                            "name": var["name"],
                            "status": "success",
                            "path": str(out_path),
                            "size_kb": len(img_bytes) // 1024,
                        })
                    else:
                        print(f"    ⚠️ 이미지 URL 없음")
                        img_results["variations"].append({"name": var["name"], "status": "no_url"})
                else:
                    print(f"    ⚠️ 응답에 이미지 없음: {json.dumps(result)[:200]}")
                    img_results["variations"].append({"name": var["name"], "status": "no_images"})

            except Exception as e:
                print(f"    ❌ {str(e)[:100]}")
                img_results["variations"].append({
                    "name": var["name"],
                    "status": "error",
                    "error": str(e)[:200],
                })

        results.append(img_results)

    # HTML 비교 페이지 생성
    html = build_html(results)
    html_path = OUTPUT_DIR / "angle_comparison.html"
    html_path.write_text(html, encoding="utf-8")
    print(f"\n✅ 비교 페이지: {html_path}")
    print(f"   open {html_path}")


def build_html(results: list) -> str:
    cards = ""
    for r in results:
        source_name = Path(r["source"]).name
        imgs_html = ""
        for var in r["variations"]:
            if var["status"] == "original":
                b64 = base64.b64encode(Path(r["source"]).read_bytes()).decode()
                imgs_html += f'''
                <div class="var">
                    <h4>{var["name"]}</h4>
                    <img src="data:image/png;base64,{b64}">
                </div>'''
            elif var["status"] == "success":
                b64 = base64.b64encode(Path(var["path"]).read_bytes()).decode()
                imgs_html += f'''
                <div class="var">
                    <h4>{var["name"]}</h4>
                    <img src="data:image/png;base64,{b64}">
                    <span class="size">{var.get("size_kb", "?")}KB</span>
                </div>'''
            else:
                imgs_html += f'''
                <div class="var fail">
                    <h4>{var["name"]}</h4>
                    <div class="err">{var.get("error", var["status"])[:100]}</div>
                </div>'''

        cards += f'''
        <div class="card">
            <h3>원본: {source_name}</h3>
            <div class="grid">{imgs_html}</div>
        </div>'''

    return f'''<!DOCTYPE html>
<html lang="ko"><head><meta charset="utf-8">
<title>fal.ai 앵글 편집 비교</title>
<style>
* {{ box-sizing: border-box; margin: 0; padding: 0; }}
body {{ font-family: -apple-system, sans-serif; background: #0f1117; color: #e4e6ed; padding: 24px; }}
h2 {{ margin-bottom: 24px; }}
.card {{ background: #1e2230; border-radius: 12px; padding: 20px; margin-bottom: 20px; }}
.card h3 {{ margin-bottom: 12px; font-size: 14px; color: #8b90a0; }}
.grid {{ display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 12px; }}
.var {{ background: #252938; border-radius: 8px; padding: 10px; }}
.var h4 {{ font-size: 13px; color: #6386ff; margin-bottom: 8px; }}
.var img {{ width: 100%; border-radius: 6px; }}
.var .size {{ font-size: 11px; color: #5c6070; }}
.fail {{ border: 1px solid rgba(248,113,113,0.3); }}
.err {{ color: #f87171; font-size: 12px; }}
</style></head><body>
<h2>fal.ai 앵글 편집 비교 — qwen-image-edit-2511-multiple-angles</h2>
{cards}
</body></html>'''


if __name__ == "__main__":
    run()
