#!/usr/bin/env python3
"""Hook video for the Durgam Cheruvu series (ManaNagaram desk).

Renders 1920x1080 "paper" editorial frames with Pillow, narrates each scene with
edge-tts, renders one mp4 per scene (slow zoom), concatenates, done.
"""
import json
import os
import subprocess

BASE = "/home/workspace/ManaNagaram/video/durgam-cheruvu-hook"
W, H = 1920, 1080
FPS = 30
VOICE = "en-IN-PrabhatNeural"

PAPER = (251, 250, 247)
INK = (22, 21, 15)
MUTED = (109, 103, 94)
TEAL = (11, 110, 95)
OCHRE = (168, 85, 10)
RED = (179, 38, 30)
RULE = (230, 225, 216)

F_BLACK = "/usr/share/fonts/opentype/inter/Inter-Black.otf"
F_BOLD = "/usr/share/fonts/opentype/inter/Inter-Bold.otf"
F_SEMI = "/usr/share/fonts/opentype/inter/Inter-SemiBold.otf"
F_MED = "/usr/share/fonts/opentype/inter/Inter-Medium.otf"
F_REG = "/usr/share/fonts/opentype/inter/Inter-Regular.otf"
F_TE = "/usr/share/fonts/truetype/noto/NotoSansTelugu-Bold.ttf"

from PIL import Image, ImageDraw, ImageFont

SCENES = [
    {
        "id": "s1",
        "kicker": "DURGAM CHERUVU · MADHAPUR · HYDERABAD",
        "headline": [("You have driven", INK), ("past this lake.", INK)],
        "unit": "Between Madhapur, Jubilee Hills and Raidurg — the lake the cable bridge crosses.",
        "body": ["Four hundred years ago it supplied drinking water to Golconda Fort.",
                 "Today it sits in the middle of the city's tech corridor."],
        "foot": "start here — chapter 01",
        "narration": "Durgam Cheruvu — in the middle of Hyderabad's IT corridor, four hundred years old. Under the Qutb Shahis, it supplied the drinking water for Golconda Fort."},
    {
        "id": "s2",
        "kicker": "WHAT YOU ARE ACTUALLY LOOKING AT",
        "headline": [("It is not a pond.", INK)],
        "unit": "A cheruvu is an engineered tank — a bund, a sluice, a surplus weir, and a catchment it was sized for.",
        "body": ["That one fact explains almost every lake dispute in this city.",
                 "The shoreline is not the lake. The slopes above it are."],
        "foot": "our framing — see chapter 01",
        "narration": "It is not a pond. A cheruvu is an engineered tank: a bund, a sluice, and a catchment it was sized for. Its health is decided uphill, not at the shoreline."},
    {
        "id": "s3",
        "kicker": "AUGUST 2026",
        "headline": [("₹2,833", TEAL), ("crore", INK)],
        "unit": "two plots, about 300 m from the water — a record rate for Hyderabad",
        "body": ["Sold at e-auction by the state's own infrastructure corporation.",
                 "Ten point six three acres, carved out of a single survey number."],
        "foot": "official claim — TGIIC e-auction result",
        "narration": "Now the number. In August twenty twenty-six, two plots about three hundred metres from the water sold for two thousand eight hundred and thirty-three crore rupees. A record for this city."},
    {
        "id": "s4",
        "kicker": "THE PARCELS",
        "headline": [("10.63 acres", INK)],
        "unit": "Survey No. 83/1, Raidurg Panmaktha · plots P1 and P2",
        "stats": [("₹266.53 cr", "per acre realised"), ("₹175 cr", "per acre reserve set")],
        "body": ["TGIIC — the Telangana State Industrial Infrastructure Corporation."],
        "foot": "official claim — auction brochure",
        "narration": "Ten point six three acres, out of Survey Number Eighty-three by one, at Raidurg Panmaktha. Two hundred and sixty-six crore rupees an acre, against a reserve of one seventy-five."},
    {
        "id": "s5",
        "kicker": "AND IT IS NOT EVEN CLEANLY OWNED",
        "headline": [("Six claimants.", INK)],
        "unit": "One survey number. One 2007 judgment that recorded them all.",
        "chips": ["TGIIC", "State Bank of India", "Jamia Nizamia (Waqf claim)",
                  "DLF (allotment resumed)", "Co-operative housing society", "2007 judgment"],
        "foot": "court finding — see chapter 04",
        "narration": "But that survey number is not cleanly owned. Six parties claim an interest: the state's infrastructure corporation, the State Bank of India, a Waqf claim from Jamia Nizamia, DLF, a co-operative housing society — and a two thousand seven judgment that recorded them all."},
    {
        "id": "s6",
        "kicker": "WHAT ELSE IS ON THAT GROUND",
        "headline": [("2.5 billion", OCHRE), ("years old", INK)],
        "unit": "Listed under G.O. Ms. No. 4 of 2003 · Society to Save Rocks precinct",
        "body": ["The auction went ahead anyway. No stay is recorded against it."],
        "foot": "our inference — no stay recorded against the auction",
        "narration": "The ground is older still. These rocks are two and a half billion years old, listed under a Government Order from two thousand three. The auction went ahead anyway."},
    {
        "id": "s7",
        "kicker": "MEANWHILE, THE LAKE ITSELF",
        "headline": [("2.7 mg/l", RED)],
        "unit": "dissolved oxygen — fish need 5.0",
        "bar": True,
        "body": ["August 2026: mass fish deaths. Two treatment plants running beyond capacity.",
                 "Untreated sewage discharge stated on the record by officials."],
        "foot": "official claim — TGPCB / irrigation officials",
        "narration": "And the lake itself is failing. Dissolved oxygen: two point seven milligrams per litre. Fish need five. In August, fish died here in thousands."},
    {
        "id": "s8",
        "kicker": "ONE CORRECTION, BECAUSE IT GETS REPEATED",
        "headline": [("Not a notified", RED), ("Biodiversity Heritage Site", INK)],
        "unit": "Ameenpur Lake — notified 4 November 2016 — is Telangana's only one.",
        "body": ["Durgam Cheruvu is reported as a site in contention. No notification followed.",
                 "If you repeat the claim, this is where it goes wrong."],
        "foot": "court finding — Biological Diversity Act, s.41",
        "narration": "One correction, because it gets repeated everywhere. Durgam Cheruvu is not a notified Biodiversity Heritage Site. Ameenpur Lake, twenty sixteen, is Telangana's only one."},
    {
        "id": "s9",
        "kicker": "మన నగరం · MANANAGARAM",
        "headline": [("Thirteen chapters.", TEAL)],
        "unit": "Every figure labelled: official · court · activist · inference.",
        "body": ["Every chapter links its sources, so you can check it yourself.",
                 "cashlessconsumer.zo.space/durgam-cheruvu"],
        "foot": "Hyderabad civic-rights desk",
        "narration": "Thirteen chapters. Every figure labelled by what kind of claim it is. The whole story, sourced, at Mana Nagaram dot cashless consumer dot zo dot space."},
]


def font(path, size):
    return ImageFont.truetype(path, size)


def wrap(draw, text, fnt, max_w):
    words, lines, cur = text.split(), [], ""
    for w in words:
        t = (cur + " " + w).strip()
        if draw.textlength(t, font=fnt) <= max_w:
            cur = t
        else:
            if cur:
                lines.append(cur)
            cur = w
    if cur:
        lines.append(cur)
    return lines


SS = 2  # supersample factor for crisp downscaled text


def render(scene, out_png):
    w, h = W * SS, H * SS
    img = Image.new("RGB", (w, h), PAPER)
    d = ImageDraw.Draw(img)
    M = 120 * SS  # margin

    # top rule + kicker
    d.line([(M, 92 * SS), (w - M, 92 * SS)], fill=RULE, width=2 * SS)
    d.text((M, 118 * SS), scene["kicker"], font=font(F_BOLD, 26 * SS), fill=TEAL)

    # Telugu wordmark top-right
    te = "మన నగరం"
    d.text((w - M - d.textlength(te, font=font(F_TE, 30 * SS)), 112 * SS), te, font=font(F_TE, 30 * SS), fill=MUTED)

    y = 250 * SS

    # headline
    for text, color in scene["headline"]:
        fnt = font(F_BLACK, (128 if len(text) < 16 else 104) * SS)
        d.text((M, y), text, font=fnt, fill=color)
        y += int(fnt.size * 1.02)

    y += 40 * SS

    # unit line
    f_unit = font(F_MED, 40 * SS)
    for ln in wrap(d, scene["unit"], f_unit, w - 2 * M - 200 * SS):
        d.text((M, y), ln, font=f_unit, fill=MUTED)
        y += 54 * SS

    y += 26 * SS

    # stats row
    for label, sub in scene.get("stats", []):
        f1, f2 = font(F_BOLD, 60 * SS), font(F_REG, 28 * SS)
        d.text((M, y), label, font=f1, fill=INK)
        d.text((M + d.textlength(label, font=f1) + 24 * SS, y + 28 * SS), sub, font=f2, fill=MUTED)
        y += 90 * SS

    # bar for the DO scene
    if scene.get("bar"):
        bx0, bx1, by = M, M + 980 * SS, y + 10 * SS
        d.rounded_rectangle([bx0, by, bx1, by + 26 * SS], radius=13 * SS, fill=(238, 233, 226))
        d.rounded_rectangle([bx0, by, bx0 + int((bx1 - bx0) * 2.7 / 5.0), by + 26 * SS], radius=13 * SS, fill=RED)
        d.line([(bx0 + int((bx1 - bx0) * 5.0 / 5.0), by - 10 * SS), (bx0 + int((bx1 - bx0) * 5.0 / 5.0), by + 36 * SS)],
               fill=INK, width=3 * SS)
        d.text((bx1 + 24 * SS, by - 4 * SS), "5.0 = healthy", font=font(F_MED, 26 * SS), fill=MUTED)
        y += 80 * SS

    # chips
    chips = scene.get("chips", [])
    if chips:
        f_chip = font(F_SEMI, 28 * SS)
        cx, cy = M, y + 6 * SS
        for c in chips:
            wd = int(d.textlength(c, font=f_chip)) + 56 * SS
            if cx + wd > w - M:
                cx, cy = M, cy + 78 * SS
            d.rounded_rectangle([cx, cy, cx + wd, cy + 58 * SS], radius=29 * SS, outline=TEAL, width=2 * SS)
            d.text((cx + 28 * SS, cy + 14 * SS), c, font=f_chip, fill=INK)
            cx += wd + 20 * SS
        y = cy + 90 * SS

    # body lines
    f_body = font(F_REG, 34 * SS)
    for para in scene.get("body", []):
        for ln in wrap(d, para, f_body, w - 2 * M - 260 * SS):
            d.text((M, y), ln, font=f_body, fill=INK)
            y += 48 * SS
        y += 14 * SS

    # footer
    f_foot = font(F_MED, 24 * SS)
    d.line([(M, h - 116 * SS), (w - M, h - 116 * SS)], fill=RULE, width=2 * SS)
    d.text((M, h - 92 * SS), scene["foot"], font=f_foot, fill=MUTED)
    right = "Durgam Cheruvu, from zero"
    d.text((w - M - d.textlength(right, font=f_foot), h - 92 * SS), right, font=f_foot, fill=TEAL)

    img.save(out_png)


def sh(cmd):
    r = subprocess.run(cmd, capture_output=True, text=True)
    if r.returncode != 0:
        raise SystemExit(f"FAILED: {' '.join(cmd)}\n{r.stderr[-2000:]}")
    return r


def duration(path):
    r = sh(["ffprobe", "-v", "quiet", "-show_entries", "format=duration", "-of", "csv=p=0", path])
    return float(r.stdout.strip())


def main():
    audio_dir = os.path.join(BASE, "audio")
    rd = os.path.join(BASE, "renders")
    os.makedirs(rd, exist_ok=True)

    parts = []
    for i, sc in enumerate(SCENES, 1):
        png = os.path.join(rd, f"{sc['id']}.png")
        mp3 = os.path.join(audio_dir, f"{sc['id']}.mp3")
        render(sc, png)

        sh(["edge-tts", "--voice", VOICE, "--rate=+8%", "--text", sc["narration"], "--write-media", mp3])
        dur = duration(mp3) + 0.45
        frames = int(dur * FPS)

        out = os.path.join(rd, f"{sc['id']}.mp4")
        sh([
            "ffmpeg", "-y", "-framerate", str(FPS), "-loop", "1", "-t", f"{dur:.3f}", "-i", png,
            "-i", mp3,
            "-filter_complex",
            f"[0:v]zoompan=z='min(zoom+0.00035,1.055)':x='iw/2-(iw/zoom/2)':y='ih/2-(ih/zoom/2)':"
            f"d=1:s={W * SS}x{H * SS}:fps={FPS},scale={W}:{H}:flags=lanczos,format=yuv420p[v];"
            f"[1:a]apad,aresample=48000[a]",
            "-map", "[v]", "-map", "[a]", "-t", f"{dur:.3f}",
            "-c:v", "libx264", "-preset", "medium", "-crf", "20",
            "-c:a", "aac", "-b:a", "160k", "-movflags", "+faststart",
            out,
        ])
        print(f"  {sc['id']}: {dur:.1f}s -> {out}")
        parts.append(out)

    with open(os.path.join(BASE, "concat_list.txt"), "w") as f:
        for p in parts:
            f.write(f"file '{p}'\n")

    final = os.path.join(BASE, "durgam-cheruvu-hook.mp4")
    sh(["ffmpeg", "-y", "-f", "concat", "-safe", "0",
        "-i", os.path.join(BASE, "concat_list.txt"),
        "-c", "copy", "-movflags", "+faststart", final])
    print(f"\nFINAL: {final}  {duration(final):.1f}s")


if __name__ == "__main__":
    main()
