"""
Batch-run the Canine demo pipeline on the selected eval clips and record,
for each clip, the four fields raters will score:
    environment / activity / intention / interpretation

These are the demo's genuine outputs: environment/activity/intention are the
top dominant themes in each category (from categorized_word_meaning.json, the
same knowledge base the results page uses), and interpretation is the demo's
overall_meaning sentence.

Results are written incrementally to human_eval/_results.json so progress can
be monitored. Stops once TARGET clips with usable semantics are collected.
"""
import sys, os, time, json, shutil, subprocess

os.chdir('/home/theron/projects/web_apps/canine')
sys.path.insert(0, '.')
import app, config  # noqa

HERE = os.path.join(config.BASE_DIR, 'human_eval')
CAND = json.load(open(os.path.join(HERE, '_candidates.json')))
RESULTS_PATH = os.path.join(HERE, '_results.json')
TARGET = 30

# Build category -> set(keywords) so we can classify each dominant theme.
WM = json.load(open(os.path.join(config.MODEL_DIR, 'categorized_word_meaning.json')))
CAT = {'environment': set(), 'activity': set(), 'intension': set()}
for _w, _m in WM.items():
    for c in CAT:
        CAT[c].update(_m.get(c, {}).keys())

def classify(theme):
    for c in ('environment', 'activity', 'intension'):
        if theme in CAT[c]:
            return c
    return None

def pick_fields(sem):
    """From a demo semantics dict, pull one environment/activity/intention theme
    plus the interpretation sentence."""
    overall = sem.get('overall', {}) if sem else {}
    themes = overall.get('dominant_themes', []) or []
    fields = {'environment': None, 'activity': None, 'intension': None}
    for t in themes:
        c = classify(t)
        if c and fields[c] is None:
            fields[c] = t
    # Fallback: scan per-sentence keywords for any missing category.
    if None in fields.values():
        for s in (sem.get('sentences') or []):
            for kw in s.get('keywords', []):
                c = classify(kw)
                if c and fields[c] is None:
                    fields[c] = kw
    detected = []
    for s in (sem.get('sentences') or []):
        detected += s.get('detected_words', [])
    return {
        'environment': fields['environment'] or 'N/A',
        'activity': fields['activity'] or 'N/A',
        'intention': fields['intension'] or 'N/A',
        'interpretation': overall.get('overall_meaning', '') or '',
        'dominant_themes': themes,
        'detected_words': sorted(set(detected), key=lambda x: (len(x), x)),
    }

def dur(p):
    o = subprocess.run(['ffprobe', '-v', 'error', '-show_entries',
                        'format=duration', '-of', 'default=nk=1:nw=1', p],
                       capture_output=True, text=True)
    try:
        return float(o.stdout.strip())
    except ValueError:
        return None

results = []
if os.path.exists(RESULTS_PATH):
    results = json.load(open(RESULTS_PATH))
done_keys = {r['key'] for r in results if r.get('ok')}

kept = len([r for r in results if r.get('ok')])
for cand in CAND:
    if kept >= TARGET:
        break
    key = cand['key']
    if key in done_keys:
        continue
    clip = cand['clip_path']
    d = dur(clip)
    if not d:
        continue
    fid = 'eval_' + key.replace('+', '').replace(' ', '')[:40]
    raw = os.path.join(config.UPLOAD_FOLDER_RAW, fid + '_clip.mp4')
    try:
        shutil.copy(clip, raw)
    except Exception as e:
        print('copy fail', key, e, flush=True)
        continue
    t0 = time.time()
    print(f'[{kept+1}/{TARGET}] processing {key} ({cand["breed"]}, {d:.1f}s)...', flush=True)
    try:
        res = app.process_audio(fid, raw, 0.0, d)
    except Exception as e:
        print('  ERROR', e, flush=True)
        results.append({'key': key, 'ok': False, 'error': str(e), **cand})
        json.dump(results, open(RESULTS_PATH, 'w'), indent=1)
        continue
    sem = res.get('semantics')
    ok = bool(sem and sem.get('sentences'))
    rec = {'key': key, 'breed': cand['breed'], 'video_id': cand['video_id'],
           'duration': round(d, 2), 'clip_path': clip,
           'n_sentences': len(res.get('sed') or []),
           'n_phones': len(res.get('transcription') or []),
           'elapsed': round(time.time() - t0, 1), 'ok': ok}
    if ok:
        rec.update(pick_fields(sem))
        kept += 1
        print(f'  OK {rec["elapsed"]}s  env={rec["environment"]} | '
              f'act={rec["activity"]} | int={rec["intention"]}', flush=True)
    else:
        rec['error'] = res.get('error') or 'no semantics (no sentences detected)'
        print(f'  skip: {rec["error"]}', flush=True)
    results.append(rec)
    json.dump(results, open(RESULTS_PATH, 'w'), indent=1)

print(f'DONE. kept={kept} usable of {len(results)} attempted.', flush=True)
