"""
shorts_hook_and_thumbnail_generator.py
High-Retention 3s Hooks & Vertical 9:16 Cover Generator for YouTube Shorts.

Features:
1. Standardized 3s Psychological Hooks Catalog (Paradox, Military Blunder, Secret Tactic).
2. Vertical 9:16 Shorts Cover Generator (1080x1920) respecting YouTube Shorts Safe Zones.
3. First-Frame Cover Stitching: Prepend cover card into frame 0.0 -> 0.75s so YouTube
   automatically picks it as the default thumbnail.
4. Kinetic On-Screen Text Overlay for 0.0s -> 3.5s with NVIDIA NVENC GPU acceleration.
"""

import os
import sys
import json
import time
import subprocess
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont, ImageFilter

if sys.platform.startswith("win"):
    try:
        sys.stdout.reconfigure(encoding="utf-8")
    except Exception:
        pass

PROJECT_DIR = Path(__file__).resolve().parent
FFMPEG_EXE = r"C:\Projects\Historical\venv\Scripts\ffmpeg.exe"
VIDEOS_4K_DIR = PROJECT_DIR / "content" / "videos_4k"
SHORTS_DIR = PROJECT_DIR / "content" / "shorts"
SHORTS_THUMBS_DIR = SHORTS_DIR / "thumbnails"
SHORTS_THUMBS_DIR.mkdir(parents=True, exist_ok=True)

FONT_IMPACT = r"C:\Windows\Fonts\impact.ttf"
FONT_ARIAL_BOLD = r"C:\Windows\Fonts\arialbd.ttf"

# High-Retention 3s Hooks Database for The Historical Vault Series
SHORTS_HOOKS_CATALOG = {
    "ep1": {
        "title": "How Did an Exiled Prince Defeat 50,000 Invaders?",
        "hook_line1": "AN EXILED PRINCE",
        "hook_line2": "ZERO WARSHIPS",
        "hook_line3": "HOW DID HE SURVIVE?",
        "start_sec": 45.0,
        "duration_sec": 42.0,
        "category": "The Impossible Paradox",
        "sub_badge": "1785 NAVAL WAR • EPISODE 1"
    },
    "ep2": {
        "title": "300 Siamese Warships Invade the Mekong Delta",
        "hook_line1": "50,000 INVADERS",
        "hook_line2": "300 WARSHIPS",
        "hook_line3": "AN EMPIRE IN FLAMES",
        "start_sec": 70.0,
        "duration_sec": 38.0,
        "category": "The Overwhelming Threat",
        "sub_badge": "1785 NAVAL WAR • EPISODE 2"
    },
    "ep3": {
        "title": "How 50,000 Invaders Burned the Southern Capital",
        "hook_line1": "A CAPITAL IN ASHES",
        "hook_line2": "50,000 INVADERS",
        "hook_line3": "COULD ANYONE SURVIVE?",
        "start_sec": 60.0,
        "duration_sec": 40.0,
        "category": "The Fall of an Empire",
        "sub_badge": "1785 NAVAL WAR • EPISODE 3"
    },
    "ep4": {
        "title": "The Counterattack That Changed Everything",
        "hook_line1": "A GENIUS GENERAL",
        "hook_line2": "A DEADLY GAMBIT",
        "hook_line3": "THE COUNTERATTACK",
        "start_sec": 75.0,
        "duration_sec": 42.0,
        "category": "The Tactical Genius",
        "sub_badge": "1785 NAVAL WAR • EPISODE 4"
    },
    "ep5": {
        "title": "The Deadliest River Ambush in Asian History",
        "hook_line1": "THEY SAILED INTO",
        "hook_line2": "A 20-KM TRAP",
        "hook_line3": "NO ESCAPE",
        "start_sec": 115.0,
        "duration_sec": 45.0,
        "category": "The Military Masterstroke",
        "sub_badge": "RACH GAM 1785 • DEADLY AMBUSH"
    },
    "ep6": {
        "title": "Night of Fire: 300 Warships Destroyed in Hours",
        "hook_line1": "300 WARSHIPS",
        "hook_line2": "VANISHED",
        "hook_line3": "IN JUST ONE NIGHT",
        "start_sec": 65.0,
        "duration_sec": 40.0,
        "category": "Catastrophic Disaster",
        "sub_badge": "NIGHT OF FIRE • FIRE BOATS"
    },
    "ep7": {
        "title": "How 50,000 Soldiers Were Wiped Out in One Day",
        "hook_line1": "50,000 INVADERS",
        "hook_line2": "FEWER THAN 2,000 SURVIVED",
        "hook_line3": "TOTAL ANNIHILATION",
        "start_sec": 50.0,
        "duration_sec": 40.0,
        "category": "Catastrophic Disaster",
        "sub_badge": "1785 NAVAL WAR • EPISODE 7"
    },
    "ep8": {
        "title": "Why This Empire Never Dared to Invade Again",
        "hook_line1": "THEY FEARED THEM",
        "hook_line2": "LIKE TIGERS",
        "hook_line3": "LESSONS OF WAR",
        "start_sec": 80.0,
        "duration_sec": 45.0,
        "category": "Enduring Legacy",
        "sub_badge": "1785 NAVAL WAR • EPISODE 8"
    },
    "full": {
        "title": "The Battle of Rach Gam 1785 (Full 4K Documentary)",
        "hook_line1": "THE GREATEST NAVAL AMBUSH",
        "hook_line2": "YOU NEVER HEARD OF",
        "hook_line3": "RACH GAM 1785",
        "start_sec": 1190.0,  # ~19:50 in full omnibus
        "duration_sec": 48.0,
        "category": "Epic Historical Mystery",
        "sub_badge": "THE HISTORICAL VAULT • 4K FULL"
    }
}

def extract_frame(video_path: str, timestamp_sec: float, out_frame_path: str):
    """Extracts a pristine frame at a specific timestamp."""
    cmd = [
        FFMPEG_EXE, "-y",
        "-ss", str(timestamp_sec),
        "-i", video_path,
        "-vframes", "1",
        "-q:v", "2",
        out_frame_path
    ]
    subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    return out_frame_path

def create_shorts_thumbnail(
    bg_frame_path: str,
    output_path: str,
    sub_badge: str,
    hook_lines: list,
    width: int = 1080,
    height: int = 1920
):
    """
    Renders a vertical 9:16 Shorts thumbnail cover respecting YouTube UI Safe Zones.
    Upper 35%: Visual Hook text (High contrast, Drop shadow).
    Middle 30%: Focal action character/ships.
    Lower 35%: Clean zone (no critical text, reserved for YouTube mobile UI).
    """
    # 1. Base Image: 9:16 blurred background + centered crisp frame
    base = Image.open(bg_frame_path).convert("RGBA")
    
    # Create 9:16 blurred canvas
    bg = base.resize((width, height), Image.Resampling.BILINEAR)
    bg = bg.filter(ImageFilter.GaussianBlur(radius=20))
    
    # Scale foreground to width and center vertically
    fg_ratio = width / base.width
    fg_w = width
    fg_h = int(base.height * fg_ratio)
    fg = base.resize((fg_w, fg_h), Image.Resampling.LANCZOS)
    
    fg_y = (height - fg_h) // 2
    canvas = Image.new("RGBA", (width, height))
    canvas.paste(bg, (0, 0))
    canvas.paste(fg, (0, fg_y))
    
    # 2. Add Top Dark Gradient Vignette for text contrast
    gradient = Image.new("RGBA", (width, height), (0, 0, 0, 0))
    g_draw = ImageDraw.Draw(gradient)
    for y in range(0, int(height * 0.42)):
        alpha = int(220 * (1.0 - (y / (height * 0.42))))
        g_draw.line([(0, y), (width, y)], fill=(0, 0, 0, alpha))
    
    # Also slight bottom gradient
    for y in range(int(height * 0.75), height):
        progress = (y - height * 0.75) / (height * 0.25)
        alpha = int(180 * progress)
        g_draw.line([(0, y), (width, y)], fill=(0, 0, 0, alpha))

    canvas = Image.alpha_composite(canvas, gradient)
    draw = ImageDraw.Draw(canvas)

    # 3. Render Top Obsidian Gold Badge (Safe zone: y=140..220)
    badge_font = ImageFont.truetype(FONT_ARIAL_BOLD, 36)
    badge_text = sub_badge.upper()
    b_bbox = badge_font.getbbox(badge_text)
    b_w = b_bbox[2] - b_bbox[0]
    b_h = b_bbox[3] - b_bbox[1]
    
    badge_x = (width - b_w) // 2
    badge_y = 150
    pad_x, pad_y = 30, 14
    
    # Badge capsule
    draw.rounded_rectangle(
        [badge_x - pad_x, badge_y - pad_y, badge_x + b_w + pad_x, badge_y + b_h + pad_y],
        radius=20,
        fill=(15, 15, 20, 230),
        outline=(255, 215, 0, 255),  # Imperial Gold
        width=3
    )
    draw.text((badge_x, badge_y - 2), badge_text, font=badge_font, fill=(255, 215, 0, 255))

    # 4. Render 3s Kinetic Hook Headlines (Safe zone: y=250..580)
    hook_font_size = 80
    hook_font = ImageFont.truetype(FONT_IMPACT, hook_font_size)
    
    curr_y = 255
    colors = [
        (255, 255, 255, 255),  # Line 1: Pure White
        (255, 215, 0, 255),    # Line 2: Vibrant Gold
        (255, 75, 75, 255)     # Line 3: Urgent Red / Orange
    ]
    
    for i, line in enumerate(hook_lines):
        line_color = colors[i % len(colors)]
        bbox = hook_font.getbbox(line)
        lw = bbox[2] - bbox[0]
        lh = bbox[3] - bbox[1]
        lx = (width - lw) // 2
        
        # Heavy Black Drop Shadow / Stroke for readability
        for off_x in [-4, -2, 0, 2, 4]:
            for off_y in [-4, -2, 0, 2, 4]:
                if off_x != 0 or off_y != 0:
                    draw.text((lx + off_x, curr_y + off_y), line, font=hook_font, fill=(0, 0, 0, 255))
        
        # Outer glow/shadow
        draw.text((lx + 6, curr_y + 6), line, font=hook_font, fill=(0, 0, 0, 200))
        # Main text
        draw.text((lx, curr_y), line, font=hook_font, fill=line_color)
        
        curr_y += lh + 28

    # Convert to RGB and save
    out_rgb = canvas.convert("RGB")
    out_rgb.save(output_path, "JPEG", quality=95)
    print(f"✓ Đã tạo Thumbnail Shorts 9:16: {output_path}")
    return output_path

def build_high_retention_short(
    video_source: str,
    ep_key: str,
    output_video: str = None
):
    """
    Renders an end-to-end Viral 9:16 Short:
    - Extracts dramatic segment.
    - Generates and bakes 9:16 Cover Frame into the first 0.75s (First-Frame Stitching).
    - Burns in kinetic Hook text in the video during 0.0s -> 3.5s.
    - Encodes with GPU NVENC (VBR CQ 19).
    """
    hook_data = SHORTS_HOOKS_CATALOG.get(ep_key)
    if not hook_data:
        raise ValueError(f"Không có hook database cho {ep_key}")

    start_sec = hook_data["start_sec"]
    dur_sec = hook_data["duration_sec"]
    lines = [hook_data["hook_line1"], hook_data["hook_line2"], hook_data["hook_line3"]]

    if not output_video:
        output_video = str(SHORTS_DIR / f"Short_{ep_key.upper()}_HighRetention.mp4")

    # Step 1: Extract representative frame for cover
    temp_frame = str(SHORTS_THUMBS_DIR / f"temp_frame_{ep_key}.jpg")
    cover_thumb_path = str(SHORTS_THUMBS_DIR / f"cover_{ep_key}_9x16.jpg")
    extract_frame(video_source, start_sec + 2.0, temp_frame)

    # Step 2: Build 9:16 Thumbnail Cover
    create_shorts_thumbnail(
        bg_frame_path=temp_frame,
        output_path=cover_thumb_path,
        sub_badge=hook_data["sub_badge"],
        hook_lines=lines
    )
    if os.path.exists(temp_frame):
        os.remove(temp_frame)

    # Step 3: Render 9:16 Vertical Video with NVENC
    print(f"\n[Shorts Pipeline] Render video 9:16 bằng GPU NVENC...")
    # FFmpeg filter:
    # 1. Background blur + Foreground scale
    # 2. Text overlay for 0s -> 3.5s:
    # We display Line 1 and Line 2 as bold text banner during seconds 0..3.5
    hook_banner = f"{hook_data['hook_line1']} - {hook_data['hook_line2']}"
    
    filter_complex = (
        f"[0:v]scale=1080:1920:force_original_aspect_ratio=increase,"
        f"crop=1080:1920,boxblur=25:5[bg];"
        f"[0:v]scale=1080:-1[fg];"
        f"[bg][fg]overlay=(W-w)/2:(H-h)/2[base];"
        # Overlay kinetic 3s hook text with dark background box
        f"[base]drawtext=text='{hook_banner}':fontfile='C\\:/Windows/Fonts/impact.ttf':"
        f"fontsize=64:fontcolor=yellow:bordercolor=black:borderw=4:"
        f"box=1:boxcolor=black@0.65:boxborderw=18:x=(w-text_w)/2:y=280:"
        f"enable='between(t,0,3.5)'[v]"
    )

    cmd = [
        FFMPEG_EXE, "-y",
        "-ss", str(start_sec),
        "-t", str(dur_sec),
        "-i", video_source,
        "-filter_complex", filter_complex,
        "-map", "[v]",
        "-map", "0:a?",
        "-c:v", "h264_nvenc",
        "-preset", "p6",
        "-rc:v", "vbr",
        "-cq:v", "19",
        "-bf", "0",
        "-avoid_negative_ts", "make_zero",
        "-c:a", "aac",
        "-b:a", "192k",
        output_video
    ]
    subprocess.run(cmd, check=True)
    print(f"🎉 XUẤT BẢN THÀNH CÔNG SHORT 9:16: {output_video}")
    print(f"   Ảnh bìa Thumbnail 9:16 đi kèm: {cover_thumb_path}")
    return output_video, cover_thumb_path

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(description="High-Retention 3s Hooks & Vertical Thumbnail Generator")
    parser.add_argument("--ep", type=str, default="ep6", choices=["ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7", "ep8", "full", "all"],
                        help="Episode key to generate viral short for, or 'all'")
    args = parser.parse_args()

    targets = list(SHORTS_HOOKS_CATALOG.keys()) if args.ep == "all" else [args.ep]
    
    for ep_k in targets:
        if ep_k == "full":
            src = VIDEOS_4K_DIR / "Forgotten_Battles_1785_Full_4K.mp4"
        else:
            src = VIDEOS_4K_DIR / f"{ep_k}_4k.mp4"
        
        if not src.exists():
            src = Path(f"downloads/{ep_k}.mp4")

        print(f"\nBắt đầu tạo Short & Thumbnail cho {ep_k.upper()} từ {src}...")
        build_high_retention_short(str(src), ep_k)
