# -*- coding: utf-8 -*-
"""
키워드 → 병맛 삼국지 마케팅 쇼츠 (완전 자동, 원커맨드)
==========================================================

마케팅하고 싶은 대상을 '자유로운 키워드/문장'으로 주면:
  1) 랜덤으로 삼국지 인물 '썰' 매칭
  2) Gemini 로 병맛 마케팅 나레이션 생성
  3) 장면 분할(스토리보드)
  4) 장면별 이미지 + 음성 생성
  5) Ken Burns + 자막(맨 위) + 음성 합성 → 세로 mp4
  6) 한 작업의 결과(나레이션·스토리보드·이미지·음성·mp4)는
     모두 output/<작업폴더>/ 안에만 저장. 완성 후 중간 파일(assets)은 자동 삭제.

선행: scraper.py 로 만든 data/samguk_keywords_*.csv 가 있어야 함.
API 키: GEMINI_API_KEY 환경변수 (또는 --api-key). 이미지 생성은 결제 활성화 필요.

★ 실행 (shorts_all 폴더 안에서, 키워드는 자유롭게):
    python animation_samgukji\\main.py 감성 펜션 왕의휴식 객실 노천탕 평창 숲속 1박 12만원
    python animation_samgukji\\main.py "강원도 평창 '왕의휴식' 감성 펜션, 객실 노천탕, 1박 12만원"
    python animation_samgukji\\main.py https://naver.me/xxxx

옵션:
    --label 왕의휴식     결과 폴더 이름에 쓸 라벨 (기본: 키워드 앞부분 자동)
    --king 조조          특정 인물 고정 (기본: 랜덤)
    --scenes 12          장면 수=이미지 수 (기본 12)
    --seconds 45         목표 길이 초 (기본 45)
    --voice Puck         TTS 목소리
    --keep               중간 파일(assets) 삭제하지 않고 보존
"""

import argparse
import json
import os
import re
import shutil
import sys
from datetime import datetime
from pathlib import Path

if hasattr(sys.stdout, "reconfigure"):
    sys.stdout.reconfigure(encoding="utf-8")
    sys.stderr.reconfigure(encoding="utf-8")

from google import genai

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))  # 공유 모듈(naver.py, media.py)

import gen                       # 나레이션 + 스토리보드 생성(병합: 이전 gen_narration + gen_storyboard)
import render as MS              # animation 영상합성 엔진(이전 make_short.py)
import naver as NV               # 공유 네이버 크롤러/브리핑/사진 선별

HERE = Path(__file__).parent
OUT_DIR = HERE.parent / "output" / "animation_samgukji"   # 통합 출력: shorts_all/output/animation_samgukji/
TEXT_MODEL = "gemini-2.5-flash"


def safe_name(s: str) -> str:
    s = re.sub(r"[^\w가-힣]+", "_", s).strip("_")
    return s[:30] or "short"


def main():
    ap = argparse.ArgumentParser(
        description="키워드 또는 네이버 링크 → 병맛 삼국지 쇼츠 (완전 자동)",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="예) python main.py 감성 펜션 왕의휴식 객실 노천탕 1박 12만원\n"
               "    python main.py https://naver.me/xxxx   (링크만 — AI브리핑 자동 키워드화)",
    )
    # 마케팅 대상: 자유 키워드(여러 단어/문장) 또는 네이버 링크(섞어 써도 됨)
    ap.add_argument("keywords", nargs="*",
                    help="마케팅 대상 키워드/문장, 또는 네이버 링크 (--link 없이 링크만 줘도 됨)")
    # 결과 폴더 라벨
    ap.add_argument("--label", help="결과 폴더 라벨 (기본: 키워드 앞부분 자동)")
    # 생성 옵션
    ap.add_argument("--king", help="특정 인물 고정 (예: 조조, 기본: 랜덤)")
    ap.add_argument("--scenes", type=int, default=12, help="장면 수=이미지 수 (기본 12)")
    ap.add_argument("--seconds", type=int, default=45, help="목표 길이(초) (기본 45)")
    ap.add_argument("--voice", default=MS.TTS_VOICE, help=f"TTS 목소리 (기본 {MS.TTS_VOICE})")
    ap.add_argument("--keep", action="store_true", help="중간 파일(assets) 보존")
    ap.add_argument("--csv", help="썰 CSV 경로 (기본: output/ 최신)")
    # 실제 사진(가게/메뉴판) 섞기 + 마지막 링크 카드
    ap.add_argument("--images", default="load_image",
                    help="실제 사진 폴더. 가게 이름만 줘도 load_image/<가게>/ 를 찾음 "
                         "(예: --images 연산골막국수). 비었으면 전부 AI")
    ap.add_argument("--max-real", type=int, default=0,
                    help="실제 사진으로 채울 최대 장면 수 (기본 0=폴더 사진 전부, 장면 수 한도 내)")
    ap.add_argument("--real-scenes",
                    help="실제 사진 깔 장면 번호 1-based, 콤마구분 (예: 5,6,7). 주면 내용매칭 대신 수동")
    ap.add_argument("--no-match", action="store_true",
                    help="실제 사진 내용 자동매칭(Gemini 비전) 끄고 위치 기반(뒤쪽 장면)으로")
    ap.add_argument("--photos", type=int, default=4,
                    help="네이버 사진탭에서 자동 선별할 공식사진 수 (기본 4, --link 줄 때)")
    ap.add_argument("--no-photos", action="store_true",
                    help="네이버 사진 자동 수집 끄기 (전부 AI 그림으로)")
    ap.add_argument("--link", help="맨 마지막 화면에 띄울 링크/문구 (예: naver.me/xxxx)")
    ap.add_argument("--link-seconds", type=float, default=3.0, help="링크 카드 길이(초)")
    ap.add_argument("--store", help="링크 카드에 함께 띄울 가게 이름 (기본: 키워드 첫 부분 자동)")
    ap.add_argument("--cta", default=MS.LINK_CTA, help=f"링크 카드 안내문구 (기본: '{MS.LINK_CTA}')")
    ap.add_argument("--img-model", default=MS.IMG_MODEL,
                    help=f"이미지 생성 모델 (기본 {MS.IMG_MODEL}). 글자 또렷하게: gemini-3.1-flash-image / gemini-3-pro-image")
    ap.add_argument("--allow-text", action="store_true",
                    help="그림 안 글자 금지 해제 (신형 모델로 간판에 진짜 이름 넣고 싶을 때)")
    # 컷편집 느낌 (말 빠르게 + 컷 사이 침묵 제거 → 분량 ↓)
    ap.add_argument("--speed", type=float, default=1.3,
                    help="말 속도 배율 (기본 1.3=30%% 빠르게, 음정 유지). 1.0=원본")
    ap.add_argument("--no-trim", action="store_true",
                    help="컷 앞뒤 침묵 제거 끄기 (기본은 제거해 컷을 타이트하게)")
    ap.add_argument("--tail", type=float, default=0.0,
                    help="각 컷 말 끝난 뒤 여운(초). 기본 0=말 끝나자마자 다음 컷. 여유 주려면 0.1~0.2")
    ap.add_argument("--workers", type=int, default=4,
                    help="이미지·음성 동시 생성 수 (기본 4). 높이면 빠르지만 API 속도제한 주의")
    ap.add_argument("--bgm",
                    help="배경음악(병맛 BGM) 파일 또는 폴더. 폴더면 무작위 1곡. "
                         "기본: bgm/ 폴더에 곡이 있으면 자동으로 무작위 사용. "
                         "특정 곡 고정하려면 --bgm 파일명. BGM 끄려면 --bgm none")
    ap.add_argument("--bgm-volume", type=float, default=0.3,
                    help="BGM 볼륨 0~1 (기본 0.3). 나레이션 안 묻히게 조절")
    ap.add_argument("--api-key")
    args = ap.parse_args()

    # --link 안 써도 됨: 위치 인자에 네이버 링크가 섞여 있으면 그걸 링크로 빼낸다
    #   (narration/kakao 처럼 `python main.py https://naver.me/xxxx` 만으로 실행 가능)
    if not args.link:
        link_tok = next((k for k in args.keywords if NV.looks_like_naver_place(k)), None)
        if link_tok:
            args.link = link_tok
            args.keywords = [k for k in args.keywords if k != link_tok]

    product = " ".join(args.keywords).strip()
    # 키워드를 안 줬는데 --link 가 네이버 플레이스면 → AI 브리핑을 긁어와 마케팅 키워드로 사용
    brief_store = None
    if not product and NV.looks_like_naver_place(args.link or ""):
        print("■ 키워드 없음 → 네이버 AI 브리핑 자동 수집...")
        try:
            brief_store, bullets, _cat = NV.fetch_briefing(args.link)   # 업종(cat)은 animation 미사용
        except Exception as e:
            sys.exit(f"[!] AI 브리핑 수집 실패: {type(e).__name__}: {e}")
        if not bullets:
            sys.exit(f"[!] '{brief_store or '이 가게'}'에 AI 브리핑이 없어요. "
                     "키워드를 직접 주거나, 브리핑 있는 가게 링크를 쓰세요.")
        product = " ".join(bullets)
        print(f"■ 가게: {brief_store or '?'} / 브리핑 {len(bullets)}줄 → 키워드로 사용")
        for b in bullets:
            print(f"   • {b}")
    if not product:
        sys.exit("[!] 마케팅 키워드가 필요해. 예: python auto_short.py 감성 펜션 ... "
                 "또는 --link 에 네이버 가게 링크(AI 브리핑 자동 수집).")

    api_key = args.api_key or os.getenv("GEMINI_API_KEY") or os.getenv("GOOGLE_API_KEY")
    if not api_key:
        sys.exit("[!] API 키 없음. setx GEMINI_API_KEY \"키\" 후 새 터미널, 또는 --api-key.")

    csv_path = Path(args.csv) if args.csv else gen.latest_csv()
    rows = gen.load_rows(csv_path)

    client = genai.Client(api_key=api_key)
    OUT_DIR.mkdir(parents=True, exist_ok=True)
    ts = datetime.now().strftime("%Y%m%d_%H%M%S")

    # 네이버 링크 주고 사진 폴더를 직접 안 줬으면 → 사진탭 공식사진을 자동 수집·선별
    if (not args.no_photos and NV.looks_like_naver_place(args.link or "")
            and args.images == "load_image"):
        store_for_dir = (brief_store or args.store or args.label
                         or " ".join(args.keywords[:2]) or "naver_store")
        photo_dir = HERE / "load_image" / safe_name(store_for_dir)
        existing = [p for p in photo_dir.glob("*.jpg")] if photo_dir.exists() else []
        if existing:
            print(f"■ 사진 캐시 재사용: {photo_dir} ({len(existing)}장)")
            args.images = safe_name(store_for_dir)
        else:
            print(f"■ 네이버 사진탭 → 공식사진 {args.photos}장 자동 수집·선별...")
            try:
                _, finals = NV.fetch_and_select(args.link, photo_dir,
                                                n=args.photos, client=client)
                if finals:
                    args.images = safe_name(store_for_dir)
                    print(f"■ 사진 {len(finals)}장 준비 → {photo_dir}")
                else:
                    print("■ 공식사진을 못 받았어요 → 전부 AI 그림으로 진행")
            except Exception as e:
                print(f"■ 사진 수집 실패({type(e).__name__}) → 전부 AI 그림으로 진행")

    # 이 작업 전용 폴더 (모든 산출물은 여기 안에만)
    label = args.label or " ".join(args.keywords[:3]) or brief_store or product[:20]
    stem = f"{safe_name(label)}_{ts}"
    job_dir = OUT_DIR / stem
    job_dir.mkdir(parents=True, exist_ok=True)

    # 1) 랜덤(또는 지정) 인물 썰 매칭
    seed = gen.pick_seed(rows, king=args.king)
    print(f"■ 작업 폴더   : {job_dir}")
    print(f"■ 마케팅 대상 : {product}")
    print(f"■ 매칭된 썰   : {seed['king']} / \"{seed['keyword']}\"\n")

    # 2) 병맛 나레이션
    print("[1/4] 나레이션 생성...")
    narr = gen.generate(client, TEXT_MODEL, seed, product, args.seconds)
    (job_dir / "narration.json").write_text(
        json.dumps(narr, ensure_ascii=False, indent=2), encoding="utf-8")
    # 사람이 바로 읽는 나레이션 '가사' 텍스트 (제목 + 본문 + 해시태그)
    lyric = (f"[제목] {narr.get('title','')}\n"
             f"[소재] {seed['king']} / \"{seed['keyword']}\"\n"
             f"[마케팅] {product}\n"
             f"{'-'*40}\n"
             f"{narr.get('full_narration','')}\n")
    if narr.get("hashtags"):
        lyric += f"\n{' '.join(narr['hashtags'])}\n"
    (job_dir / "narration.txt").write_text(lyric, encoding="utf-8")
    print(f"      \"{narr['title']}\" ({len(narr['full_narration'])}자)")

    # 3) 스토리보드(장면 분할)
    print("[2/4] 스토리보드 분할...")
    sb = gen.make_storyboard(client, TEXT_MODEL, narr["full_narration"],
                            product, seed["king"], scenes=args.scenes)
    (job_dir / "storyboard.json").write_text(
        json.dumps(sb, ensure_ascii=False, indent=2), encoding="utf-8")
    print(f"      장면 {len(sb['scenes'])}개")

    # 4) 이미지 + 음성 + 영상 합성 (assets·mp4 모두 job_dir 안)
    #    실제 사진(가게/메뉴판)을 내용 맞는 장면에 AI 배경과 합성, 마지막에 링크 카드 추가
    real_images, real_scenes = MS.resolve_real_images(args.images, args.real_scenes)
    if not real_images:
        print(f"■ 실제 사진   : 없음 ('{args.images}' 폴더 비었거나 없음) → 전부 AI")
    # --bgm 안 줘도 bgm/ 폴더에 곡이 있으면 자동으로 무작위 1곡 사용
    bgm = MS.resolve_bgm(args.bgm or "bgm")
    _off = str(args.bgm or "").strip().lower() in ("none", "off", "no")
    if args.bgm and not _off and not bgm:
        print(f"■ BGM '{args.bgm}' 를 못 찾음 → BGM 없이 진행")
    elif bgm:
        print(f"■ BGM        : {bgm.name} (bgm/ 무작위)")
    print("[3/4] 이미지·음성 생성 + [4/4] 영상 합성...")
    store = args.store or brief_store or product.split(",")[0].strip() or None
    out_mp4 = MS.produce(client, sb, stem, workdir=job_dir, voice=args.voice, force=False,
                         real_images=real_images, real_scenes=real_scenes,
                         max_real=args.max_real, match_content=not args.no_match,
                         link=args.link, link_seconds=args.link_seconds, store=store, cta=args.cta,
                         img_model=args.img_model, allow_text=args.allow_text,
                         speed=args.speed, trim_silence=not args.no_trim, tail=args.tail,
                         workers=args.workers, bgm=bgm, bgm_volume=args.bgm_volume)

    # 5) 중간 파일 정리: assets/ + 기계용 json 삭제. 결과 mp4 와 narration.txt(제목/해시태그)만 남김.
    if not args.keep and out_mp4.exists() and out_mp4.stat().st_size > 0:
        shutil.rmtree(job_dir / "assets", ignore_errors=True)
        for junk in ("narration.json", "storyboard.json"):
            (job_dir / junk).unlink(missing_ok=True)
        print("      중간 파일(assets·json) 삭제 완료 — mp4 + narration.txt 만 보존")

    print(f"\n✅ 완성: {out_mp4}")
    print(f"   {seed['king']} 썰 · {product}")


if __name__ == "__main__":
    main()
