#!/usr/bin/env python3
import json, re, subprocess, time, urllib.request, unicodedata
from difflib import SequenceMatcher
from pathlib import Path

PROJECT = Path('/data/video-pipeline/Youtube-Video-Maker/Project/01062026-001-Pham-Ngu-Lao')
ENDPOINT = 'http://192.168.1.103:8001/transcribe-and-save'
HEALTH = 'http://192.168.1.103:8001/health'
OUT_DIR = PROJECT / 'logs/modified_segment_asr_audit'
REPORT = PROJECT / 'logs/modified_segment_asr_audit_report.json'
TARGET_SEGMENTS = [
    'scene_012_seg_001', 'scene_020_seg_001', 'scene_022_seg_002', 'scene_026_seg_001',
    'scene_027_seg_001', 'scene_028_seg_001', 'scene_036_seg_001', 'scene_044_seg_002',
    'scene_049_seg_002', 'scene_051_seg_001', 'scene_055_seg_001', 'scene_057_seg_001',
    'scene_081_seg_001', 'scene_128_seg_001', 'scene_049_seg_001', 'scene_067_seg_001',
    'scene_094_seg_001', 'scene_155_seg_001',
]


def post_json(url, payload=None, timeout=7200):
    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 else {}, method='POST' if payload else 'GET')
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        return json.loads(resp.read().decode('utf-8'))


def duration(path):
    try:
        out = subprocess.check_output(['ffprobe','-v','error','-show_entries','format=duration','-of','default=nw=1:nk=1',str(path)], text=True, timeout=10)
        return float(out.strip())
    except Exception:
        return None


def norm(s):
    s = unicodedata.normalize('NFD', s.lower())
    s = ''.join(ch for ch in s if unicodedata.category(ch) != 'Mn')
    s = re.sub(r'[^a-z0-9đ\s]', ' ', s)
    s = s.replace('đ','d')
    s = re.sub(r'\s+', ' ', s).strip()
    return s


def tokens(s):
    return norm(s).split()


def similarity(a, b):
    return SequenceMatcher(None, norm(a), norm(b)).ratio()


def collect_text(asr):
    text = asr.get('text') or asr.get('transcript') or ''
    if text:
        return re.sub(r'\s+', ' ', text).strip()
    segs = asr.get('segments') or []
    return re.sub(r'\s+', ' ', ' '.join(seg.get('text','') for seg in segs)).strip()


def main():
    OUT_DIR.mkdir(parents=True, exist_ok=True)
    post_json(HEALTH, None, timeout=30)
    manifest = json.loads((PROJECT/'audio/voice_segments/voice_segments_manifest.json').read_text(encoding='utf-8'))
    lookup = {it['voice_segment_id']: it for it in manifest['items']}
    results=[]
    for vid in TARGET_SEGMENTS:
        item = lookup.get(vid)
        if not item:
            results.append({'voice_segment_id': vid, 'status': 'missing_manifest'})
            continue
        audio = Path(item['audio_path'])
        out_json = OUT_DIR / f'{vid}.json'
        if audio.exists():
            payload={'input_path': str(audio), 'output_path': str(out_json), 'language':'vi', 'task':'transcribe', 'word_timestamps': False}
            print(time.strftime('%F %T'), 'ASR', vid, flush=True)
            try:
                resp = post_json(ENDPOINT, payload, timeout=7200)
                if out_json.exists():
                    asr = json.loads(out_json.read_text(encoding='utf-8'))
                else:
                    asr = resp
                asr_text = collect_text(asr)
                sim = similarity(item['text'], asr_text)
                src_tok = set(tokens(item['text']))
                asr_tok = set(tokens(asr_text))
                recall = len(src_tok & asr_tok) / len(src_tok) if src_tok else 0
                wc_src=len(tokens(item['text'])); wc_asr=len(tokens(asr_text))
                dur=duration(audio)
                status='ok'
                notes=[]
                if sim < 0.58 or recall < 0.55:
                    status='suspect'
                    notes.append('low_text_match')
                if wc_asr < max(2, wc_src * 0.45):
                    status='suspect'
                    notes.append('asr_much_shorter_than_source')
                results.append({'voice_segment_id':vid,'status':status,'audio_path':str(audio),'duration':dur,'source_text':item['text'],'asr_text':asr_text,'similarity':round(sim,3),'token_recall':round(recall,3),'source_words':wc_src,'asr_words':wc_asr,'notes':notes})
            except Exception as e:
                results.append({'voice_segment_id':vid,'status':'asr_failed','audio_path':str(audio),'error':str(e),'duration':duration(audio),'source_text':item['text']})
        else:
            results.append({'voice_segment_id':vid,'status':'missing_audio','audio_path':str(audio),'source_text':item['text']})
    REPORT.write_text(json.dumps({'status':'completed','targets':TARGET_SEGMENTS,'results':results}, ensure_ascii=False, indent=2), encoding='utf-8')
    print(json.dumps({'status':'completed','report':str(REPORT),'suspect':[r['voice_segment_id'] for r in results if r.get('status')!='ok']}, ensure_ascii=False, indent=2))

if __name__ == '__main__':
    main()
