"""Prepare local email drafts from an Excel CSV export. Never connects or sends."""
import argparse
import csv
from decimal import Decimal, InvalidOperation
from email.message import EmailMessage
from email.policy import SMTP
from pathlib import Path
import re


def prepare(source, destination):
    required = {'Name', 'Email', 'Invoice', 'Amount'}
    drafts = []
    seen = set()
    with Path(source).open(encoding='utf-8-sig', newline='') as handle:
        reader = csv.DictReader(handle)
        if not required.issubset(reader.fieldnames or []):
            raise ValueError('Required columns: Name, Email, Invoice, Amount')
        for number, row in enumerate(reader, 2):
            values = {key: (row.get(key) or '').strip() for key in required}
            if any(not value or '\n' in value or '\r' in value for value in values.values()):
                raise ValueError(f'Row {number}: empty or multiline field')
            if not re.fullmatch(r'[^\s@,;<>]+@[^\s@,;<>]+\.[^\s@,;<>]+', values['Email']):
                raise ValueError(f'Row {number}: use one plain email address')
            invoice = values['Invoice']
            if not re.fullmatch(r'[A-Za-z0-9_-]{1,60}', invoice):
                raise ValueError(f'Row {number}: invalid invoice identifier')
            if invoice in seen:
                raise ValueError(f'Row {number}: duplicate invoice identifier')
            seen.add(invoice)
            if values['Amount'].startswith('-'):
                raise ValueError(f'Row {number}: amount cannot be negative')
            if not re.fullmatch(r'[0-9]+(?:\.[0-9]{1,2})?', values['Amount']):
                raise ValueError(f'Row {number}: use a plain amount with at most two decimal places')
            try:
                amount = Decimal(values['Amount'])
                amount = amount.quantize(Decimal('0.01'))
            except InvalidOperation as error:
                raise ValueError(f'Row {number}: invalid amount') from error
            message = EmailMessage(policy=SMTP)
            message['To'] = values['Email']
            message['Subject'] = f'Invoice {invoice} for review'
            message['X-Unsent'] = '1'
            message.set_content(f"Hi {values['Name']},\n\nPlease review invoice {invoice} for ${amount:.2f}.\n")
            drafts.append((invoice, message.as_bytes()))
    if not drafts:
        raise ValueError('No data rows found')
    # Validate the complete input before creating any output. Never overwrite a run.
    folder = Path(destination)
    folder.mkdir(parents=True, exist_ok=False)
    for invoice, payload in drafts:
        (folder / f'{invoice}.eml').write_bytes(payload)
    return len(drafts)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('csv_file')
    parser.add_argument('new_output_folder')
    args = parser.parse_args()
    try:
        count = prepare(args.csv_file, args.new_output_folder)
    except UnicodeDecodeError:
        parser.exit(1, 'Export the workbook as CSV UTF-8 (Comma delimited), then try again.\n')
    except (ValueError, OSError) as error:
        parser.exit(1, f'{error}\n')
    print(f'Prepared {count} local .eml files. Sent 0 messages.')
