"""Conservative review of supplied timestamp metadata; no audio or inference."""

import argparse
import json
import math


def number(value, name):
    if isinstance(value, bool) or not isinstance(value, (int, float)):
        raise ValueError(name + ' must be a finite number')
    try:
        value = float(value)
    except OverflowError as error:
        raise ValueError(name + ' exceeds the supported numeric range') from error
    if not math.isfinite(value):
        raise ValueError(name + ' must be a finite number')
    return value


def interval(row, duration):
    if not isinstance(row, dict):
        raise ValueError('Each interval must be an object')
    start, end = number(row.get('start'), 'start'), number(row.get('end'), 'end')
    if not 0 <= start < end <= duration:
        raise ValueError('Intervals must be positive, ordered and inside the recording')
    return start, end


def covered(pieces):
    end = None
    total = 0.0
    for left, right in sorted(pieces):
        if end is None or left >= end:
            total += right - left
            end = right
        elif right > end:
            total += right - end
            end = right
    return total


def review(record):
    if not isinstance(record, dict) or record.get('time_unit') != 'seconds':
        raise ValueError('Expected an editorial record with explicit seconds')
    key = record.get('diarization_recording_id')
    if not isinstance(key, str) or not key.strip() or key != record.get('asr_recording_id'):
        raise ValueError('Both branches must have the same nonempty recording ID')
    duration = number(record.get('audio_duration_seconds'), 'duration')
    if duration <= 0:
        raise ValueError('Recording duration must be positive')
    turns, words = record.get('turns'), record.get('words')
    if not isinstance(turns, list) or not isinstance(words, list) or not words:
        raise ValueError('Expected a turn list and a nonempty word list')
    normalized = []
    for turn in turns:
        start, end = interval(turn, duration)
        speaker = turn.get('speaker')
        if not isinstance(speaker, str) or not speaker.strip():
            raise ValueError('Each turn needs a nonempty anonymous speaker label')
        normalized.append((start, end, speaker))
    if len({speaker for _, _, speaker in normalized}) > 8:
        raise ValueError('This editorial schema allows at most eight distinct labels')
    output = []
    for word in words:
        start, end = interval(word, duration)
        if not isinstance(word.get('word'), str) or not word['word'].strip():
            raise ValueError('Each word needs nonempty text')
        candidates = {}
        for left, right, speaker in normalized:
            a, b = max(start, left), min(end, right)
            if b > a:
                candidates.setdefault(speaker, []).append((a, b))
        labels = sorted(candidates)
        speaker = None
        if not labels:
            status = 'UNASSIGNED'
        elif len(labels) > 1:
            status = 'AMBIGUOUS'
        elif covered(candidates[labels[0]]) >= end - start - 1e-9:
            status, speaker = 'MATCHED_UNVERIFIED_AUDIO', labels[0]
        else:
            status = 'PARTIAL_SPEECH'
        output.append({'word': word['word'], 'start': start, 'end': end,
                       'reviewStatus': status, 'speakerCandidates': labels, 'speaker': speaker})
    return {'status': 'METADATA_REVIEW_NOT_AUDIO_ACCURACY_PROOF', 'recordingId': key,
            'words': output, 'audioInspected': False, 'speakerIdentityVerified': False,
            'modelInferenceRequests': 0}


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('record')
    args = parser.parse_args()
    try:
        with open(args.record, encoding='utf-8') as source:
            print(json.dumps(review(json.load(source)), indent=2))
    except (OSError, ValueError) as error:
        parser.exit(1, str(error) + '\n')
