"""Route a supplied report CSV. A CSV alone does not prove live provenance.
No network calls, database calls, external actions or automatic writes.
"""
import csv, hashlib, json, sys
from pathlib import Path

def blank(value):
    return value is None or value.strip().lower() in ('', 'null', '(null)', '<null>')

def optional_int(value):
    if blank(value): return None
    return int(value)

def read_rows(path):
    with Path(path).open(encoding='utf-8-sig', newline='') as f:
        reader=csv.DictReader(f)
        names=reader.fieldnames or []
        normalized=[x.split('.')[-1].strip('" ') for x in names]
        if len(normalized)!=len(set(normalized)) or not set(REQUIRED).issubset(normalized):
            raise ValueError('Missing or ambiguous report columns')
        rows=[]; seen=set()
        for raw in reader:
            if None in raw: raise ValueError('CSV row has extra fields')
            row={new:raw[old] for old,new in zip(names,normalized)}
            if any(row[k] is None for k in REQUIRED): raise ValueError('CSV row has missing fields')
            key=row[KEY]
            if blank(key) or key in seen: raise ValueError('Missing or duplicate report key')
            seen.add(key); rows.append(row)
    if not rows: raise ValueError('Empty report cannot verify this fixture')
    return rows

def route(path):
    path=Path(path)
    return {'input_sha256':hashlib.sha256(path.read_bytes()).hexdigest(),
            'source':'Supplied CSV; link actual console export evidence separately',
            'database_provenance_verified':False, 'external_actions_sent':False,
            'result':transform(read_rows(path))}

REQUIRED=['id', 'device_id', 'reading_no', 'reading_value', 'previous_id', 'resolved_previous_id', 'previous_device_id', 'previous_reading_no', 'previous_value', 'reason']
KEY='id'

def transform(rows):
    result=[]; seen_sequences=set()
    for row in sorted(rows,key=lambda x:(x['device_id'],int(x['reading_no']),x['id'])):
        number=optional_int(row['reading_no']); value=optional_int(row['reading_value']); previous=optional_int(row['previous_value'])
        seq=(row['device_id'],number)
        if number is None or number<1 or seq in seen_sequences: raise ValueError('Unstable or duplicate device sequence')
        seen_sequences.add(seq)
        if number==1:
            if not blank(row['previous_id']) or not blank(row['resolved_previous_id']): raise ValueError('Baseline unexpectedly has a predecessor')
            reason='baseline'
        else:
            if blank(row['previous_id']): raise ValueError('Predecessor ID must be explicit after the baseline')
            if not blank(row['resolved_previous_id']):
                if row['resolved_previous_id']!=row['previous_id'] or row['previous_device_id']!=row['device_id'] or optional_int(row['previous_reading_no'])!=number-1:
                    raise ValueError('Predecessor does not match the same-device sequence')
            elif previous is not None: raise ValueError('Unresolved predecessor has a value')
            reason='missing_comparison' if value is None or previous is None else 'reset_candidate' if value<previous else 'ordinary_increase'
        if row['reason']!=reason: raise ValueError('Report reason disagrees with the two readings')
        result.append({'id':row['id'],'reason':reason})
    return result

if __name__=='__main__':
    if len(sys.argv)!=2: raise SystemExit('Usage: python route-export.py report.csv')
    try: print(json.dumps(route(sys.argv[1]),indent=2))
    except (ValueError,KeyError): raise SystemExit('Report failed the declared CSV contract; inspect the source fields.')
