"""Reference helper for fictional practice enquiries; source is never modified."""
import argparse
import csv
import json
from collections import Counter
from decimal import Decimal, InvalidOperation
from pathlib import Path


def summarise(source):
    channels = Counter()
    references = set()
    known = Decimal(0)
    missing = 0
    with Path(source).open(encoding='utf-8-sig', newline='') as handle:
        reader = csv.DictReader(handle)
        if reader.fieldnames != ['reference', 'channel', 'minutes']:
            raise ValueError('Expected columns: reference,channel,minutes')
        for line, row in enumerate(reader, start=2):
            if None in row or any(row.get(key) is None for key in reader.fieldnames):
                raise ValueError(f'Incomplete or extra fields at row {line}')
            reference = row['reference'].strip()
            channel = row['channel'].strip()
            if not reference or not channel:
                raise ValueError(f'Missing reference or channel at row {line}')
            if reference in references:
                raise ValueError(f'Duplicate reference at row {line}')
            references.add(reference)
            channels[channel] += 1
            raw = row['minutes'].strip()
            if not raw:
                missing += 1
                continue
            try:
                value = Decimal(raw)
            except InvalidOperation:
                raise ValueError(f'Non-numeric minutes at row {line}') from None
            if not value.is_finite() or value < 0:
                raise ValueError(f'Minutes must be finite and non-negative at row {line}')
            known += value
    # Decimal totals are strings so the JSON output preserves their decimal value.
    total = format(known, 'f')
    return {
        'records': len(references),
        'channelCounts': dict(sorted(channels.items())),
        'knownMinuteCount': len(references) - missing,
        'missingMinuteCount': missing,
        'knownMinutes': total,
        'fullMinutesTotal': None if missing else total,
    }


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('source', help='CSV with reference,channel,minutes columns')
    parser.add_argument('--output', help='Optional new JSON file; existing files are refused')
    args = parser.parse_args()
    try:
        result = summarise(args.source)
        output = json.dumps(result, indent=2, ensure_ascii=False) + '\n'
        if args.output:
            if Path(args.output).resolve() == Path(args.source).resolve():
                raise ValueError('Output must be different from the source')
            with Path(args.output).open('x', encoding='utf-8') as handle:
                handle.write(output)
            print('Created the requested new JSON output.')
        else:
            print(output, end='')
    except (ValueError, OSError) as error:
        parser.exit(2, f'Cannot complete the summary: {error}\n')


if __name__ == '__main__':
    main()
