#!/usr/bin/env python3
import json
import os
import sqlite3
import subprocess
import time
from pathlib import Path

DB = Path('/root/workspace/shorts_data/shorts.db')
MEDIA = Path('/root/workspace/shorts_data/media')
YTDLP = '/root/.local/bin/yt-dlp'
COOKIES = Path('/root/workspace/shorts_data/youtube-cookies.txt')


def setting(db, key, default=''):
    row = db.execute('SELECT value FROM settings WHERE key=?', (key,)).fetchone()
    return row[0] if row else default


def command(*args):
    return subprocess.run(args, text=True, capture_output=True, timeout=900)


def ytdlp_args(*args):
    auth = ('--cookies', str(COOKIES)) if COOKIES.is_file() else ()
    return (YTDLP, *auth, '--js-runtimes', 'node', '--remote-components', 'ejs:github', *args)


def probe_source(url):
    result = command(*ytdlp_args('--flat-playlist', '--dump-single-json', '--playlist-end', '5', url))
    if result.returncode != 0:
        raise RuntimeError(result.stderr[-500:] or 'yt-dlp could not read the source')
    data = json.loads(result.stdout)
    entries = data.get('entries') or [data]
    return [e for e in entries if e and e.get('id')]


def insert_source_candidates(db, source_url, entries):
    created = 0
    for entry in entries:
        video_id = entry['id']
        url = entry.get('webpage_url') or f'https://www.youtube.com/watch?v={video_id}'
        title = entry.get('title') or video_id
        exists = db.execute('SELECT 1 FROM candidates WHERE source_url=?', (url,)).fetchone()
        if exists:
            continue
        db.execute('INSERT INTO candidates(source_url,title,status,ratings_json,warnings_json,created_at) VALUES (?,?,?,?,?,?)', (url, title, 'discovered', '[]', '[]', int(time.time())))
        created += 1
    return created


def process_candidate(db, row):
    cid, url = row
    MEDIA.mkdir(parents=True, exist_ok=True)
    work = MEDIA / str(cid)
    work.mkdir(exist_ok=True)
    source = work / 'source.mp4'
    if not source.exists():
        result = command(*ytdlp_args('--no-playlist', '-f', 'bv*[height<=1080]+ba/b[height<=1080]', '--merge-output-format', 'mp4', '-o', str(source), url))
        if result.returncode != 0:
            db.execute('UPDATE candidates SET status=?, warnings_json=? WHERE id=?', ('error', json.dumps([result.stderr[-500:] or 'download failed']), cid)); return
    clip = work / 'clip.mp4'
    if not clip.exists():
        # A deterministic first candidate is intentionally conservative; the agent scorer can later replace this with scored timestamps.
        result = command('ffmpeg', '-y', '-ss', '0', '-i', str(source), '-t', '60', '-vf', "scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920", '-c:v', 'libx264', '-preset', 'veryfast', '-crf', '23', '-c:a', 'aac', str(clip))
        if result.returncode != 0:
            db.execute('UPDATE candidates SET status=?, warnings_json=? WHERE id=?', ('error', json.dumps([result.stderr[-500:] or 'render failed']), cid)); return
    db.execute('UPDATE candidates SET clip_path=?, status=? WHERE id=?', (str(clip), 'awaiting_agent_scores', cid))


def run_once():
    with sqlite3.connect(DB) as db:
        started = int(time.time())
        db.execute('INSERT OR REPLACE INTO settings(key,value) VALUES (?,?)', ('worker_status','scanning'))
        db.execute('INSERT OR REPLACE INTO settings(key,value) VALUES (?,?)', ('scan_started',str(started)))
        db.commit()
        source = setting(db, 'source_url')
        if not source:
            result = {'status': 'idle', 'reason': 'source_url_not_configured', 'created': 0}
            db.execute('INSERT OR REPLACE INTO settings(key,value) VALUES (?,?)', ('worker_status','idle'))
            db.execute('INSERT OR REPLACE INTO settings(key,value) VALUES (?,?)', ('last_scan',str(int(time.time()))))
            db.commit()
            return result
        try:
            entries = probe_source(source)
            created = insert_source_candidates(db, source, entries)
            for row in db.execute("SELECT id,source_url FROM candidates WHERE status='discovered' LIMIT 1").fetchall():
                process_candidate(db, row)
            result = {'status': 'ok', 'entries': len(entries), 'created': created}
            db.execute('INSERT OR REPLACE INTO settings(key,value) VALUES (?,?)', ('worker_status','ok'))
            db.execute('INSERT OR REPLACE INTO settings(key,value) VALUES (?,?)', ('last_scan',str(int(time.time()))))
            db.execute('INSERT OR REPLACE INTO settings(key,value) VALUES (?,?)', ('last_error',''))
            db.execute('INSERT OR REPLACE INTO settings(key,value) VALUES (?,?)', ('last_result',json.dumps(result)))
            db.commit()
            return result
        except Exception as exc:
            message = str(exc)
            result = {'status': 'error', 'error': message}
            db.execute('INSERT OR REPLACE INTO settings(key,value) VALUES (?,?)', ('worker_status','error'))
            db.execute('INSERT OR REPLACE INTO settings(key,value) VALUES (?,?)', ('last_scan',str(int(time.time()))))
            db.execute('INSERT OR REPLACE INTO settings(key,value) VALUES (?,?)', ('last_error', message))
            db.execute('INSERT OR REPLACE INTO settings(key,value) VALUES (?,?)', ('last_result',json.dumps(result)))
            db.commit()
            return result


if __name__ == '__main__':
    print('shorts worker started', flush=True)
    while True:
        print(run_once(), flush=True)
        time.sleep(10)
