#!/usr/bin/env python3
"""generate_training.py — JvG Consultancy training generator (standalone, python-pptx).

Bouwt een housestyle-trainingdeck uit een content-JSON
(schema: scripts/training_content_schema.md). Zelfstandig draaibaar;
geen imports uit andere projectscripts.

CLI:
    python generate_training.py --content content.json --brand neutral --out out.pptx
"""
import argparse
import json
import sys
from pathlib import Path

from pptx import Presentation
from pptx.dml.color import RGBColor
from pptx.enum.shapes import MSO_SHAPE
from pptx.enum.text import MSO_ANCHOR, PP_ALIGN
from pptx.opc.constants import RELATIONSHIP_TYPE as RT
from pptx.oxml.ns import qn
from pptx.util import Emu, Inches, Pt

# ---------------------------------------------------------------- constants --
PRIMARY = "003366"      # JvG donkerblauw
ACCENT = "FF6D00"       # JvG oranje
BODY = "1F2937"         # bodytekst
CARD_BG = "F8F9FA"      # kaartachtergrond
CARD_BORDER = "E5E7EB"  # kaartrand
SUCCESS = "00A859"      # correct / groen
FOOTER_BG = "374151"    # footerbalk
DARK_BG = "0A1628"      # titelfolie achtergrond
WHITE = "FFFFFF"
FONT = "Arial"
SLIDE_W, SLIDE_H = Inches(10), Inches(5.625)
MARGIN = Inches(0.55)
FOOTER_H = Inches(0.42)
JVG_LOGO = Path(__file__).resolve().parent.parent / "assets/jvg-logo-white-large.png"
CLIENT_LOGOS = {
    "lions": Path(__file__).resolve().parent.parent / "assets/branding/clients/lions-logo.png",
}
NEUTRAL_TAGLINE = "JvG Consultancy | Safety • Governance • Advisory"
MAX_BULLETS_PER_CARD = 6


def rgb(hexstr):
    return RGBColor.from_string(hexstr)


# ------------------------------------------------------------------ helpers --
def logo_ratio(path):
    """PIL-ratio (w/h); None als bestand ontbreekt of PIL niet beschikbaar is."""
    try:
        from PIL import Image
        with Image.open(path) as im:
            return im.width / im.height
    except Exception:
        return None


def add_logo(slide, path, x, y, target_h, max_w):
    """Logo met PIL-ratio: hoogte vast (target_h), breedte = h*ratio, max max_w."""
    if not Path(path).exists():
        return None
    ratio = logo_ratio(path) or 1.0
    h = min(int(target_h), int(max_w / ratio))
    w = int(h * ratio)
    return slide.shapes.add_picture(str(path), x, y, width=Emu(w), height=Emu(h))


def set_alpha(shape, pct):
    """Vultransparantie: pct = dekking in procent (20 -> 20% dekking)."""
    try:
        srgb = shape.fill.fore_color._xFill.find(qn("a:srgbClr"))
        srgb.append(srgb.makeelement(qn("a:alpha"), {"val": str(int(pct * 1000))}))
    except Exception:
        pass


def rect(slide, x, y, w, h, color, alpha_pct=None, rotation=None, rounded=False, border=None):
    shp = slide.shapes.add_shape(
        MSO_SHAPE.ROUNDED_RECTANGLE if rounded else MSO_SHAPE.RECTANGLE, x, y, w, h)
    shp.fill.solid()
    shp.fill.fore_color.rgb = rgb(color)
    if alpha_pct is not None:
        set_alpha(shp, alpha_pct)
    if border:
        shp.line.color.rgb = rgb(border)
        shp.line.width = Pt(1)
    else:
        shp.line.fill.background()
    if rotation is not None:
        shp.rotation = rotation
    shp.shadow.inherit = False
    return shp


def text(slide, x, y, w, h, lines, anchor=MSO_ANCHOR.TOP):
    """lines = lijst dicts: t, size, color, bold, align, space_after."""
    box = slide.shapes.add_textbox(x, y, w, h)
    tf = box.text_frame
    tf.word_wrap = True
    tf.vertical_anchor = anchor
    for i, ln in enumerate(lines):
        p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
        p.alignment = ln.get("align", PP_ALIGN.LEFT)
        p.space_after = Pt(ln.get("space_after", 4))
        run = p.add_run()
        run.text = ln["t"]
        f = run.font
        f.name = FONT
        f.size = Pt(ln.get("size", 14))
        f.bold = ln.get("bold", False)
        f.color.rgb = rgb(ln.get("color", BODY))
    return box


def blank(prs):
    return prs.slides.add_slide(prs.slide_layouts[6])


def footer(slide, page_no, label):
    y = SLIDE_H - FOOTER_H
    rect(slide, 0, y, SLIDE_W, FOOTER_H, FOOTER_BG)
    if JVG_LOGO.exists():
        add_logo(slide, JVG_LOGO, Inches(0.15), y + Inches(0.06),
                 FOOTER_H - Inches(0.12), Inches(0.75))
    text(slide, Inches(1.0), y, Inches(6.5), FOOTER_H,
         [{"t": label, "size": 10, "color": WHITE}], anchor=MSO_ANCHOR.MIDDLE)
    text(slide, Inches(8.7), y, Inches(1.15), FOOTER_H,
         [{"t": str(page_no), "size": 10, "color": WHITE, "align": PP_ALIGN.RIGHT}],
         anchor=MSO_ANCHOR.MIDDLE)


def slide_title(slide, title, sub=None):
    text(slide, MARGIN, Inches(0.28), Inches(8.9), Inches(0.6),
         [{"t": title, "size": 26, "bold": True, "color": PRIMARY}])
    rect(slide, MARGIN, Inches(0.88), Inches(8.9), Inches(0.04), ACCENT)
    if sub:
        text(slide, MARGIN, Inches(1.0), Inches(8.9), Inches(0.35),
             [{"t": sub, "size": 13, "color": "6B7280"}])


def chunk(lst, n):
    return [lst[i:i + n] for i in range(0, len(lst), n)]


def add_jump(shape, source_slide, target_slide):
    """Interne slide-jump op gehele vorm: a:hlinkClick op p:cNvPr via lxml."""
    rId = source_slide.part.relate_to(target_slide.part, RT.SLIDE)
    cNvPr = shape._element.nvSpPr.cNvPr
    cNvPr.append(cNvPr.makeelement(
        qn("a:hlinkClick"),
        {qn("r:id"): rId, "action": "ppaction://hlinksldjump"}))


# ------------------------------------------------------------------ slides ---
def build_title(prs, data, brand):
    s = blank(prs)
    rect(s, 0, 0, SLIDE_W, SLIDE_H, DARK_BG)
    rect(s, 0, 0, Inches(7.2), SLIDE_H, PRIMARY, alpha_pct=55)
    rect(s, Inches(7.4), Inches(-1.2), Inches(3.6), Inches(8.4), ACCENT,
         alpha_pct=20, rotation=18)
    text(s, MARGIN, Inches(1.7), Inches(6.2), Inches(1.6),
         [{"t": data.get("titel", "Training"), "size": 40, "bold": True, "color": WHITE}])
    sub = data.get("ondertitel", "")
    if sub:
        text(s, MARGIN, Inches(3.1), Inches(6.2), Inches(0.6),
             [{"t": sub, "size": 18, "color": CARD_BG}])
    meta = "  |  ".join(filter(None, [data.get("type", ""), data.get("duur", "")]))
    if meta:
        text(s, MARGIN, Inches(3.75), Inches(6.2), Inches(0.4),
             [{"t": meta, "size": 13, "color": "9CA3AF"}])
    add_logo(s, JVG_LOGO, Inches(0.55), Inches(0.35), Inches(0.8), Inches(2.0))
    if brand in CLIENT_LOGOS:
        add_logo(s, CLIENT_LOGOS[brand], Inches(7.55), Inches(3.9), Inches(1.1), Inches(2.2))
    return s


def build_menu(prs, data, label):
    s = blank(prs)
    slide_title(s, "Welkom", "Kies een module om te starten")
    cards = []
    cw, ch = Inches(4.25), Inches(1.15)
    gx, gy = Inches(0.35), Inches(0.25)
    for i, mod in enumerate(data.get("modules", [])):
        r, c = divmod(i, 2)
        x = MARGIN + c * (cw + gx)
        y = Inches(1.55) + r * (ch + gy)
        card = rect(s, x, y, cw, ch, CARD_BG, rounded=True, border=CARD_BORDER)
        text(s, x + Inches(0.2), y + Inches(0.14), cw - Inches(0.4), Inches(0.3),
             [{"t": f"Module {i + 1}", "size": 11, "bold": True, "color": ACCENT}])
        text(s, x + Inches(0.2), y + Inches(0.46), cw - Inches(0.4), Inches(0.6),
             [{"t": mod.get("titel", ""), "size": 14, "bold": True, "color": PRIMARY}])
        cards.append(card)
    footer(s, 2, label)
    return s, cards


def build_leerdoelen(prs, data, page, label):
    s = blank(prs)
    slide_title(s, "Leerdoelen", "Na deze training kun je:")
    y = Inches(1.5)
    for doel in data.get("leerdoelen", []):
        badge = rect(s, MARGIN, y + Inches(0.02), Inches(0.32), Inches(0.32),
                     SUCCESS, rounded=True)
        p = badge.text_frame.paragraphs[0]
        p.alignment = PP_ALIGN.CENTER
        run = p.add_run()
        run.text = "✓"
        run.font.name = FONT
        run.font.size = Pt(14)
        run.font.bold = True
        run.font.color.rgb = rgb(WHITE)
        text(s, MARGIN + Inches(0.5), y, Inches(8.3), Inches(0.45),
             [{"t": doel, "size": 15, "color": BODY}])
        y += Inches(0.62)
    footer(s, page, label)
    return s


def build_module(prs, idx, mod, page, label):
    s = blank(prs)
    slide_title(s, f"Module {idx + 1}: {mod.get('titel', '')}")
    bullets = mod.get("bullets", [])
    chunks = chunk(bullets, MAX_BULLETS_PER_CARD) or [[]]
    cw = Inches(8.7) if len(chunks) == 1 else Inches(4.25)
    x = MARGIN
    for c in chunks[:2]:
        rect(s, x, Inches(1.45), cw, Inches(3.4), CARD_BG, rounded=True,
             border=CARD_BORDER)
        lines = [{"t": "•  " + b, "size": 13, "color": BODY, "space_after": 8}
                 for b in c]
        if lines:
            text(s, x + Inches(0.2), Inches(1.65), cw - Inches(0.4), Inches(3.0), lines)
        x += cw + Inches(0.2)
    bronnen = mod.get("bronnen", [])
    if bronnen:
        text(s, MARGIN, Inches(4.95), Inches(8.9), Inches(0.3),
             [{"t": "Bronnen: " + "; ".join(bronnen), "size": 9, "color": "6B7280"}])
    footer(s, page, label)
    return s


def build_table(prs, idx, mod, page, label):
    tabel = mod.get("tabel")
    if not tabel:
        return None
    s = blank(prs)
    slide_title(s, f"Module {idx + 1}: {mod.get('titel', '')} — overzicht")
    headers = tabel.get("headers", [])
    rows = tabel.get("rows", [])
    gt = s.shapes.add_table(len(rows) + 1, max(len(headers), 1),
                            MARGIN, Inches(1.45), Inches(8.9), Inches(3.2))
    table = gt.table
    for c, head in enumerate(headers):
        cell = table.cell(0, c)
        cell.fill.solid()
        cell.fill.fore_color.rgb = rgb(PRIMARY)
        run = cell.text_frame.paragraphs[0].add_run()
        run.text = str(head)
        run.font.name = FONT
        run.font.bold = True
        run.font.size = Pt(13)
        run.font.color.rgb = rgb(WHITE)
    for r, row in enumerate(rows, start=1):
        for c in range(len(headers)):
            cell = table.cell(r, c)
            cell.fill.solid()
            cell.fill.fore_color.rgb = rgb(WHITE if r % 2 else CARD_BG)
            run = cell.text_frame.paragraphs[0].add_run()
            run.text = str(row[c]) if c < len(row) else ""
            run.font.name = FONT
            run.font.size = Pt(12)
            run.font.color.rgb = rgb(BODY)
    footer(s, page, label)
    return s


def build_quiz(prs, data, page, label):
    slides = []
    vragen = data.get("quiz", [])
    for i, q in enumerate(vragen, start=1):
        s = blank(prs)
        slide_title(s, f"Quiz — vraag {i} van {len(vragen)}")
        text(s, MARGIN, Inches(1.25), Inches(8.9), Inches(0.6),
             [{"t": q.get("vraag", ""), "size": 17, "bold": True, "color": BODY}])
        correct = int(q.get("correct", 0))
        y = Inches(2.05)
        for j, a in enumerate(q.get("antwoorden", [])):
            is_ok = (j == correct)
            rect(s, MARGIN, y, Inches(8.9), Inches(0.72),
                 "EAF7F0" if is_ok else CARD_BG, rounded=True,
                 border=SUCCESS if is_ok else CARD_BORDER)
            prefix = "✓  " if is_ok else chr(65 + j) + ".  "
            text(s, MARGIN + Inches(0.25), y + Inches(0.12), Inches(8.4), Inches(0.5),
                 [{"t": prefix + a, "size": 14, "bold": is_ok,
                   "color": SUCCESS if is_ok else BODY}])
            y += Inches(0.88)
        footer(s, page + i - 1, label)
        slides.append(s)
    return slides


def build_bronnen(prs, data, page, label):
    s = blank(prs)
    slide_title(s, "Bronnenlijst")
    lines = [{"t": f"{i}.  {bron}", "size": 14, "color": BODY, "space_after": 10}
             for i, bron in enumerate(data.get("bronnenlijst", []), start=1)]
    text(s, MARGIN, Inches(1.5), Inches(8.9), Inches(3.4), lines)
    footer(s, page, label)
    return s


def build_kernpunten(prs, data, page, label):
    s = blank(prs)
    slide_title(s, "Kernpunten", "Neem dit mee")
    y = Inches(1.5)
    for punt in data.get("kernpunten", []):
        rect(s, MARGIN, y + Inches(0.14), Inches(0.18), Inches(0.18), ACCENT,
             rounded=True)
        text(s, MARGIN + Inches(0.45), y, Inches(8.5), Inches(0.5),
             [{"t": punt, "size": 15, "color": BODY}])
        y += Inches(0.72)
    footer(s, page, label)
    return s


# -------------------------------------------------------------------- main ---
def main():
    ap = argparse.ArgumentParser(description="JvG Consultancy training generator")
    ap.add_argument("--content", required=True, help="pad naar content-JSON")
    ap.add_argument("--brand", default=None,
                    help="neutral | lions | klantnaam (override op JSON-brand)")
    ap.add_argument("--out", required=True, help="uitvoerpad (.pptx)")
    args = ap.parse_args()

    data = json.loads(Path(args.content).read_text(encoding="utf-8"))
    brand = (args.brand or str(data.get("brand", "neutral"))).strip().lower()

    if brand in ("jvg-hseq", "wintershall"):
        from brands import build_jvg_hseq, build_wintershall
        if brand == "jvg-hseq":
            out, kind, n = build_jvg_hseq(data, args.out)
        else:
            out, kind, n = build_wintershall(data, args.out)
        print(f"OK: {out} ({n} slides, brand={brand}, type={kind})")
        return

    label = NEUTRAL_TAGLINE if brand == "neutral" else f"JvG Consultancy × {brand.upper()}"

    prs = Presentation()
    prs.slide_width = SLIDE_W
    prs.slide_height = SLIDE_H

    build_title(prs, data, brand)                      # 1: titelfolie (geen footer)
    menu, cards = build_menu(prs, data, label)         # 2: welkom + menu
    page = 3
    build_leerdoelen(prs, data, page, label)           # 3: leerdoelen
    page += 1
    module_slides = []
    for i, mod in enumerate(data.get("modules", [])):  # 4/5: modules + tabellen
        module_slides.append(build_module(prs, i, mod, page, label))
        page += 1
        if build_table(prs, i, mod, page, label) is not None:
            page += 1
    for _ in build_quiz(prs, data, page, label):       # 6: quiz
        page += 1
    build_bronnen(prs, data, page, label)              # 7: bronnenlijst
    page += 1
    build_kernpunten(prs, data, page, label)           # 8: kernpunten

    for card, target in zip(cards, module_slides):     # menu-jumps achteraf
        add_jump(card, menu, target)

    out = Path(args.out)
    out.parent.mkdir(parents=True, exist_ok=True)
    prs.save(str(out))
    print(f"OK: {out} ({len(prs.slides)} slides, brand={brand})")


if __name__ == "__main__":
    main()
