"""Copies practitioner MP3s to audio_phone/ with phone-friendly names + ID3 tags + cover.

For each module:
  - rename to M{N}-{idx:02d}-{tema}.mp3
  - tag: title, album="NLP Practitioner - Modul N - Tema", artist, track, year, genre
  - embed PNG cover (generated with Pillow) showing module + theme

Run: .venv/Scripts/python.exe rename_phone_practitioner.py
"""
from __future__ import annotations

import json
import shutil
import sys
from pathlib import Path

from PIL import Image, ImageDraw, ImageFont
from mutagen.id3 import APIC, ID3, ID3NoHeaderError, TALB, TIT2, TPE1, TPE2, TRCK, TYER, TCON
from mutagen.mp3 import MP3

ROOT = Path(__file__).parent
PRACT = ROOT / "nlp-practitioner"
AUDIO_SRC = PRACT / "audio"
AUDIO_DST = PRACT / "audio_phone"
COVERS_DIR = PRACT / "audio_phone" / "_covers"
MANIFEST = PRACT / "manifest.json"

THEMES = {
    1: {"slug": "PACING_RAPORT",            "title_full": "Pacing si Raport",                       "filename_phrase": "Pacing si Raport"},
    2: {"slug": "SUBMODALITATI",            "title_full": "Submodalitati si VAK",                   "filename_phrase": "Submodalitati"},
    3: {"slug": "OBIECTIVE",                "title_full": "Setarea si Atingerea Obiectivelor",      "filename_phrase": "Obiective"},
    4: {"slug": "NIVELURI_NEUROLOGICE",     "title_full": "Niveluri Neurologice si Metamodel",      "filename_phrase": "Niveluri Neurologice"},
    5: {"slug": "MILTON_MODEL",             "title_full": "Modelul Milton, Hipnoza si Asertivitate","filename_phrase": "Modelul Milton"},
    6: {"slug": "RECADRARE_PARTI",          "title_full": "Recadrare si Parti Interioare",          "filename_phrase": "Recadrare si Parti"},
    7: {"slug": "PARTI_INTERIOARE",         "title_full": "Parti Interioare si Triunghi Dramatic",  "filename_phrase": "Parti Interioare"},
    8: {"slug": "INTEGRARE_MICROSTRATEGII", "title_full": "Integrare, Linia Timpului, Microstrategii","filename_phrase": "Microstrategii"},
}

ARTIST = "cursnlp.ro"
ALBUM_ARTIST = "Horia Radu si Marius Mutu"
YEAR = "2024"
GENRE = "Speech"
COURSE_PREFIX = "NLP Practitioner"


def find_font(size: int) -> ImageFont.FreeTypeFont:
    candidates = [
        "C:/Windows/Fonts/segoeuib.ttf",
        "C:/Windows/Fonts/arialbd.ttf",
        "C:/Windows/Fonts/arial.ttf",
        "C:/Windows/Fonts/segoeui.ttf",
    ]
    for c in candidates:
        if Path(c).exists():
            return ImageFont.truetype(c, size)
    return ImageFont.load_default()


def wrap_text(text: str, font: ImageFont.FreeTypeFont, max_width: int, draw: ImageDraw.ImageDraw) -> list[str]:
    words = text.split()
    lines: list[str] = []
    cur = ""
    for w in words:
        trial = (cur + " " + w).strip()
        bbox = draw.textbbox((0, 0), trial, font=font)
        if bbox[2] - bbox[0] <= max_width:
            cur = trial
        else:
            if cur:
                lines.append(cur)
            cur = w
    if cur:
        lines.append(cur)
    return lines


# Per-module accent colors (calm, distinct, readable on white text)
PALETTE = {
    1: ("#1f4e79", "#2e75b6"),  # blue
    2: ("#5b2c6f", "#9b59b6"),  # purple
    3: ("#922b21", "#cd6155"),  # red
    4: ("#1e8449", "#52be80"),  # green
    5: ("#7e5109", "#d68910"),  # amber/gold
    6: ("#0e6655", "#48c9b0"),  # teal
    7: ("#4a235a", "#7d3c98"),  # deep purple
    8: ("#212f3c", "#566573"),  # slate
}


def make_cover(module_num: int, theme_full: str, out_path: Path) -> Path:
    """Generate 1000x1000 PNG cover with gradient + module + theme text."""
    size = 1000
    img = Image.new("RGB", (size, size), "#ffffff")
    draw = ImageDraw.Draw(img)
    c1, c2 = PALETTE.get(module_num, ("#222", "#666"))

    def hex_to_rgb(h: str) -> tuple[int, int, int]:
        h = h.lstrip("#")
        return int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)

    r1, g1, b1 = hex_to_rgb(c1)
    r2, g2, b2 = hex_to_rgb(c2)
    for y in range(size):
        t = y / (size - 1)
        r = int(r1 + (r2 - r1) * t)
        g = int(g1 + (g2 - g1) * t)
        b = int(b1 + (b2 - b1) * t)
        draw.line([(0, y), (size, y)], fill=(r, g, b))

    # Course label (small, top)
    f_small = find_font(36)
    label = COURSE_PREFIX + " - 2024"
    bbox = draw.textbbox((0, 0), label, font=f_small)
    draw.text(((size - (bbox[2] - bbox[0])) / 2, 60), label, font=f_small, fill="#ffffff")

    # Big module number
    f_huge = find_font(280)
    mod_text = f"M{module_num}"
    bbox = draw.textbbox((0, 0), mod_text, font=f_huge)
    draw.text(((size - (bbox[2] - bbox[0])) / 2, 160), mod_text, font=f_huge, fill="#ffffff")

    # "MODUL N" word
    f_med = find_font(54)
    sub = f"MODUL {module_num}"
    bbox = draw.textbbox((0, 0), sub, font=f_med)
    draw.text(((size - (bbox[2] - bbox[0])) / 2, 470), sub, font=f_med, fill="#ffffff")

    # Theme (wrapped)
    f_theme = find_font(64)
    lines = wrap_text(theme_full, f_theme, max_width=size - 120, draw=draw)
    y = 600
    for line in lines:
        bbox = draw.textbbox((0, 0), line, font=f_theme)
        draw.text(((size - (bbox[2] - bbox[0])) / 2, y), line, font=f_theme, fill="#ffffff")
        y += int((bbox[3] - bbox[1]) * 1.4)

    # Footer
    f_foot = find_font(28)
    foot = ARTIST
    bbox = draw.textbbox((0, 0), foot, font=f_foot)
    draw.text(((size - (bbox[2] - bbox[0])) / 2, size - 70), foot, font=f_foot, fill="#ffffff")

    out_path.parent.mkdir(parents=True, exist_ok=True)
    img.save(out_path, "PNG", optimize=True)
    return out_path


def safe_filename_part(s: str) -> str:
    bad = '<>:"/\\|?*'
    for ch in bad:
        s = s.replace(ch, "")
    return s.strip()


def tag_mp3(mp3_path: Path, *, title: str, album: str, track_num: int, track_total: int, cover_png: Path) -> None:
    try:
        tags = ID3(mp3_path)
        tags.delete()
    except ID3NoHeaderError:
        tags = ID3()

    tags.add(TIT2(encoding=3, text=title))
    tags.add(TALB(encoding=3, text=album))
    tags.add(TPE1(encoding=3, text=ARTIST))
    tags.add(TPE2(encoding=3, text=ALBUM_ARTIST))
    tags.add(TRCK(encoding=3, text=f"{track_num}/{track_total}"))
    tags.add(TYER(encoding=3, text=YEAR))
    tags.add(TCON(encoding=3, text=GENRE))

    with open(cover_png, "rb") as f:
        img_bytes = f.read()
    tags.add(APIC(encoding=3, mime="image/png", type=3, desc="Cover", data=img_bytes))

    tags.save(mp3_path, v2_version=3)


def main() -> int:
    if not MANIFEST.exists():
        print(f"ERROR: manifest not found: {MANIFEST}")
        return 1

    with MANIFEST.open(encoding="utf-8") as f:
        manifest = json.load(f)

    AUDIO_DST.mkdir(parents=True, exist_ok=True)
    COVERS_DIR.mkdir(parents=True, exist_ok=True)

    total_copied = 0
    total_skipped = 0
    total_errors = 0

    for mod in manifest["modules"]:
        name = mod["name"]  # "Modul N"
        try:
            mod_num = int(name.split()[-1])
        except Exception:
            print(f"SKIP module without numeric suffix: {name}")
            continue

        theme = THEMES.get(mod_num)
        if theme is None:
            print(f"SKIP module {mod_num}: no theme defined")
            continue

        audio_lectures = [lec for lec in mod.get("lectures", []) if lec.get("type") == "audio"]
        if not audio_lectures:
            continue

        cover_path = COVERS_DIR / f"M{mod_num}.png"
        if not cover_path.exists():
            make_cover(mod_num, theme["title_full"], cover_path)
            print(f"  cover: {cover_path.name}")

        album = f"{COURSE_PREFIX} - Modul {mod_num} - {theme['title_full']}"
        track_total = len(audio_lectures)

        out_dir = AUDIO_DST / f"M{mod_num} - {theme['filename_phrase']}"
        out_dir.mkdir(parents=True, exist_ok=True)

        for idx, lec in enumerate(audio_lectures, start=1):
            src_path_str = lec.get("audio_path", "")
            src = ROOT / src_path_str
            if not src.exists():
                print(f"  MISSING src: {src}")
                total_errors += 1
                continue

            phrase = safe_filename_part(theme["filename_phrase"])
            new_name = f"M{mod_num}-{idx:02d}-{phrase}.mp3"
            dst = out_dir / new_name

            if dst.exists() and dst.stat().st_size == src.stat().st_size:
                print(f"  skip (already copied): {new_name}")
                total_skipped += 1
            else:
                shutil.copy2(src, dst)
                print(f"  copied: {new_name}")
                total_copied += 1

            title_short = lec.get("title", new_name)
            track_title = f"M{mod_num} {title_short} - {theme['filename_phrase']}"
            try:
                tag_mp3(
                    dst,
                    title=track_title,
                    album=album,
                    track_num=idx,
                    track_total=track_total,
                    cover_png=cover_path,
                )
            except Exception as e:
                print(f"  TAG ERROR for {new_name}: {e}")
                total_errors += 1

    print(f"\nDONE. copied={total_copied} skipped={total_skipped} errors={total_errors}")
    print(f"Output: {AUDIO_DST}")
    return 0 if total_errors == 0 else 2


if __name__ == "__main__":
    sys.exit(main())
