"""Ocean6 v0.1 offline fixture verifier. No network, signatures or payments."""
import hashlib
import json
import re
import sys
from datetime import datetime
from pathlib import Path

TRANSITIONS = {
    'submitted': {'accepted', 'rejected', 'cancelled'},
    'accepted': {'running', 'failed', 'cancelled', 'expired'},
    'running': {'delivered', 'failed', 'cancelled', 'expired'},
    'delivered': {'validated', 'disputed'},
    'validated': {'settled', 'disputed'},
}
STATES = set(TRANSITIONS) | {s for targets in TRANSITIONS.values() for s in targets}


def check(ok, message):
    if not ok:
        raise ValueError(message)


def utc(value):
    check(isinstance(value, str) and re.fullmatch(r'\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z', value), 'UTC timestamp format invalid')
    return datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ')


def verify(data, output_bytes, input_bytes):
    check(isinstance(data, dict) and data.get('schema_version') == '0.1', 'Unsupported reference version')
    check(data.get('example_only') is True, 'This verifier is only for explicitly marked examples')
    task, receipt, events = data.get('task'), data.get('receipt'), data.get('events')
    check(isinstance(task, dict) and isinstance(receipt, dict) and isinstance(events, list) and len(events) >= 2, 'Missing reference records')
    for field in ('task_id', 'attempt_id', 'input_sha256', 'acceptance_profile'):
        check(isinstance(task.get(field), str) and bool(task[field]), 'Missing task field: ' + field)
    for field in ('task_id', 'attempt_id', 'provider_id', 'runtime_version', 'output_sha256', 'delivered_at', 'status'):
        check(isinstance(receipt.get(field), str) and bool(receipt[field]), 'Missing receipt field: ' + field)
    for field in ('task_id', 'attempt_id'):
        check(receipt[field] == task[field], 'Receipt correlation mismatch: ' + field)
    check(receipt['status'] in STATES, 'Unknown receipt state')
    check('settlement' in receipt and receipt['settlement'] is None, 'This offline example must not claim payment')
    check(receipt['status'] != 'settled', 'A settled receipt is outside this offline example')
    previous_state, previous_time = None, None
    delivery_time = None
    for event in events:
        check(isinstance(event, dict), 'Invalid event')
        check(event.get('task_id') == task['task_id'] and event.get('attempt_id') == task['attempt_id'], 'Event correlation mismatch')
        state, timestamp = event.get('state'), utc(event.get('at'))
        check(state in STATES, 'Unknown event state')
        if previous_state is None:
            check(state == 'submitted', 'First state must be submitted')
        else:
            check(state in TRANSITIONS.get(previous_state, set()), 'Invalid state transition')
            check(timestamp >= previous_time, 'Non-monotonic event timestamps')
        if state == 'delivered':
            delivery_time = timestamp
        previous_state, previous_time = state, timestamp
    check(previous_state == receipt['status'], 'Receipt state differs from last event')
    check(delivery_time is not None and utc(receipt['delivered_at']) == delivery_time, 'Delivery timestamp mismatch')
    for value in (task['input_sha256'], receipt['output_sha256']):
        check(re.fullmatch(r'[a-f0-9]{64}', value), 'Invalid SHA-256 digest')
    check(hashlib.sha256(input_bytes).hexdigest() == task['input_sha256'], 'Input digest mismatch')
    actual = hashlib.sha256(output_bytes).hexdigest()
    check(actual == receipt['output_sha256'], 'Output digest mismatch')
    return actual


if __name__ == '__main__':
    try:
        if len(sys.argv) != 4:
            raise ValueError('Usage: python3 verify_receipt.py example.json result.txt input.txt')
        digest = verify(json.loads(Path(sys.argv[1]).read_text()), Path(sys.argv[2]).read_bytes(), Path(sys.argv[3]).read_bytes())
        print('PASS: offline fixture integrity, correlation and lifecycle checks')
        print('SHA-256:', digest)
        print('Does not prove provider identity, model execution, quality or payment.')
    except (ValueError, OSError, KeyError, TypeError) as exc:
        print('FAIL:', exc, file=sys.stderr)
        sys.exit(1)
