#!/usr/bin/env python3
"""
T.O.P 리크루팅 캠페인 - Angle A: 정착지원금
Meta 캐러셀 배너 슬라이드 1-3 생성 스크립트

파이프라인:
  1. Gemini Flash Image API → 배경 이미지 생성 (JPEG)
  2. HTML/CSS 템플릿으로 텍스트 오버레이 구성
  3. Playwright 헤드리스 브라우저로 PNG 캡처 (1080x1080)
"""

from __future__ import annotations

import base64
import json
import subprocess
import sys
import time
from pathlib import Path

import requests
from playwright.sync_api import sync_playwright

# ─────────────────────────────────────────────────────────────────
# 설정 상수
# ─────────────────────────────────────────────────────────────────

OUTPUT_DIR = Path("/home/jay/workspace/output/meta-ads/angle-A/v6-benchmark")
TOOLS_DIR = Path("/home/jay/workspace/tools/ai-image-gen")

MODEL_ID = "gemini-3.1-flash-image-preview"
GEMINI_API_BASE = "https://generativelanguage.googleapis.com/v1beta"
GEMINI_SCOPE = "https://www.googleapis.com/auth/generative-language"

# ─────────────────────────────────────────────────────────────────
# Gemini 배경 이미지 프롬프트
# ─────────────────────────────────────────────────────────────────

BG_PROMPTS = {
    "slide-01": (
        "Professional dark navy blue abstract background with subtle geometric shapes "
        "suggesting stagnation. Flat horizontal lines and a plateauing graph motif faintly "
        "visible in the dark background. Moody corporate atmosphere with very soft diffuse "
        "lighting. Dark navy (#003B5C) dominant color with slight emerald green (#047857) "
        "subtle accent tones. Clean large empty center area for text overlay. "
        "NO text, NO people, NO logos, NO numbers. Purely abstract. 1080x1080 square format."
    ),
    "slide-02": (
        "Abstract dark navy (#003B5C) to emerald green (#047857) gradient background with "
        "subtle geometric clock gear shapes and angular urgency motifs faintly visible. "
        "Dramatic lighting with a sense of time pressure and transition. Professional "
        "financial atmosphere with deep shadows. Clean empty center area for text overlay. "
        "NO text, NO people, NO numbers. Abstract professional. 1080x1080 square format."
    ),
    "slide-03": (
        "Uplifting professional background blending deep navy (#003B5C) and emerald green "
        "on the sides with abstract upward-pointing arrow shapes and growth motifs subtly "
        "embedded. Hopeful solution-oriented mood. Modern financial atmosphere with soft "
        "glowing light from below. Brighter and more vibrant than a problem slide. "
        "Clean empty center area for text overlay. "
        "NO text, NO people, NO logos. 1080x1080 square format."
    ),
}

# ─────────────────────────────────────────────────────────────────
# 인증 헬퍼
# ─────────────────────────────────────────────────────────────────

def get_api_key() -> str | None:
    """~/.env 또는 ~/workspace/.env.keys 에서 GEMINI_API_KEY 로드."""
    import os, re
    # 환경변수 우선
    if os.environ.get("GEMINI_API_KEY"):
        return os.environ["GEMINI_API_KEY"]
    # .env.keys 파일
    for path in ["/home/jay/workspace/.env.keys", "/home/jay/workspace/.env"]:
        if Path(path).exists():
            content = Path(path).read_text(encoding="utf-8")
            m = re.search(r"GEMINI_API_KEY\s*=\s*\"?([^\"\n]+)\"?", content)
            if m:
                return m.group(1).strip()
    return None


def get_gcloud_token() -> str:
    """gcloud auth print-access-token 으로 Bearer 토큰 획득."""
    try:
        result = subprocess.run(
            ["gcloud", "auth", "print-access-token"],
            capture_output=True, text=True, check=True,
        )
        token = result.stdout.strip()
        if token:
            return token
    except Exception as e:
        print(f"[WARN] gcloud CLI 실패: {e}")
    raise RuntimeError("gcloud 토큰 획득 실패")


# ─────────────────────────────────────────────────────────────────
# Gemini 이미지 생성
# ─────────────────────────────────────────────────────────────────

def get_bearer_token() -> str:
    """SA 서비스 계정 또는 gcloud CLI를 통해 Bearer 토큰을 반환합니다."""
    # SA 토큰 우선
    try:
        import sys
        sys.path.insert(0, str(TOOLS_DIR))
        import gcloud_auth
        return gcloud_auth.get_service_account_token()
    except Exception:
        pass
    # gcloud CLI fallback
    return get_gcloud_token()


def generate_bg_image(slide_key: str, prompt: str, output_path: Path) -> bool:
    """Gemini API로 배경 이미지를 생성해서 저장합니다. 성공 시 True."""

    # Bearer 토큰 방식 (SA 우선, gcloud CLI fallback)
    try:
        token = get_bearer_token()
    except RuntimeError as e:
        print(f"[ERROR] 인증 실패: {e}")
        return False

    url = f"{GEMINI_API_BASE}/models/{MODEL_ID}:generateContent"
    headers = {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json",
    }

    payload = {
        "contents": [{"parts": [{"text": prompt}]}],
        "generationConfig": {"responseModalities": ["IMAGE", "TEXT"]},
    }

    print(f"  [{slide_key}] Gemini 이미지 생성 요청 중... (모델: {MODEL_ID})")
    start = time.time()

    try:
        response = requests.post(url, headers=headers, json=payload, timeout=180)
        response.raise_for_status()
    except requests.HTTPError as e:
        print(f"  [ERROR] HTTP {e.response.status_code}: {e.response.text[:300]}")
        # fallback 모델
        fallback_model = "gemini-2.5-flash-image"
        print(f"  [RETRY] {fallback_model}로 재시도...")
        url = f"{GEMINI_API_BASE}/models/{fallback_model}:generateContent"
        try:
            response = requests.post(url, headers=headers, json=payload, timeout=180)
            response.raise_for_status()
        except Exception as e2:
            print(f"  [ERROR] 재시도도 실패: {e2}")
            return False

    elapsed = time.time() - start
    data = response.json()

    candidates = data.get("candidates", [])
    if not candidates:
        print(f"  [ERROR] candidates 없음: {json.dumps(data)[:200]}")
        return False

    parts = candidates[0].get("content", {}).get("parts", [])
    image_part = next((p for p in parts if "inlineData" in p), None)
    if image_part is None:
        text_parts = [p.get("text", "") for p in parts if "text" in p]
        print(f"  [ERROR] 이미지 데이터 없음. 텍스트: {text_parts[:1]}")
        return False

    mime_type = image_part["inlineData"].get("mimeType", "image/jpeg")
    image_bytes = base64.b64decode(image_part["inlineData"]["data"])

    ext = ".jpg" if "jpeg" in mime_type else ".png"
    actual_path = output_path.with_suffix(ext)
    actual_path.write_bytes(image_bytes)
    size_kb = actual_path.stat().st_size / 1024
    print(f"  [{slide_key}] 배경 이미지 저장: {actual_path.name} ({size_kb:.0f}KB, {elapsed:.1f}초)")
    return True


# ─────────────────────────────────────────────────────────────────
# HTML 템플릿 생성
# ─────────────────────────────────────────────────────────────────

GOOGLE_FONTS_IMPORT = """
@import url('https://fonts.googleapis.com/css2?family=Black+Han+Sans&family=Noto+Sans+KR:wght@400;500;700;900&family=Pretendard:wght@400;600;700;900&display=swap');
"""

# Pretendard는 로컬 설치 경로 사용 (Google Fonts에 없음)
LOCAL_FONT_FACE = """
@font-face {
  font-family: 'Pretendard';
  src: url('/home/jay/.local/share/fonts/Pretendard/Pretendard-Black.otf') format('opentype');
  font-weight: 900;
}
@font-face {
  font-family: 'Pretendard';
  src: url('/home/jay/.local/share/fonts/Pretendard/Pretendard-Bold.otf') format('opentype');
  font-weight: 700;
}
@font-face {
  font-family: 'Pretendard';
  src: url('/home/jay/.local/share/fonts/Pretendard/Pretendard-SemiBold.otf') format('opentype');
  font-weight: 600;
}
@font-face {
  font-family: 'Pretendard';
  src: url('/home/jay/.local/share/fonts/Pretendard/Pretendard-Regular.otf') format('opentype');
  font-weight: 400;
}
"""


def build_html_slide1(bg_path: str) -> str:
    """슬라이드 1: 문제 공감 (Problem Empathy)"""
    return f"""<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=1080" />
  <title>Slide 01 - 문제 공감</title>
  <style>
    {LOCAL_FONT_FACE}

    *, *::before, *::after {{ box-sizing: border-box; margin: 0; padding: 0; }}

    html, body {{
      width: 1080px;
      height: 1080px;
      overflow: hidden;
      -webkit-font-smoothing: antialiased;
    }}

    /* 배경 레이어 */
    #bg {{
      position: absolute;
      inset: 0;
      background-image: url('{bg_path}');
      background-size: cover;
      background-position: center;
      z-index: 0;
    }}

    /* 하단 그라디언트 오버레이 (가독성용, 하단 40%) */
    #bottom-grad {{
      position: absolute;
      left: 0; right: 0;
      bottom: 0;
      height: 432px; /* 1080 * 0.40 */
      background: linear-gradient(
        to bottom,
        rgba(0, 59, 92, 0) 0%,
        rgba(0, 59, 92, 0.75) 60%,
        rgba(0, 59, 92, 0.92) 100%
      );
      z-index: 1;
    }}

    /* 중앙 상단 약한 다크 오버레이 (배경 텍스트 대비) */
    #top-overlay {{
      position: absolute;
      left: 0; right: 0;
      top: 0;
      height: 720px;
      background: linear-gradient(
        to bottom,
        rgba(0, 20, 40, 0.35) 0%,
        rgba(0, 20, 40, 0.15) 100%
      );
      z-index: 1;
    }}

    /* 콘텐츠 래퍼 */
    #content {{
      position: absolute;
      inset: 0;
      z-index: 10;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
    }}

    /* 헤드라인 블록 (center ~50% 기준) */
    .headline-block {{
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 16px;
      margin-top: -64px; /* 약간 위로 올려 시각적 무게 중심 */
    }}

    .headline-line {{
      font-family: 'Black Han Sans', 'Noto Sans KR', sans-serif;
      font-size: 96px;
      font-weight: 900;
      color: #FFFFFF;
      text-align: center;
      line-height: 1.15;
      letter-spacing: -0.02em;
      word-break: keep-all;
      text-shadow:
        0 2px 12px rgba(0, 0, 0, 0.60),
        0 4px 32px rgba(0, 0, 0, 0.40);
    }}

    .orange {{
      color: #EA580C;
    }}

    /* CTA 바 (하단 고정) */
    #cta-bar {{
      position: absolute;
      left: 0; right: 0;
      bottom: 0;
      height: 128px;
      background: #047857;
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 20;
    }}

    .cta-text {{
      font-family: 'Pretendard', 'Noto Sans KR', sans-serif;
      font-size: 44px;
      font-weight: 700;
      color: #FFFFFF;
      text-align: center;
      letter-spacing: -0.01em;
      word-break: keep-all;
    }}
  </style>
</head>
<body>
  <div id="bg"></div>
  <div id="top-overlay"></div>
  <div id="bottom-grad"></div>

  <div id="content">
    <div class="headline-block">
      <div class="headline-line">열심히는 하는데,</div>
      <div class="headline-line">월급은 <span class="orange">제자리걸음</span>?</div>
    </div>
  </div>

  <div id="cta-bar">
    <span class="cta-text">다른 방법이 있다는 걸 아세요? →</span>
  </div>
</body>
</html>"""


def build_html_slide2(bg_path: str) -> str:
    """슬라이드 2: 긴급성 (Urgency)"""
    return f"""<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=1080" />
  <title>Slide 02 - 긴급성</title>
  <style>
    {LOCAL_FONT_FACE}

    *, *::before, *::after {{ box-sizing: border-box; margin: 0; padding: 0; }}

    html, body {{
      width: 1080px;
      height: 1080px;
      overflow: hidden;
      -webkit-font-smoothing: antialiased;
    }}

    #bg {{
      position: absolute;
      inset: 0;
      background-image: url('{bg_path}');
      background-size: cover;
      background-position: center;
      z-index: 0;
    }}

    /* 상단~중단 오버레이 (텍스트 가독성) */
    #center-overlay {{
      position: absolute;
      inset: 0;
      background: radial-gradient(
        ellipse 90% 70% at 50% 40%,
        rgba(0, 30, 60, 0.50) 0%,
        rgba(0, 10, 30, 0.20) 100%
      );
      z-index: 1;
    }}

    /* 하단 그라디언트 */
    #bottom-grad {{
      position: absolute;
      left: 0; right: 0;
      bottom: 0;
      height: 380px;
      background: linear-gradient(
        to bottom,
        rgba(0, 10, 25, 0) 0%,
        rgba(0, 10, 25, 0.88) 100%
      );
      z-index: 1;
    }}

    #content {{
      position: absolute;
      inset: 0;
      z-index: 10;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      padding: 0 80px;
    }}

    .headline-block {{
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 16px;
      margin-bottom: 48px;
    }}

    .headline-line {{
      font-family: 'Black Han Sans', 'Noto Sans KR', sans-serif;
      font-size: 96px;
      font-weight: 900;
      color: #FFFFFF;
      text-align: center;
      line-height: 1.15;
      letter-spacing: -0.02em;
      word-break: keep-all;
      text-shadow:
        0 2px 12px rgba(0, 0, 0, 0.65),
        0 4px 40px rgba(0, 0, 0, 0.45);
    }}

    .orange {{ color: #EA580C; }}

    .subtext {{
      font-family: 'Pretendard', 'Noto Sans KR', sans-serif;
      font-size: 64px;
      font-weight: 600;
      color: #E0E0E0;
      text-align: center;
      letter-spacing: -0.01em;
      word-break: keep-all;
      text-shadow:
        0 2px 8px rgba(0, 0, 0, 0.55);
    }}

    /* CTA 바 */
    #cta-bar {{
      position: absolute;
      left: 0; right: 0;
      bottom: 0;
      height: 128px;
      background: rgba(0, 20, 45, 0.92);
      border-top: 3px solid rgba(4, 120, 87, 0.60);
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 20;
    }}

    .cta-text {{
      font-family: 'Pretendard', 'Noto Sans KR', sans-serif;
      font-size: 44px;
      font-weight: 700;
      color: #FFFFFF;
      text-align: center;
      letter-spacing: -0.01em;
      word-break: keep-all;
    }}
  </style>
</head>
<body>
  <div id="bg"></div>
  <div id="center-overlay"></div>
  <div id="bottom-grad"></div>

  <div id="content">
    <div class="headline-block">
      <div class="headline-line">2026년 <span class="orange">7월</span>,</div>
      <div class="headline-line">게임이 바뀝니다.</div>
    </div>
    <div class="subtext"><span class="orange">지금</span>이 마지막 타이밍입니다.</div>
  </div>

  <div id="cta-bar">
    <span class="cta-text">변화 전에 움직이세요 →</span>
  </div>
</body>
</html>"""


def build_html_slide3(bg_path: str) -> str:
    """슬라이드 3: 해결책 (Solution)"""
    return f"""<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=1080" />
  <title>Slide 03 - 해결책</title>
  <style>
    {LOCAL_FONT_FACE}

    *, *::before, *::after {{ box-sizing: border-box; margin: 0; padding: 0; }}

    html, body {{
      width: 1080px;
      height: 1080px;
      overflow: hidden;
      -webkit-font-smoothing: antialiased;
    }}

    #bg {{
      position: absolute;
      inset: 0;
      background-image: url('{bg_path}');
      background-size: cover;
      background-position: center;
      z-index: 0;
    }}

    /* 오버레이: 상단 어둡게 하여 헤드라인 가독성 */
    #top-overlay {{
      position: absolute;
      inset: 0;
      background: linear-gradient(
        to bottom,
        rgba(0, 30, 55, 0.55) 0%,
        rgba(0, 30, 55, 0.25) 40%,
        rgba(0, 30, 55, 0.10) 60%,
        rgba(0, 30, 55, 0.40) 100%
      );
      z-index: 1;
    }}

    #content {{
      position: absolute;
      inset: 0;
      z-index: 10;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: flex-start;
      padding-top: 88px;
    }}

    /* 헤드라인 (상단 영역) */
    .headline-block {{
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 16px;
      margin-bottom: 56px;
    }}

    .headline-line {{
      font-family: 'Black Han Sans', 'Noto Sans KR', sans-serif;
      font-size: 84px;
      font-weight: 900;
      color: #FFFFFF;
      text-align: center;
      line-height: 1.18;
      letter-spacing: -0.02em;
      word-break: keep-all;
      text-shadow:
        0 2px 12px rgba(0, 0, 0, 0.65),
        0 4px 32px rgba(0, 0, 0, 0.40);
    }}

    /* 핵심 데이터 블록 (center) */
    .data-block {{
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 16px;
      background: rgba(0, 30, 55, 0.52);
      border: 1px solid rgba(255,255,255,0.12);
      border-radius: 24px;
      padding: 48px 72px;
      backdrop-filter: blur(8px);
      -webkit-backdrop-filter: blur(8px);
    }}

    .hero-number {{
      font-family: 'Pretendard', 'Noto Sans KR', sans-serif;
      font-size: 112px;
      font-weight: 900;
      color: #EA580C;
      text-align: center;
      letter-spacing: -0.03em;
      line-height: 1.0;
      text-shadow:
        0 0 40px rgba(234, 88, 12, 0.45),
        0 2px 8px rgba(0, 0, 0, 0.50);
    }}

    .hero-label {{
      font-family: 'Pretendard', 'Noto Sans KR', sans-serif;
      font-size: 64px;
      font-weight: 700;
      color: #FFFFFF;
      text-align: center;
      letter-spacing: -0.01em;
    }}

    .hero-sub {{
      font-family: 'Pretendard', 'Noto Sans KR', sans-serif;
      font-size: 64px;
      font-weight: 500;
      color: #E0E0E0;
      text-align: center;
      letter-spacing: -0.01em;
    }}

    /* CTA 바 */
    #cta-bar {{
      position: absolute;
      left: 0; right: 0;
      bottom: 0;
      height: 128px;
      background: #047857;
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 20;
    }}

    .cta-text {{
      font-family: 'Pretendard', 'Noto Sans KR', sans-serif;
      font-size: 44px;
      font-weight: 700;
      color: #FFFFFF;
      text-align: center;
      letter-spacing: -0.01em;
      word-break: keep-all;
    }}

    /* 면책 문구 */
    #disclaimer {{
      position: absolute;
      bottom: 144px; /* CTA 바 위 */
      right: 40px;
      font-family: 'Pretendard', 'Noto Sans KR', sans-serif;
      font-size: 40px;
      font-weight: 400;
      color: #CCCCCC;
      text-align: right;
      z-index: 20;
      text-shadow: 0 1px 4px rgba(0,0,0,0.50);
    }}
  </style>
</head>
<body>
  <div id="bg"></div>
  <div id="top-overlay"></div>

  <div id="content">
    <div class="headline-block">
      <div class="headline-line">정착지원금이</div>
      <div class="headline-line">문제를 풀어줍니다.</div>
    </div>

    <div class="data-block">
      <div class="hero-number">최대 1,000만원</div>
      <div class="hero-label">신입 정착지원금</div>
      <div class="hero-sub">경력 직전 연봉 50%까지</div>
    </div>
  </div>

  <div id="cta-bar">
    <span class="cta-text">파격적 지원 확인하기 →</span>
  </div>

  <div id="disclaimer">조건 있음, 상담 시 확인</div>
</body>
</html>"""


# ─────────────────────────────────────────────────────────────────
# Playwright 캡처
# ─────────────────────────────────────────────────────────────────

def capture_slide(html_content: str, output_path: Path) -> None:
    """HTML 콘텐츠를 1080x1080 PNG로 캡처합니다."""
    html_file = output_path.with_suffix(".html")
    html_file.write_text(html_content, encoding="utf-8")

    with sync_playwright() as p:
        browser = p.chromium.launch()
        try:
            page = browser.new_page(viewport={"width": 1080, "height": 1080})
            page.goto(f"file://{html_file.resolve()}", wait_until="networkidle")
            # 폰트 렌더링 완료 대기
            page.wait_for_timeout(2500)
            page.screenshot(path=str(output_path), type="png")
        finally:
            browser.close()

    size_kb = output_path.stat().st_size / 1024
    print(f"  [캡처] {output_path.name} ({size_kb:.0f}KB)")


# ─────────────────────────────────────────────────────────────────
# 메인 실행
# ─────────────────────────────────────────────────────────────────

def main():
    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
    print("=" * 60)
    print("T.O.P 캐러셀 배너 생성 시작 (슬라이드 1-3)")
    print(f"출력 디렉토리: {OUTPUT_DIR}")
    print("=" * 60)

    slides = ["slide-01", "slide-02", "slide-03"]
    bg_paths: dict[str, Path] = {}

    # ── Step 1: 배경 이미지 생성 ──────────────────────────────────
    print("\n[Step 1] Gemini 배경 이미지 생성")
    for slide in slides:
        bg_out = OUTPUT_DIR / f"bg_{slide}"  # 확장자는 generate_bg_image에서 결정
        # 이미 존재하면 스킵
        existing = list(OUTPUT_DIR.glob(f"bg_{slide}.*"))
        if existing:
            bg_paths[slide] = existing[0]
            print(f"  [{slide}] 기존 배경 이미지 사용: {existing[0].name}")
            continue
        ok = generate_bg_image(slide, BG_PROMPTS[slide], bg_out)
        if not ok:
            print(f"  [ERROR] {slide} 배경 생성 실패")
            sys.exit(1)
        # 실제 저장된 파일 경로 찾기
        saved = list(OUTPUT_DIR.glob(f"bg_{slide}.*"))
        if not saved:
            print(f"  [ERROR] 배경 이미지 파일을 찾을 수 없음: bg_{slide}.*")
            sys.exit(1)
        bg_paths[slide] = saved[0]

    # ── Step 2: HTML 생성 + Playwright 캡처 ──────────────────────
    print("\n[Step 2] HTML 오버레이 + PNG 캡처")
    html_builders = {
        "slide-01": build_html_slide1,
        "slide-02": build_html_slide2,
        "slide-03": build_html_slide3,
    }

    for slide in slides:
        bg_path_str = f"file://{bg_paths[slide].resolve()}"
        html = html_builders[slide](bg_path_str)
        out_png = OUTPUT_DIR / f"{slide}.png"
        print(f"  [{slide}] HTML 빌드 + 캡처...")
        capture_slide(html, out_png)

    # ── 결과 요약 ─────────────────────────────────────────────────
    print("\n" + "=" * 60)
    print("생성 완료!")
    print("=" * 60)
    for slide in slides:
        png = OUTPUT_DIR / f"{slide}.png"
        if png.exists():
            size_kb = png.stat().st_size / 1024
            print(f"  OK  {png}  ({size_kb:.0f}KB)")
        else:
            print(f"  FAIL  {png}  (파일 없음)")


if __name__ == "__main__":
    main()
