"""
Validation audit UI: browse video segments, play audio, inspect
transcription + validation scores side-by-side.

Usage:
    python scripts/validation_audit_ui.py [--port 5111]

Opens at http://localhost:5111
"""
from __future__ import annotations

import json
import math
import mimetypes
import os
import sys
from pathlib import Path

from flask import Flask, Response, jsonify, request, send_file

ROOT = Path(__file__).resolve().parent.parent
AUDIT_DIR = ROOT / "data" / "validation_audit" / "audit_output"
AUDIT_JSON = AUDIT_DIR / "audit_data.json"

app = Flask(__name__, static_folder=None)


def _load_audit_data() -> dict:
    with open(AUDIT_JSON) as f:
        return json.load(f)


BUCKET_COLORS = {
    "golden": "#22c55e",
    "redo": "#eab308",
    "dispose": "#ef4444",
    "unknown": "#6b7280",
}

LANG_LABELS = {
    "te": "Telugu", "hi": "Hindi", "en": "English",
    "ta": "Tamil", "kn": "Kannada", "ml": "Malayalam",
    "mr": "Marathi", "gu": "Gujarati", "bn": "Bengali",
    "pa": "Punjabi", "or": "Odia", "as": "Assamese",
}


@app.route("/")
def index():
    return _render_html()


@app.route("/api/data")
def api_data():
    return jsonify(_load_audit_data())


@app.route("/audio/<video_id>/<path:segment_id>")
def serve_audio(video_id: str, segment_id: str):
    # segment_id already ends with .flac
    fname = segment_id if segment_id.endswith(".flac") else f"{segment_id}.flac"
    path = AUDIT_DIR / video_id / "segments" / fname
    if not path.exists():
        return Response("Not found", status=404)
    return send_file(str(path), mimetype="audio/flac")


def _render_html() -> str:
    data = _load_audit_data()
    return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Validation Audit UI</title>
<style>
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
body {{ font-family: 'Inter', -apple-system, system-ui, sans-serif; background: #0f172a; color: #e2e8f0; }}
.header {{ background: #1e293b; border-bottom: 1px solid #334155; padding: 16px 24px; position: sticky; top: 0; z-index: 100; }}
.header h1 {{ font-size: 20px; font-weight: 600; color: #f8fafc; }}
.header p {{ font-size: 13px; color: #94a3b8; margin-top: 4px; }}
.layout {{ display: flex; height: calc(100vh - 80px); }}
.sidebar {{ width: 280px; min-width: 280px; background: #1e293b; border-right: 1px solid #334155; overflow-y: auto; }}
.main {{ flex: 1; overflow-y: auto; padding: 20px; }}
.video-tab {{ padding: 12px 16px; cursor: pointer; border-bottom: 1px solid #334155; transition: background .15s; }}
.video-tab:hover {{ background: #334155; }}
.video-tab.active {{ background: #1e40af; border-left: 3px solid #3b82f6; }}
.video-tab .vid-id {{ font-size: 14px; font-weight: 600; color: #f1f5f9; font-family: monospace; }}
.video-tab .vid-meta {{ font-size: 12px; color: #94a3b8; margin-top: 4px; }}
.badge {{ display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 600; color: #fff; margin-right: 4px; }}
.badge-golden {{ background: {BUCKET_COLORS["golden"]}; }}
.badge-redo {{ background: {BUCKET_COLORS["redo"]}; }}
.badge-dispose {{ background: {BUCKET_COLORS["dispose"]}; }}
.filters {{ padding: 12px 16px; border-bottom: 1px solid #334155; background: #1e293b; }}
.filters label {{ font-size: 12px; color: #94a3b8; margin-right: 8px; cursor: pointer; }}
.filters input[type="checkbox"] {{ margin-right: 3px; accent-color: #3b82f6; }}
.segment-card {{ background: #1e293b; border: 1px solid #334155; border-radius: 8px; margin-bottom: 12px; overflow: hidden; transition: border-color .15s; }}
.segment-card:hover {{ border-color: #475569; }}
.segment-card.golden {{ border-left: 4px solid {BUCKET_COLORS["golden"]}; }}
.segment-card.redo {{ border-left: 4px solid {BUCKET_COLORS["redo"]}; }}
.segment-card.dispose {{ border-left: 4px solid {BUCKET_COLORS["dispose"]}; }}
.seg-header {{ display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; background: #0f172a; }}
.seg-header .seg-id {{ font-family: monospace; font-size: 13px; color: #cbd5e1; }}
.seg-body {{ display: grid; grid-template-columns: 1fr 1fr; gap: 0; }}
.seg-left, .seg-right {{ padding: 14px; }}
.seg-left {{ border-right: 1px solid #334155; }}
.seg-label {{ font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: #64748b; margin-bottom: 4px; font-weight: 600; }}
.seg-value {{ font-size: 13px; color: #e2e8f0; margin-bottom: 10px; word-break: break-word; }}
.seg-value.transcription {{ font-size: 15px; line-height: 1.6; min-height: 40px; }}
.score-grid {{ display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }}
.score-item {{ background: #0f172a; border-radius: 6px; padding: 8px 10px; }}
.score-item .score-label {{ font-size: 10px; text-transform: uppercase; color: #64748b; }}
.score-item .score-val {{ font-size: 16px; font-weight: 700; margin-top: 2px; }}
.score-val.good {{ color: {BUCKET_COLORS["golden"]}; }}
.score-val.warn {{ color: {BUCKET_COLORS["redo"]}; }}
.score-val.bad {{ color: {BUCKET_COLORS["dispose"]}; }}
audio {{ width: 100%; height: 36px; margin-top: 6px; }}
.summary-bar {{ display: flex; gap: 16px; padding: 12px 0; margin-bottom: 16px; }}
.summary-stat {{ background: #1e293b; border-radius: 8px; padding: 12px 18px; text-align: center; flex: 1; }}
.summary-stat .num {{ font-size: 24px; font-weight: 700; }}
.summary-stat .lbl {{ font-size: 11px; color: #94a3b8; text-transform: uppercase; margin-top: 4px; }}
.progress-bar {{ display: flex; height: 6px; border-radius: 3px; overflow: hidden; background: #334155; margin-top: 8px; }}
.progress-bar .seg {{ height: 100%; }}
@media (max-width: 900px) {{
    .seg-body {{ grid-template-columns: 1fr; }}
    .seg-left {{ border-right: none; border-bottom: 1px solid #334155; }}
}}
</style>
</head>
<body>

<div class="header">
    <h1>Validation Audit UI</h1>
    <p>Audio segment review &mdash; polished replay + transcription + validation scores</p>
</div>

<div class="layout">
    <div class="sidebar" id="sidebar">
        <div class="filters" id="filters"></div>
        <div id="video-list"></div>
    </div>
    <div class="main" id="main-content"></div>
</div>

<script>
const DATA = {json.dumps(data)};

const BUCKET_COLORS = {json.dumps(BUCKET_COLORS)};
const LANG_LABELS = {json.dumps(LANG_LABELS)};

let activeVideo = null;
let bucketFilters = {{ golden: true, redo: true, dispose: true }};

function init() {{
    renderVideoList();
    renderFilters();
    const firstVid = Object.keys(DATA)[0];
    if (firstVid) selectVideo(firstVid);
}}

function renderFilters() {{
    const el = document.getElementById('filters');
    el.innerHTML = '<div style="font-size:12px;color:#94a3b8;margin-bottom:8px;font-weight:600">Filter Buckets</div>' +
        ['golden','redo','dispose'].map(b => `
            <label>
                <input type="checkbox" ${{bucketFilters[b] ? 'checked' : ''}}
                    onchange="toggleBucket('${{b}}')">
                <span class="badge badge-${{b}}">${{b}}</span>
            </label>
        `).join('');
}}

function toggleBucket(b) {{
    bucketFilters[b] = !bucketFilters[b];
    if (activeVideo) renderSegments(activeVideo);
}}

function renderVideoList() {{
    const el = document.getElementById('video-list');
    el.innerHTML = Object.entries(DATA).map(([vid, d]) => {{
        const buckets = {{}};
        d.segments.forEach(s => {{ buckets[s.bucket] = (buckets[s.bucket]||0) + 1; }});
        return `
        <div class="video-tab" id="tab-${{vid}}" onclick="selectVideo('${{vid}}')">
            <div class="vid-id">${{vid}}</div>
            <div class="vid-meta">
                ${{LANG_LABELS[d.language] || d.language}} &middot; ${{d.total_polished}} segments
            </div>
            <div style="margin-top:6px">
                ${{buckets.golden ? `<span class="badge badge-golden">${{buckets.golden}} golden</span>` : ''}}
                ${{buckets.redo ? `<span class="badge badge-redo">${{buckets.redo}} redo</span>` : ''}}
                ${{buckets.dispose ? `<span class="badge badge-dispose">${{buckets.dispose}} dispose</span>` : ''}}
            </div>
            <div class="progress-bar">
                <div class="seg" style="width:${{(buckets.golden||0)*100/d.total_polished}}%;background:${{BUCKET_COLORS.golden}}"></div>
                <div class="seg" style="width:${{(buckets.redo||0)*100/d.total_polished}}%;background:${{BUCKET_COLORS.redo}}"></div>
                <div class="seg" style="width:${{(buckets.dispose||0)*100/d.total_polished}}%;background:${{BUCKET_COLORS.dispose}}"></div>
            </div>
        </div>`;
    }}).join('');
}}

function selectVideo(vid) {{
    if (activeVideo) {{
        const prev = document.getElementById('tab-' + activeVideo);
        if (prev) prev.classList.remove('active');
    }}
    activeVideo = vid;
    const tab = document.getElementById('tab-' + vid);
    if (tab) tab.classList.add('active');
    renderSegments(vid);
}}

function renderSegments(vid) {{
    const d = DATA[vid];
    const segments = d.segments.filter(s => bucketFilters[s.bucket]);

    const buckets = {{}};
    d.segments.forEach(s => {{ buckets[s.bucket] = (buckets[s.bucket]||0) + 1; }});
    const total = d.total_polished;

    const el = document.getElementById('main-content');
    el.innerHTML = `
        <div class="summary-bar">
            <div class="summary-stat">
                <div class="num" style="color:#f1f5f9">${{total}}</div>
                <div class="lbl">Total Segments</div>
            </div>
            <div class="summary-stat">
                <div class="num" style="color:${{BUCKET_COLORS.golden}}">${{buckets.golden||0}}</div>
                <div class="lbl">Golden</div>
            </div>
            <div class="summary-stat">
                <div class="num" style="color:${{BUCKET_COLORS.redo}}">${{buckets.redo||0}}</div>
                <div class="lbl">Redo</div>
            </div>
            <div class="summary-stat">
                <div class="num" style="color:${{BUCKET_COLORS.dispose}}">${{buckets.dispose||0}}</div>
                <div class="lbl">Dispose</div>
            </div>
        </div>
        <div style="font-size:13px;color:#94a3b8;margin-bottom:16px">
            Showing ${{segments.length}} of ${{total}} segments for
            <strong style="color:#f1f5f9">${{vid}}</strong>
            (${{LANG_LABELS[d.language] || d.language}})
        </div>
        ${{segments.map((s, i) => renderCard(vid, s, i)).join('')}}
    `;
}}

function fmtScore(val, thresholds) {{
    if (val === null || val === undefined || val === '') return `<span style="color:#64748b">—</span>`;
    const n = parseFloat(val);
    if (isNaN(n)) return `<span style="color:#64748b">${{val}}</span>`;
    let cls = 'good';
    if (thresholds) {{
        if (n < thresholds[0]) cls = 'bad';
        else if (n < thresholds[1]) cls = 'warn';
    }}
    return `<span class="score-val ${{cls}}">${{n.toFixed(3)}}</span>`;
}}

function renderCard(vid, s, idx) {{
    const bucket = s.bucket || 'unknown';
    const lid = s.lid_agree_count ?? '—';
    const lidCls = lid >= 3 ? 'good' : lid >= 2 ? 'warn' : 'bad';
    const ctc = s.conformer_multi_ctc_normalized;
    const dur = s.duration_s || (s.duration_ms / 1000);

    return `
    <div class="segment-card ${{bucket}}">
        <div class="seg-header">
            <div>
                <span class="seg-id">#${{idx+1}} ${{s.segment_id}}</span>
            </div>
            <div>
                <span class="badge badge-${{bucket}}">${{bucket}}</span>
                <span style="font-size:12px;color:#94a3b8;margin-left:8px">${{dur ? dur.toFixed(1) + 's' : ''}}</span>
            </div>
        </div>
        <div class="seg-body">
            <div class="seg-left">
                <div class="seg-label">Audio (polished replay)</div>
                <audio controls preload="none" src="/audio/${{vid}}/${{s.segment_id}}"></audio>

                <div class="seg-label" style="margin-top:14px">Transcription</div>
                <div class="seg-value transcription">${{s.transcription || '<em style=color:#64748b>empty</em>'}}</div>

                <div class="seg-label">Tagged</div>
                <div class="seg-value">${{s.tagged || '<em style=color:#64748b>—</em>'}}</div>

                <div class="seg-label">Detected Language</div>
                <div class="seg-value">${{s.detected_language || '—'}} ${{s.detected_language && LANG_LABELS[s.detected_language] ? '(' + LANG_LABELS[s.detected_language] + ')' : ''}}</div>
            </div>
            <div class="seg-right">
                <div class="seg-label">Validation Scores</div>
                <div class="score-grid">
                    <div class="score-item">
                        <div class="score-label">LID Agree</div>
                        <div class="score-val ${{lidCls}}">${{lid}}/3</div>
                    </div>
                    <div class="score-item">
                        <div class="score-label">LID Consensus</div>
                        <div class="score-val ${{s.lid_consensus ? 'good' : 'bad'}}">${{s.lid_consensus ? 'YES' : 'NO'}}</div>
                    </div>
                    <div class="score-item">
                        <div class="score-label">CTC Score</div>
                        ${{fmtScore(ctc, [0.3, 0.7])}}
                    </div>
                    <div class="score-item">
                        <div class="score-label">Quality</div>
                        ${{fmtScore(s.gemini_quality_score, [0.3, 0.5])}}
                    </div>
                    <div class="score-item">
                        <div class="score-label">MMS Lang</div>
                        <div class="seg-value" style="margin:0">${{s.mms_lang_iso1 || s.mms_lang || '—'}} ${{s.mms_confidence ? '(' + parseFloat(s.mms_confidence).toFixed(2) + ')' : ''}}</div>
                    </div>
                    <div class="score-item">
                        <div class="score-label">Vox Lang</div>
                        <div class="seg-value" style="margin:0">${{s.vox_lang_iso1 || s.vox_lang || '—'}} ${{s.vox_confidence ? '(' + parseFloat(s.vox_confidence).toFixed(2) + ')' : ''}}</div>
                    </div>
                    <div class="score-item">
                        <div class="score-label">Duration</div>
                        <div class="score-val ${{dur >= 2 ? 'good' : dur >= 1 ? 'warn' : 'bad'}}">${{dur ? dur.toFixed(2) + 's' : '—'}}</div>
                    </div>
                    <div class="score-item">
                        <div class="score-label">Source</div>
                        <div class="seg-value" style="margin:0;font-size:12px">${{s.validation_source || '—'}}</div>
                    </div>
                </div>
                ${{s.was_split ? `
                <div style="margin-top:12px;padding:8px;background:#0f172a;border-radius:6px;font-size:12px;color:#94a3b8">
                    <strong>Split segment</strong>: from <code>${{s.original_file}}</code> index ${{s.split_index}}
                    <br>Original: ${{(s.original_duration_ms/1000).toFixed(1)}}s &rarr; Polished: ${{(s.duration_ms/1000).toFixed(1)}}s
                    ${{s.trimmed_start_ms ? '<br>Trimmed: start ' + s.trimmed_start_ms.toFixed(0) + 'ms, end ' + (s.trimmed_end_ms||0).toFixed(0) + 'ms' : ''}}
                    ${{s.abrupt_start ? '<br><span style=color:#ef4444>Abrupt start detected</span>' : ''}}
                    ${{s.abrupt_end ? '<br><span style=color:#ef4444>Abrupt end detected</span>' : ''}}
                </div>` : ''}}
            </div>
        </div>
    </div>`;
}}

init();
</script>
</body>
</html>"""


if __name__ == "__main__":
    port = int(sys.argv[sys.argv.index("--port") + 1]) if "--port" in sys.argv else 5111
    print(f"Validation Audit UI running at http://localhost:{port}")
    app.run(host="0.0.0.0", port=port, debug=False)
