#!/usr/bin/env python3
"""Read-only matched-case cost arithmetic. Python 3.10+; standard library only.

Does not run a model, verify evidence, estimate uncertainty or forecast ROI.
One row is a case's ENTIRE workflow, including every retry and human correction.
"""
import argparse
import csv
import json
import sys
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
from pathlib import Path

FIELDS = {'case_id', 'workflow', 'work_minutes', 'review_minutes',
          'rework_minutes', 'api_tool_usd', 'accepted', 'evidence_ref'}


def nonnegative(value, label):
    try:
        n = Decimal(str(value))
    except InvalidOperation:
        raise ValueError(f'{label} must be a non-negative finite number') from None
    if not n.is_finite() or n < 0:
        raise ValueError(f'{label} must be a non-negative finite number')
    return n


def rounded(value):
    return float(value.quantize(Decimal('.01'), rounding=ROUND_HALF_UP))


def summarize(path, hourly, baseline='baseline'):
    rate = nonnegative(hourly, 'Hourly rate')
    groups = {}
    with Path(path).open(encoding='utf-8-sig', newline='') as stream:
        reader = csv.DictReader(stream)
        headers = reader.fieldnames or []
        if len(headers) != len(set(headers)) or set(headers) != FIELDS:
            raise ValueError('CSV headers must be unique and match the documented eight fields')
        for line, row in enumerate(reader, 2):
            if None in row or any(v is None for v in row.values()):
                raise ValueError(f'Line {line}: expected exactly eight fields')
            row = {k: v.strip() for k, v in row.items()}
            for name in ['case_id', 'workflow', 'evidence_ref']:
                if not row[name]:
                    raise ValueError(f'Line {line}: {name} is required')
            if row['accepted'] not in {'yes', 'no'}:
                raise ValueError(f'Line {line}: accepted must be yes or no')
            group = groups.setdefault(row['workflow'], {})
            if row['case_id'] in group:
                raise ValueError(f'Line {line}: Duplicate case in the same workflow; combine all retries in one row')
            minutes = sum((nonnegative(row[k], f'Line {line}: {k}') for k in
                           ['work_minutes', 'review_minutes', 'rework_minutes']), Decimal(0))
            group[row['case_id']] = {
                'human': minutes * rate / 60,
                'api': nonnegative(row['api_tool_usd'], f'Line {line}: api_tool_usd'),
                'accepted': row['accepted'] == 'yes'
            }
    if baseline not in groups or len(groups) < 2:
        raise ValueError(f'Include the {baseline} workflow and at least one candidate')
    cases = set(groups[baseline])
    if any(set(g) != cases for g in groups.values()):
        raise ValueError('Every workflow must contain exactly the same case IDs; do not drop failed or difficult work')
    results = {}
    baseline_accepted = sum(r['accepted'] for r in groups[baseline].values())
    for name, group in groups.items():
        accepted = sum(r['accepted'] for r in group.values())
        human = sum((r['human'] for r in group.values()), Decimal(0))
        api = sum((r['api'] for r in group.values()), Decimal(0))
        results[name] = {
            'cases': len(group), 'accepted': accepted,
            'acceptance_percent': rounded(Decimal(accepted) / len(group) * 100),
            'human_cost_usd': rounded(human), 'api_tool_cost_usd': rounded(api),
            'total_cost_usd': rounded(human + api),
            'cost_per_accepted_usd': rounded((human + api) / accepted) if accepted else None,
            'below_baseline_acceptance': accepted < baseline_accepted
        }
    return {
        'hourly_cost_usd': rounded(rate), 'baseline': baseline,
        'workflows': results,
        'limits': 'Arithmetic only. Acceptance is supplied by you, not verified by this script. '
                  'Same case IDs do not prove comparable difficulty, randomization or unbiased grading. '
                  'Include every retry in its case row. Add setup, fixed costs, downstream losses and uncertainty separately. '
                  'Released labor capacity is not automatically cash savings.'
    }


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('csv', help='Local matched-case CSV; see README.txt')
    parser.add_argument('--hourly', required=True, help='Fully loaded human cost per hour, USD')
    parser.add_argument('--baseline', default='baseline')
    args = parser.parse_args()
    try:
        result = summarize(args.csv, args.hourly, args.baseline)
    except (OSError, UnicodeError, ValueError, InvalidOperation, csv.Error) as error:
        print(f'Cannot compare: {error}', file=sys.stderr)
        return 2
    print(json.dumps(result, indent=2, allow_nan=False))
    return 0


if __name__ == '__main__':
    raise SystemExit(main())
