#!/usr/bin/env python3
"""
populate_assets.py — Populate v2 asset dirs from existing hero/turnaround refs.

Copies current {slug}_hero.{ext} files to assets/{kind}/{slug}/hero.{ext}.
Also updates casting_state.json hero_path values to point to v2 asset paths.

Safe to re-run (idempotent).

Renamed from populate_canonical.py 2026-05-26. The legacy `_canonical/`
folder is gone; the v2 layout uses `assets/identity/` and `assets/loc/`.

Usage:
    python3 -m tools.populate_assets --project afterimage
    python3 -m tools.populate_assets --project afterimage --dry-run
"""

import argparse
import json
import shutil
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent))
from recoil.pipeline._lib.taxonomy import slugify_asset_id
from recoil.core.paths import projects_root, ProjectPaths

TURNAROUND_ANGLES = ("front", "profile", "three_quarter", "back")
LOCATION_VARIANTS = ("wide", "medium", "closeup")
IMAGE_EXTS = (".png", ".jpg", ".jpeg")


def populate_asset_refs(project_name: str, dry_run: bool = False):
    proj_dir = projects_root() / project_name
    paths = ProjectPaths.for_project(project_name)
    # Legacy source layout (pre-v2): output/refs/characters/, output/refs/locations/
    refs_dir = proj_dir / "output" / "refs"
    state_path = paths.casting_state_path

    if not refs_dir.is_dir():
        print(f"ERROR: legacy refs dir not found: {refs_dir}")
        return

    # Load casting_state for hero_path updates
    state = None
    if state_path.is_file():
        try:
            state = json.loads(state_path.read_text(encoding="utf-8"))
        except (json.JSONDecodeError, IOError):
            pass

    print(f"{'[DRY RUN] ' if dry_run else ''}Populating v2 assets/ for: {project_name}")
    copied = 0

    # === Characters → assets/identity/ ===
    chars_dir = refs_dir / "characters"
    if chars_dir.is_dir():
        for char_dir in sorted(chars_dir.iterdir()):
            if not char_dir.is_dir() or char_dir.name.startswith("_"):
                continue
            slug = slugify_asset_id(char_dir.name)
            canonical_char = paths.asset_subject_dir("char", slug)

            # Hero
            for ext in IMAGE_EXTS:
                src = char_dir / f"{slug}_hero{ext}"
                if src.is_file():
                    dest = canonical_char / f"hero{ext}"
                    if not dry_run:
                        dest.parent.mkdir(parents=True, exist_ok=True)
                        # Clean up old extensions to prevent masking
                        for old_hero in canonical_char.glob("hero.*"):
                            old_hero.unlink()
                        shutil.copy2(str(src), str(dest))
                    print(f"  [CHAR] {slug}/hero{ext}")
                    copied += 1
                    # Update casting_state hero_path — case-insensitive slug match
                    if state:
                        chars_dict = state.get("characters", {})
                        cs = None
                        for key in chars_dict:
                            if slugify_asset_id(key) == slug:
                                cs = chars_dict[key]
                                break
                        if cs:
                            rel = canonical_char.relative_to(proj_dir)
                            cs["hero_path"] = str(rel / f"hero{ext}")
                    break

            # Turnarounds
            for angle in TURNAROUND_ANGLES:
                for ext in IMAGE_EXTS:
                    src = char_dir / f"{slug}_{angle}{ext}"
                    if src.is_file():
                        dest = canonical_char / f"{angle}{ext}"
                        if not dry_run:
                            dest.parent.mkdir(parents=True, exist_ok=True)
                            shutil.copy2(str(src), str(dest))
                        print(f"  [CHAR] {slug}/{angle}{ext}")
                        copied += 1
                        break

    # === Locations → assets/loc/ ===
    locs_dir = refs_dir / "locations"
    if locs_dir.is_dir():
        for loc_dir in sorted(locs_dir.iterdir()):
            if not loc_dir.is_dir() or loc_dir.name.startswith("_"):
                continue
            slug = slugify_asset_id(loc_dir.name)
            canonical_loc = paths.asset_subject_dir("loc", slug)

            # Hero
            for ext in IMAGE_EXTS:
                src = loc_dir / f"{slug}_hero{ext}"
                if src.is_file():
                    dest = canonical_loc / f"hero{ext}"
                    if not dry_run:
                        dest.parent.mkdir(parents=True, exist_ok=True)
                        # Clean up old extensions to prevent masking
                        for old_hero in canonical_loc.glob("hero.*"):
                            old_hero.unlink()
                        shutil.copy2(str(src), str(dest))
                    print(f"  [LOC] {slug}/hero{ext}")
                    copied += 1
                    # Update casting_state hero_path
                    if state:
                        locs = state.get("locations", {})
                        ls = locs.get(slug, locs.get(loc_dir.name, {}))
                        if ls:
                            rel = canonical_loc.relative_to(proj_dir)
                            ls["hero_path"] = str(rel / f"hero{ext}")
                    break

            # Angle variants
            for variant in LOCATION_VARIANTS:
                for ext in IMAGE_EXTS:
                    src = loc_dir / f"{slug}_{variant}{ext}"
                    if src.is_file():
                        dest = canonical_loc / f"{variant}{ext}"
                        if not dry_run:
                            dest.parent.mkdir(parents=True, exist_ok=True)
                            shutil.copy2(str(src), str(dest))
                        print(f"  [LOC] {slug}/{variant}{ext}")
                        copied += 1
                        break

    # Save updated casting_state
    if state and not dry_run:
        shutil.copy2(str(state_path), str(state_path.with_suffix(".json.pre-v2-assets")))
        state_path.write_text(json.dumps(state, indent=2), encoding="utf-8")
        print(f"  [STATE] Updated casting_state.json hero_path values")

    print(f"\n{'[DRY RUN] ' if dry_run else ''}Done: {copied} files copied to assets/")


def main():
    parser = argparse.ArgumentParser(description="Populate v2 assets/ from existing hero/turnaround refs")
    parser.add_argument("--project", "-p", required=True, help="Project name")
    parser.add_argument("--dry-run", "-d", action="store_true", help="Preview without changes")
    args = parser.parse_args()
    populate_asset_refs(args.project, dry_run=args.dry_run)


if __name__ == "__main__":
    main()
