#!/usr/bin/env python3
import json
import time
import urllib.request
from pathlib import Path

PROJECT = Path('/data/video-pipeline/Youtube-Video-Maker/Project/08062026-003-Le-Hoan')
ENDPOINT = 'http://192.168.1.103:8001/transcribe-and-save'
HEALTH_ENDPOINT = 'http://192.168.1.103:8001/health'
MASTER_AUDIO = PROJECT / 'audio/master_narration.wav'
PROBE_JSON = PROJECT / 'audio/master_audio_probe.json'
ASR_OUTPUT = PROJECT / 'subtitles/master_narration_asr_words.json'
MANIFEST = PROJECT / 'logs/step10_asr_master_manifest.json'


def log(msg):
    print(time.strftime('%Y-%m-%d %H:%M:%S ') + msg, flush=True)


def post_json(url, payload=None, timeout=3600):
    data = None if payload is None else json.dumps(payload, ensure_ascii=False).encode('utf-8')
    req = urllib.request.Request(
        url,
        data=data,
        headers={'Content-Type': 'application/json'} if payload is not None else {},
        method='POST' if payload is not None else 'GET',
    )
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        return json.loads(resp.read().decode('utf-8'))


def collect_words(doc):
    words = []
    for seg in doc.get('segments', []) or []:
        for word in seg.get('words', []) or []:
            if word.get('start') is not None and word.get('end') is not None:
                words.append(word)
    if not words and isinstance(doc.get('words'), list):
        words = [w for w in doc['words'] if w.get('start') is not None and w.get('end') is not None]
    return words


def main():
    if not MASTER_AUDIO.exists():
        raise SystemExit(f'Missing master audio: {MASTER_AUDIO}')
    if not PROBE_JSON.exists():
        raise SystemExit(f'Missing Step 9 probe json: {PROBE_JSON}')

    probe = json.loads(PROBE_JSON.read_text(encoding='utf-8'))
    ASR_OUTPUT.parent.mkdir(parents=True, exist_ok=True)
    MANIFEST.parent.mkdir(parents=True, exist_ok=True)

    health = post_json(HEALTH_ENDPOINT, None, timeout=30)
    payload = {
        'input_path': str(MASTER_AUDIO),
        'output_path': str(ASR_OUTPUT),
        'language': 'vi',
        'task': 'transcribe',
        'word_timestamps': True,
    }

    log(f'POST {ENDPOINT} input={MASTER_AUDIO} output={ASR_OUTPUT} word_timestamps=true')
    response = post_json(ENDPOINT, payload, timeout=7200)

    if not ASR_OUTPUT.exists():
        # Some services return the full JSON without saving; preserve it at the required path.
        ASR_OUTPUT.write_text(json.dumps(response, ensure_ascii=False, indent=2), encoding='utf-8')

    asr_doc = json.loads(ASR_OUTPUT.read_text(encoding='utf-8'))
    words = collect_words(asr_doc)
    segments = asr_doc.get('segments', []) or []

    validation_errors = []
    if not segments:
        validation_errors.append('ASR output has no segments')
    if not words:
        validation_errors.append('ASR output has no word timestamps')
    prev = -1.0
    for i, word in enumerate(words):
        start = float(word.get('start'))
        end = float(word.get('end'))
        if start < prev:
            validation_errors.append(f'word timestamps are not monotonic at index {i}')
            break
        if end < start:
            validation_errors.append(f'word end before start at index {i}')
            break
        prev = start

    status = 'completed' if not validation_errors else 'failed_validation'
    transcript = asr_doc.get('text') or asr_doc.get('transcript') or response.get('text') or response.get('transcript') or ''
    manifest = {
        'project_id': PROJECT.name,
        'step': 10,
        'status': status,
        'endpoint': ENDPOINT,
        'health': health,
        'input_audio_path': str(MASTER_AUDIO),
        'probe_duration_sec': probe.get('master_duration_sec'),
        'output_asr_json': str(ASR_OUTPUT),
        'word_timestamps_requested': True,
        'language': 'vi',
        'segments_count': len(segments),
        'words_count': len(words),
        'transcript_chars': len(transcript),
        'validation_errors': validation_errors,
        'note': 'ASR transcript is for timing only; subtitle display text must come from original scene.narration/source text.',
    }
    MANIFEST.write_text(json.dumps(manifest, ensure_ascii=False, indent=2), encoding='utf-8')
    if validation_errors:
        raise SystemExit('ASR validation failed: ' + '; '.join(validation_errors))
    log('DONE ' + json.dumps({'status': status, 'segments': len(segments), 'words': len(words)}, ensure_ascii=False))


if __name__ == '__main__':
    main()
