#!/usr/bin/env python3
"""Flag referral handoff questions locally. Does not choose a referrer or award."""
import argparse
import csv
import datetime as dt
import re
import sys
from collections import Counter

FIELDS = ['submission_id', 'person_key', 'role_id', 'referrer_key', 'policy_version',
          'submitted_on', 'acknowledged_on', 'followup_on', 'owner_id',
          'contact_permission', 'prior_record']
OUTPUT = FIELDS + ['same_role_submissions', 'review_flags', 'followup_status']
TOKEN = re.compile(r'[A-Za-z0-9][A-Za-z0-9_-]{0,63}\Z')


def date(value):
    if not re.fullmatch(r'\d{4}-\d{2}-\d{2}', value or ''):
        raise ValueError('dates must use YYYY-MM-DD')
    return dt.date.fromisoformat(value)


def review(rows, cutoff):
    checked, seen = [], set()
    for line, row in enumerate(rows, 2):
        try:
            if set(row) != set(FIELDS) or any(v is None for v in row.values()):
                raise ValueError('row must have exactly the documented fields')
            for key in FIELDS[:5] + ['owner_id']:
                if key == 'owner_id' and row[key] == '':
                    continue
                if not TOKEN.fullmatch(row[key]):
                    raise ValueError(f'{key} needs an opaque 1-64 character identifier')
            if row['submission_id'] in seen:
                raise ValueError('duplicate submission_id; reconcile the export first')
            seen.add(row['submission_id'])
            submitted = date(row['submitted_on'])
            ack = date(row['acknowledged_on']) if row['acknowledged_on'] else None
            due = date(row['followup_on']) if row['followup_on'] else None
            if submitted > cutoff:
                raise ValueError('submitted_on is after the reporting cutoff')
            if ack and not submitted <= ack <= cutoff:
                raise ValueError('acknowledged_on must be between submitted_on and cutoff')
            if due and due < submitted:
                raise ValueError('followup_on must not precede submitted_on')
            if any(row[k] not in ('yes', 'no', 'unknown') for k in ['contact_permission', 'prior_record']):
                raise ValueError('contact_permission and prior_record require yes, no or unknown')
            checked.append((row, ack, due))
        except ValueError as exc:
            raise ValueError(f'CSV line {line}: {exc}') from None
    groups = Counter((r['person_key'], r['role_id']) for r, _, _ in checked)
    output = []
    for row, ack, due in checked:
        n = groups[row['person_key'], row['role_id']]
        flags = []
        if n > 1:
            flags.append('duplicate_same_role')
        if row['contact_permission'] != 'yes':
            flags.append('permission_hold')
        if row['prior_record'] == 'yes':
            flags.append('prior_record_review')
        elif row['prior_record'] == 'unknown':
            flags.append('record_check')
        if not row['owner_id']:
            flags.append('owner_missing')
        if not ack:
            flags.append('acknowledgement_missing')
        if not due:
            flags.append('followup_missing')
        when = 'unknown' if not due else 'overdue' if due < cutoff else 'due_today' if due == cutoff else 'scheduled'
        output.append({**row, 'same_role_submissions': n,
                       'review_flags': ';'.join(flags) or 'none', 'followup_status': when})
    return output


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('csv_file')
    parser.add_argument('--as-of', required=True, help='Calendar date in your one documented timezone')
    args = parser.parse_args()
    try:
        cutoff = date(args.as_of)
        with open(args.csv_file, newline='', encoding='utf-8-sig') as source:
            reader = csv.DictReader(source)
            if reader.fieldnames != FIELDS:
                raise ValueError('CSV header/order must match the supplied template')
            output = review(reader, cutoff)
        writer = csv.DictWriter(sys.stdout, fieldnames=OUTPUT, lineterminator='\n')
        writer.writeheader()
        writer.writerows(output)
    except (ValueError, OSError, csv.Error) as exc:
        print(str(exc), file=sys.stderr)
        return 2
    return 0


if __name__ == '__main__':
    sys.exit(main())
