#!/usr/bin/env python3
"""
Generate 100 lyrics→visual scene description sets for Pop songs.

Issue: timmy-config #606
Output: training-data/scene-descriptions-pop.jsonl
Also mirrors to ~/.hermes/training-data/scene-descriptions-pop.jsonl

Format per training-data/schema.json:
  song, artist, genre, bpm, beat, timestamp, duration_seconds,
  lyric_line, scene { mood, colors, composition, camera, description }
"""

import json
import os
from pathlib import Path

GENRE = "Pop"
DURATION_SECONDS = 4.0

SONGS = [
    {
        "title": "Neon Heartbeat",
        "artist": "Synthwave Collective",
        "bpm": 120,
        "lyrics": [
            "I saw your light through the pouring rain / A neon signal cutting through the pain",
            "We're dancing on the edge of something real / The bass drops low and I can finally feel",
            "Spin me faster through the galaxy / Every atom screaming you and me",
            "The morning comes like an unpaid debt / The glow sticks fade but I don't forget",
            "Sitting in the car with the engine off / Replaying every word, every laugh, every cough",
            "Your silhouette against the vending machine glow / The only currency I want to know",
            "Radio static, your voice cuts through / Telling me things I already knew",
            "Streetlights blur like watercolor tears / We've been running from our fears for years",
            "Hold my hand till the sunrise breaks / Promise me this is not a mistake",
            "The city exhales and we exhale too / Neon heartbeat, me and you",
        ],
        "moods": [
            "hopeful", "euphoric", "euphoric", "bittersweet", "reflective",
            "tender", "intimate", "nostalgic", "yearning", "peaceful",
        ],
        "colors": [
            ["pink", "cyan"],
            ["neon pink", "electric blue", "white"],
            ["gold", "hot pink", "silver"],
            ["purple", "grey"],
            ["midnight blue", "soft gold"],
            ["amber", "magenta"],
            ["steel blue", "pale yellow"],
            ["violet", "teal"],
            ["coral", "indigo"],
            ["rose", "sky blue"],
        ],
        "compositions": [
            "wide establishing shot", "medium close-up", "dynamic tracking",
            "static intimate", "low angle", "extreme close-up", "overhead",
            "two-shot", "profile", "aerial",
        ],
        "cameras": [
            "slow dolly in", "handheld follow", "steady tracking", "locked off",
            "crane up", "push in", "whip pan", "steadicam orbit", "rack focus",
            "drone pull-back",
        ],
        "descriptions": [
            "Rain-slicked streets reflect neon signs in puddles. A lone figure stands under a flickering marquee, face half-lit by pink and cyan fluorescence.",
            "Crowded dance floor, bodies moving in synchronized abandon. Laser beams cut through haze as the drop hits.",
            "Cosmic visualization—two figures spin in zero gravity surrounded by stardust trails and holographic constellations.",
            "Empty warehouse at dawn. Discarded glow sticks scatter the floor like fallen stars. One person sits against the wall, head in hands.",
            "Interior car scene, dashboard glow illuminating tired eyes. Rain streaks the windshield. A cassette tape hangs from the rearview.",
            "Late-night convenience store. Fluorescent hum. Two people share a candy bar, laughter echoing off linoleum tiles.",
            "Bedroom at 2 AM. Radio dial glows amber. One person lies on their back, phone pressed to ear, staring at ceiling cracks.",
            "Rain-blurred windshield, streetlights smearing like wet paint. Hands grip steering wheel at ten and two, knuckles white.",
            "Rooftop at sunrise. Two silhouettes against a gradient sky. City skyline behind them, steam rising from coffee cups.",
            "Wide cityscape at dawn. Neon signs flicker off one by one. Two figures walk away from camera down an empty boulevard.",
        ],
    },
    {
        "title": "Sugar Wounds",
        "artist": "Candy Knife",
        "bpm": 128,
        "lyrics": [
            "You taste like summer but you leave like winter / Sweet on the tongue, then the frost sets in",
            "I keep your letters in a mason jar / Every word a sugar-coated scar",
            "The carnival left town but I'm still here / Riding the Ferris wheel of fear",
            "Cotton candy dreams dissolve in rain / Sticky fingers, nothing to gain",
            "You called me darling then you called me gone / Left me humming the same old song",
            "Baking cookies at 3 AM / Pretending you're coming home again",
            "The recipe says two cups of trust / I measured wrong and now it's dust",
            "Pinwheel spinning in the garden bed / Childhood wishes, long since dead",
            "I wear your sweater like armor now / Too big, too warm, too full of how",
            "Sugar wounds don't bleed, they ache / A cavity where love used to bake",
        ],
        "moods": [
            "bittersweet", "melancholic", "anxious", "defeated", "lonely",
            "nostalgic", "resigned", "wistful", "guarded", "aching",
        ],
        "colors": [
            ["candy pink", "ice blue"],
            ["pastel yellow", "dusty rose"],
            ["rust", "faded teal"],
            ["lavender", "grey"],
            ["sepia", "burnt orange"],
            ["warm amber", "cream"],
            ["charcoal", "pale peach"],
            ["sunbleached red", "mint green"],
            ["oatmeal", "cranberry"],
            ["vanilla", "ash"],
        ],
        "compositions": [
            "close-up", "medium shot", "wide shot", "intimate two-shot",
            "extreme close-up", "over-the-shoulder", "high angle", "low angle",
            "profile", "silhouette",
        ],
        "cameras": [
            "slow push in", "static", "gentle zoom", "locked off",
            "handheld subtle", "crane down", "dolly out", "pan across",
            "tilt down", "pull focus",
        ],
        "descriptions": [
            "Close-up of lips touching a popsicle, summer light streaming through window blinds. Ice crystals melting on warm skin.",
            "Hands placing folded letters into a glass jar on a shelf. Dust motes dance in afternoon light.",
            "Abandoned carnival at dusk. A lone Ferris wheel car sways in wind. Ticket stubs scatter across dirt.",
            "Rain dissolving cotton candy on a stick. Sticky rivulets run down a child's arm. Tears mix with rain.",
            "Empty hallway, one coat still on the hook. Shoes by the door. A phone buzzes unanswered on the table.",
            "Kitchen at night. Flour dusts the counter. One person shapes dough alone, radio playing static.",
            "Measuring cups tipped over on tile. A broken eggshell. Crumbs scattered like promises.",
            "Garden at golden hour. A pinwheel spins lazily. Overgrown grass whispers against a rusted swing set.",
            "Oversized sweater sleeves covering hands. One person hugs themselves, looking out rain-streaked glass.",
            "Empty baking sheet in an empty oven. Timer dings. No one comes to check.",
        ],
    },
    {
        "title": "Parallel Lines",
        "artist": "Geometry Club",
        "bpm": 115,
        "lyrics": [
            "We walk the same street but opposite sides / Measured distance, parallel lives",
            "Your window faces mine across the alley / I count the seconds till your light goes daily",
            "Same train, different cars, same destination / Silent calculation, endless hesitation",
            "The geometry of almost touching / Close enough to feel the air rushing",
            "You order coffee the way I would / If I had the nerve to stand where you stood",
            "Two shadows on the pavement never meet / A mathematician's defeat",
            "I drew a line from my heart to yours / It went on forever, never reached the door",
            "Same playlist, same headphones, different ears / Soundtrack to twenty-seven years",
            "The map says we're zero miles apart / But zero's just a fancy way to say 'apart'",
            "Parallel lines at infinity / Maybe there is where you'll wait for me",
        ],
        "moods": [
            "longing", "observational", "wistful", "frustrated", "timid",
            "melancholic", "hopeful", "nostalgic", "resigned", "transcendent",
        ],
        "colors": [
            ["graphite", "pale blue"],
            ["warm white", "shadow grey"],
            ["burnt sienna", "slate"],
            ["silver", "dusty pink"],
            ["espresso", "cream"],
            ["charcoal", "amber"],
            ["navy", "copper"],
            ["black", "neon green"],
            ["white", "pale gold"],
            ["indigo", "starlight white"],
        ],
        "compositions": [
            "split frame", "deep focus", "symmetrical", "asymmetrical",
            "wide", "extreme wide", "overhead", "profile", "mirror shot",
            "silhouette",
        ],
        "cameras": [
            "static split", "slow pan", "dolly parallel", "zoom",
            "whip pan", "tracking", "crane up", "tilt", "mirror glide",
            "fade to white",
        ],
        "descriptions": [
            "Split screen: two figures walking the same street, separated by a yellow line. Rain falls on one side, sun on the other.",
            "Opposite apartment windows at night. Two silhouettes brush teeth in sync, separated by brick and glass.",
            "Train platform. Two figures board different cars of the same train. Hands press against windows, never aligned.",
            "Close-up of two hands almost touching, separated by a thin glass partition. Breath fogging the surface.",
            "Coffee shop queue. One person watches another order. Steam rises between them like a curtain.",
            "Aerial shot of two shadows on pavement, walking parallel. The sun sets, stretching them impossibly long.",
            "Hand drawing a line on paper with a ruler. The pencil lead snaps at the edge of the page.",
            "Two people on a bench, headphones in, eyes closed. A leaf falls between them. Neither moves.",
            "Map spread on a table. Two pins, zero distance apart. A finger traces the gap, again and again.",
            "Two lines of light converging at a vanishing point. A figure stands at the intersection, arms open.",
        ],
    },
    {
        "title": "Glass House",
        "artist": "Transparent",
        "bpm": 105,
        "lyrics": [
            "Everyone can see what we're not saying / The glass walls amplify the silence",
            "Throwing stones from inside the frame / We're the exhibit, we're the game",
            "Dinner at eight, the neighbors watch / Every fork, every sip, every botched",
            "Smile for the camera, smile for the crowd / Smile till the cracks get too loud",
            "I swept the shards beneath the rug / But glass shines through, no matter what",
            "The thermostat's set to 'pretend' / Comfortable lies with a dividend",
            "Your reflection multiplies in the hall / Seven versions of you, none of them real at all",
            "We built this palace out of pride / Now we're just fossils trapped inside",
            "A bird flew straight into the pane / I felt the vibration, I felt the pain",
            "Let's break a window, let the air in / Better to bleed than to live in sin",
        ],
        "moods": [
            "tense", "claustrophobic", "performative", "desperate", "anxious",
            "resigned", "surreal", "trapped", "empathetic", "liberating",
        ],
        "colors": [
            ["clear", "pale grey"],
            ["silver", "white"],
            ["bone", "chrome"],
            ["porcelain", "charcoal"],
            ["frost", "steel"],
            ["ivory", "smoke"],
            ["mirror", "shadow"],
            ["glass green", "pale amber"],
            ["feather white", "blood red"],
            ["sky blue", "shattered crystal"],
        ],
        "compositions": [
            "extreme wide", "fish-eye", "reflected", "framed",
            "voyeuristic", "intimate", "distorted", "symmetrical",
            "close-up", "liberation wide",
        ],
        "cameras": [
            "static observe", "dolly zoom", "mirror pan", "through-glass",
            "peeping tom", "rack focus", "kaleidoscope spin", "crane down",
            "slow motion", "explosive pull-back",
        ],
        "descriptions": [
            "Modernist glass house at twilight. Two figures sit in silence at opposite ends of a long table, fully visible to the outside world.",
            "Fish-eye lens: a figure stands in a room of mirrors, surrounded by infinite reflections. Stones in hand, hesitating.",
            "Voyeuristic shot from outside: neighbors watch through binoculars as a couple eats dinner. Every gesture is staged.",
            "Close-up of a forced smile, cracks spiderwebbing across porcelain skin. The smile stays but the eyes betray.",
            "Rug lifted slightly, revealing glittering glass shards underneath. A hand reaches to smooth the rug back down.",
            "Hallway of mirrors. Seven reflections of the same person walk in imperfect sync. The real one stops; reflections continue.",
            "Glass palace interior. Fossils embedded in translucent walls. Two small figures wander like museum visitors in their own home.",
            "A bird's eye view of a glass roof. A small bird lies stunned on the surface. A hand presses against the glass from below.",
            "Slow motion: a hammer swings toward a glass wall. The moment of impact suspended, fractures beginning to bloom.",
            "Explosion of glass outward. Sunlight floods in. Two figures stand in the center, arms raised, bleeding and laughing.",
        ],
    },
    {
        "title": "Summer Static",
        "artist": "Beach Fossil",
        "bpm": 98,
        "lyrics": [
            "The radio hisses between the stations / Your voice cuts through like irrigation",
            "Sand in the sheets, salt in the hair / We didn't go anywhere but we were there",
            "Popsicle sticks stacked like little bones / Monument to days without phones",
            "Your tan line tells a story I can't read / A paperback abandoned in the weeds",
            "Fireflies in a mason jar / Released too soon, didn't get far",
            "The boardwalk creaks with memories / Of people we were, people we'll never be",
            "Static on the line, then your laugh / Half a conversation, broken in half",
            "We buried a time capsule in the dune / Marked it with a plastic spoon",
            "The ice cream truck plays a funeral dirge / Summer's over, nothing left to purge",
            "I'll meet you where the static ends / Where the signal clears and the heart mends",
        ],
        "moods": [
            "nostalgic", "lazy", "playful", "curious", "bittersweet",
            "melancholic", "fragmented", "hopeful", "resigned", "optimistic",
        ],
        "colors": [
            ["sunbleached yellow", "ocean blue"],
            ["sand", "salt white"],
            ["popsicle orange", "stick brown"],
            ["tan", "paperback yellow"],
            ["jar glass", "firefly gold"],
            ["weathered wood", "sunset pink"],
            ["static grey", "laughter pink"],
            ["dune beige", "plastic red"],
            ["ice cream white", "truck chrome"],
            ["clear sky", "signal green"],
        ],
        "compositions": [
            "wide beach", "intimate", "macro", "environmental",
            "close-up", "establishing", "fragmented", "nostalgic",
            "symbolic", "horizon",
        ],
        "cameras": [
            "slow drift", "static lazy", "macro focus", "wide still",
            "handheld memory", "crane over", "glitch cut", "dune slide",
            "tracking truck", "fade to clear",
        ],
        "descriptions": [
            "Beach at noon. A transistor radio hisses on a towel. A foot taps in the sand. Overexposed sky, bleached colors.",
            "Interior of a beach shack. Sand covers the floor like carpet. Two people lie on a bed, staring at a cracked ceiling.",
            "Close-up of popsicle sticks arranged in a tiny cairn on a picnic table. Melted juice stains the wood.",
            "Overgrown weeds swallowing a discarded paperback. A tan line on an arm reaches for it, then withdraws.",
            "Hands holding a mason jar of fireflies. The lid unscrews. Golden insects spiral upward into darkening air.",
            "Boardwalk at dusk. Empty rides creak in wind. One person walks the planks, dragging fingers along railings.",
            "Phone screen showing a call in progress. Static waveform dances. A laugh crackles through, half-caught.",
            "Dune at sunset. Two figures kneel, burying a shoebox. A plastic spoon marks the spot like a flag.",
            "Ice cream truck parked in empty lot. Speaker plays distorted music. One person leans against it, eyes closed.",
            "Horizon line where sea meets sky. Static dissolves into clear blue. Two figures walk toward the water, hand in hand.",
        ],
    },
    {
        "title": "Midnight Algorithm",
        "artist": "Digital Native",
        "bpm": 132,
        "lyrics": [
            "Scrolling through your life at 3 AM / Liking moments I'll never touch again",
            "The algorithm knows me better than you do / Serves me memories I never asked to view",
            "Blue light bathing my insomnia / A personalized coma, custom anesthesia",
            "Swipe right on a stranger who looks like your ghost / The machine learns what I need the most",
            "Notification: 'We miss you' / From an app I deleted in 2002",
            "My data portrait hangs in a server farm / Every click a brushstroke, every like an arm",
            "Recommended for you: a broken heart / 4.7 stars, 12K reviews, add to cart",
            "The infinite scroll of almost-love / Pixelated doves, none of them enough",
            "Cache cleared, history deleted / But the targeted ads say I'm not defeated",
            "Log out, shut down, the screen goes black / The algorithm waits for me to come back",
        ],
        "moods": [
            "isolated", "cynical", "numb", "obsessive", "sardonic",
            "existential", "consumerist", "defeated", "resigned", "cyclical",
        ],
        "colors": [
            ["screen blue", "black"],
            ["notification red", "dark grey"],
            ["neon white", "midnight"],
            ["pixel green", "void"],
            ["amber alert", "ash"],
            ["server blinking", "concrete"],
            ["shopping cart orange", "pale face"],
            ["scroll grey", "electric pink"],
            ["ad yellow", "blank"],
            ["power-off black", "standby red"],
        ],
        "compositions": [
            "screen reflection", "extreme close-up", "data visualization",
            "overhead server", "selfie mirror", "macro pixel", "wide bedroom",
            "split-screen", "voyeuristic", "void",
        ],
        "cameras": [
            "static glow", "zoom into iris", "data stream", "drone through racks",
            "mirror flip", "pixel push", "bedroom pan", "screen recording",
            "ad transition", "fade to black",
        ],
        "descriptions": [
            "Face illuminated by phone screen in total darkness. Scroll thumb moves endlessly. Reflection in the glass shows tired eyes.",
            "Phone interface close-up: 'We miss you' notification from a vintage app icon. Finger hovers, then dismisses.",
            "Server room corridor. Blinking LED lights reflect in the polished floor. A lone figure walks between the racks.",
            "Online shopping interface: 'Broken Heart — 4.7 stars — Add to Cart'. Cursor clicks. Credit card autofill flashes.",
            "Infinite scroll animation: faces blur into faces, all slightly similar. Thumb swipes upward in perpetual motion.",
            "Selfie camera view: face half-covered by phone. The background is a bedroom that hasn't been cleaned in weeks.",
            "Drone shot flying through server racks. Cables like vines. Blinking lights like bioluminescence in a digital forest.",
            "Split screen: left side shows a person crying, right side shows targeted ads for vacation packages and therapy apps.",
            "Macro shot of a single pixel turning from grey to yellow. The transition takes ten seconds. Sound of a cash register.",
            "Phone screen powers down. Face disappears into darkness. Only the standby LED remains, pulsing like a heartbeat.",
        ],
    },
    {
        "title": "Concrete Lullaby",
        "artist": "Urban Dreamer",
        "bpm": 85,
        "lyrics": [
            "The subway hums my lullaby / Steel wheels singing me goodbye",
            "Neon moon through fire escape / A concrete cradle, a steel-scape",
            "Pigeon coos on the window ledge / Nature's refugee on the city's edge",
            "The radiator hisses like the sea / A white noise symphony for you and me",
            "Garbage trucks at 5 AM / The city's way of saying amen",
            "Your breath syncs with the elevator chain / Rising and falling, again and again",
            "Sirens wail a distant song / Telling someone else they're wrong",
            "Rain on AC units, tin drum choir / Nature and machine in a tangled wire",
            "The key turns, the lock says click / Home is anywhere the noise is thick",
            "Sleep now, the city never does / But in your arms, the noise was ours",
        ],
        "moods": [
            "tender", "urban", "peaceful", "bittersweet", "industrial",
            "intimate", "observational", "melancholic", "grounded", "loving",
        ],
        "colors": [
            ["subway silver", "tunnel black"],
            ["neon orange", "rust red"],
            ["pigeon grey", "brick brown"],
            ["radiator white", "pipe green"],
            ["garbage green", "dawn grey"],
            ["elevator bronze", "skin tone"],
            ["siren red", "distant blue"],
            ["rain grey", "aluminum silver"],
            ["brass gold", "door brown"],
            ["streetlight amber", "embrace warmth"],
        ],
        "compositions": [
            "intimate wide", "framed", "close-up", "environmental",
            "low angle", "overhead", "distant", "interior",
            "macro", "two-shot",
        ],
        "cameras": [
            "subtle sway", "static observe", "gentle push", "locked off",
            "low drift", "high angle down", "slow zoom", "crane through",
            "macro focus", "static hold",
        ],
        "descriptions": [
            "Subway car interior at night. A sleeping head rests against a stranger's shoulder. Fluorescent lights flicker overhead.",
            "Fire escape view. Neon sign glows through wrought iron. A figure sits on the metal grating, legs dangling into air.",
            "Close-up of a pigeon nesting on a windowsill. Behind it, the city skyline stretches into smog.",
            "Radiator hissing steam in a tiny apartment. Two figures lie on a mattress on the floor, sharing one pillow.",
            "Street level at dawn. Garbage truck compressors grind. A figure in pajamas watches from a doorway, coffee in hand.",
            "Elevator shaft view from below. Two faces look down through the open grate. Chains and pulleys move in darkness above.",
            "Distant sirens. A figure stands at a window, hand on glass. Red and blue lights paint the wall in slow rotation.",
            "Rain falling on window AC units. Water streams down aluminum fins. One unit rattles like a snare drum.",
            "Key entering a lock. The deadbolt turns. The door opens to a dark hallway. A figure steps inside, shoulders dropping.",
            "Two figures embracing in bed. Streetlight filters through blinds, striping the sheets. City sounds rumble below.",
        ],
    },
    {
        "title": "Paper Kites",
        "artist": "Wind & Wire",
        "bpm": 110,
        "lyrics": [
            "We built a kite from unpaid bills / Flew it from the window sill",
            "The string was twine from a flour sack / We laughed till our lungs nearly cracked",
            "It caught a thermal near the bank / Danced above the tanks and tanks",
            "Your mother called, you let it ring / We had a kite, we had a string",
            "The tail was ribbon from a dress / You wore the day you said 'yes'",
            "It soared above the power lines / A paper bird between the signs",
            "The wind shifted, the kite went down / In the parking lot of the edge of town",
            "We ran to it like it was alive / Picked it up at 4:55",
            "Creased and torn but still intact / Like us, like that, like fact",
            "Let's fly it again when the weather's right / Paper kites and paper light",
        ],
        "moods": [
            "playful", "defiant", "euphoric", "rebellious", "nostalgic",
            "whimsical", "anxious", "tender", "resilient", "hopeful",
        ],
        "colors": [
            ["bill white", "ink blue"],
            ["flour sack beige", "twine brown"],
            ["bank grey", "kite red"],
            ["phone black", "sky blue"],
            ["dress ribbon pink", "memory white"],
            ["power line black", "cloud white"],
            ["asphalt grey", "kite crumpled"],
            ["parking lot yellow", "running shoe"],
            ["crease brown", "tape silver"],
            ["dawn pink", "paper thin"],
        ],
        "compositions": [
            "wide", "close-up", "medium", "high angle",
            "intimate", "dynamic", "low angle", "ground level",
            "two-shot", "horizon",
        ],
        "cameras": [
            "handheld run", "crane up", "tracking", "static joy",
            "zoom out", "dolly back", "freefall", "sprint follow",
            "macro texture", "sunrise hold",
        ],
        "descriptions": [
            "Hands folding a utility bill into a kite shape on a kitchen table. Twine and tape nearby. Laughter in the background.",
            "Kite launched from a fourth-story window. Two figures lean out, arms extended. The kite catches wind and soars.",
            "Kite dancing above a bank building. Surveillance cameras watch from corners. The kite dips and weaves like a dare.",
            "Phone buzzing on a windowsill, ignored. Two figures on the roof, flying the kite higher, competing with the clouds.",
            "Close-up of a kite tail: a faded pink ribbon flutters. Flashback: the same ribbon in hair, a 'yes' whispered.",
            "Kite soaring between power lines. Tense moment. The kite clears the last wire by inches. Exhale.",
            "Kite crashes in an empty parking lot. Two figures run toward it, shoes slapping asphalt. The kite lies crumpled.",
            "Hands smoothing creases from the kite. Tape applied to a tear. Fingers overlap. The kite is whole again.",
            "Two figures sit on the curb, kite between them. The sun rises. They hold the string like a promise.",
            "Wide shot: two figures launch the kite again at dawn. Paper catches golden light. The city wakes behind them.",
        ],
    },
    {
        "title": "Frequency",
        "artist": "Signal Lost",
        "bpm": 140,
        "lyrics": [
            "Tuning in to the frequency of your doubt / Static between what we said and what came out",
            "You broadcast love on a dying band / I receive it but I don't understand",
            "The oscillator swings between hot and cold / A sine wave story, a thousandfold",
            "Feedback loop of your silence / Amplified by my compliance",
            "Equalize the highs, compress the lows / The mix is perfect but nobody knows",
            "Phase cancellation when we kiss / Your lips are there but something's amiss",
            "Modulate my heart to match your key / But the harmonics don't agree",
            "White noise bed where we used to sleep / Every frequency too shallow, too deep",
            "I'm scanning every station, every dial / Searching for the ghost of your smile",
            "Tune out, drop off, the signal's gone / Static is the only truth from now on",
        ],
        "moods": [
            "frustrated", "technical", "anxious", "obsessive", "resigned",
            "sardonic", "confused", "numb", "desperate", "defeated",
        ],
        "colors": [
            ["oscilloscope green", "black"],
            ["radio dial amber", "dark grey"],
            ["sine wave blue", "white"],
            ["feedback red", "silence black"],
            ["compressor grey", "neon"],
            ["out-of-phase purple", "lip pink"],
            ["modulation rainbow", "key white"],
            ["white noise static", "sleep blue"],
            ["scan line white", "dial glow"],
            ["static snow", "void"],
        ],
        "compositions": [
            "macro circuit", "close-up", "abstract", "intimate",
            "wide studio", "extreme close-up", "visualization", "bedroom",
            "scanning", "void",
        ],
        "cameras": [
            "macro drift", "rack focus", "waveform scroll", "static shock",
            "dolly through", "kiss blur", "frequency sweep", "bedroom pan",
            "scan flicker", "fade to snow",
        ],
        "descriptions": [
            "Oscilloscope screen in darkness. Green waveform dances erratically. Two hands adjust knobs, never finding the right frequency.",
            "Vintage radio dial glowing amber. A finger turns the knob slowly, searching. Static hisses between stations.",
            "Abstract visualization: a perfect sine wave splits into two, diverging. Colors shift from blue to cold white.",
            "Recording studio. Red feedback light blinks. A microphone hangs abandoned. Cables coil on the floor like snakes.",
            "Mixing board close-up. Faders move automatically. Compressor needles bounce. The sound is perfect; the room is empty.",
            "Two faces almost touching. A visual ripple effect distorts the kiss. Lips meet but the image cancels itself out.",
            "Kaleidoscope of sound waves. Colors clash and merge. A heart shape attempts to form but breaks into discordant shards.",
            "Bedroom at night. TV static illuminates two sleeping figures. The snow on screen mirrors the chaos in the room.",
            "Rapid channel scan: faces, ads, news, cartoons flicker. A single smile lingers for one frame, then disappears.",
            "Television snow fills the screen. Pull back to reveal the TV in an empty room. The static is the only light source.",
        ],
    },
    {
        "title": "Undertow",
        "artist": "Deep Current",
        "bpm": 95,
        "lyrics": [
            "The tide went out but I stayed in / Breathing water, letting the light dim",
            "Your name is written in the sand / Each wave erases where I stand",
            "Seaweed tangles round my ankles now / A gentle weight that won't allow",
            "The surface shimmers like a lie / Too bright, too far, too high",
            "I opened my mouth to call your name / Filled it with salt instead of shame",
            "The fish don't judge, the crabs don't care / Down here it's quiet, down here it's fair",
            "My hair floats up like golden weed / I planted promises I didn't need",
            "The undertow has my hand in its grip / Pulling me home on a one-way trip",
            "Shells in my pockets, pearls in my eyes / The ocean teaches, the ocean lies",
            "I'll wash up somewhere, smooth and white / A stone that once was holding tight",
        ],
        "moods": [
            "surrender", "melancholic", "peaceful", "desperate", "accepting",
            "isolated", "resigned", "fatalistic", "transformative", "bittersweet",
        ],
        "colors": [
            ["sea green", "sand beige"],
            ["foam white", "wet brown"],
            ["kelp brown", "pale skin"],
            ["surface silver", "depth black"],
            ["salt crystal", "blood warm"],
            ["fish silver", "coral pink"],
            ["blonde float", "deep blue"],
            ["undertow navy", "hand pale"],
            ["pearl white", "shell pink"],
            ["stone grey", "wave white"],
        ],
        "compositions": [
            "underwater wide", "surface", "below", "looking up",
            "close-up", "environmental", "surreal", "intimate",
            "macro", "shoreline",
        ],
        "cameras": [
            "submerge slow", "surface float", "drift down", "look up",
            "bubble rise", "underwater pan", "surreal tilt", "sink",
            "macro shell", "wash ashore",
        ],
        "descriptions": [
            "Underwater wide shot. A figure stands on the ocean floor, hair floating upward. Sunlight filters down in visible beams.",
            "Surface view from below. Feet tread water above. A name written in sand on the shore, each wave licking it away.",
            "Close-up of seaweed wrapping around an ankle. Gentle, vegetative embrace. Small fish dart through the fronds.",
            "Looking up at the water surface from deep below. The light shimmers and distorts like a broken mirror.",
            "Underwater close-up: bubbles escape from lips. Eyes closed. Salt crystals form on eyelashes.",
            "A crab scuttles past. A school of silver fish parts around the figure. The seabed is soft silt.",
            "Hair spreads in a golden halo. Tiny pearls scatter from a broken necklace, drifting down like slow-motion hail.",
            "Hand reaching toward the surface. Fingers spread. The undertow pulls the other direction, gentle but insistent.",
            "Eyes opening underwater. Pearls reflected in the irises. A seashell rests on the cheek like a tear.",
            "Shoreline at dawn. A smooth stone washes up, wet and gleaming. Footsteps approach, then stop.",
        ],
    },
]


def generate():
    rows = []
    for song in SONGS:
        for i in range(10):
            rows.append(
                {
                    "song": song["title"],
                    "artist": song["artist"],
                    "genre": GENRE,
                    "bpm": song["bpm"],
                    "beat": i + 1,
                    "timestamp": f"{i * 30 // 60}:{(i * 30) % 60:02d}",
                    "duration_seconds": DURATION_SECONDS,
                    "lyric_line": song["lyrics"][i],
                    "scene": {
                        "mood": song["moods"][i],
                        "colors": song["colors"][i],
                        "composition": song["compositions"][i],
                        "camera": song["cameras"][i],
                        "description": song["descriptions"][i],
                    },
                }
            )
    return rows


def validate(rows):
    assert len(rows) == 100, f"Expected 100 rows, got {len(rows)}"
    songs = {r["song"] for r in rows}
    assert len(songs) == 10, f"Expected 10 songs, got {len(songs)}"
    for r in rows:
        assert "scene" in r
        assert "mood" in r["scene"]
        assert "colors" in r["scene"]
        assert "composition" in r["scene"]
        assert "camera" in r["scene"]
        assert "description" in r["scene"]
        assert len(r["scene"]["description"]) >= 10
    print(f"Validation passed: {len(rows)} rows, {len(songs)} songs")


def write(rows, path: Path):
    path.parent.mkdir(parents=True, exist_ok=True)
    with open(path, "w", encoding="utf-8") as f:
        for r in rows:
            f.write(json.dumps(r, ensure_ascii=False) + "\n")
    print(f"Wrote {len(rows)} rows to {path}")


def main():
    rows = generate()
    validate(rows)

    repo_path = Path(__file__).resolve().parent.parent / "training-data" / "scene-descriptions-pop.jsonl"
    home_path = Path.home() / ".hermes" / "training-data" / "scene-descriptions-pop.jsonl"

    write(rows, repo_path)
    write(rows, home_path)

    # Run schema validation if available
    schema_script = Path(__file__).resolve().parent.parent / "scripts" / "validate-scene-data.py"
    if schema_script.exists():
        import subprocess

        result = subprocess.run(
            ["python3", str(schema_script), "--schema", "training-data/schema.json", str(repo_path)],
            capture_output=True,
            text=True,
        )
        print(result.stdout)
        if result.returncode != 0:
            print(result.stderr)
            raise SystemExit(1)


if __name__ == "__main__":
    main()
