"""Recipient import & validation: CSV, TXT, manual.

NEVER accepts JSON recipient files. Validates email syntax, duplicates,
empty/invalid rows, suppression list and prior bounces.
"""
import csv
import io
import re
import secrets
from typing import List, Optional

from email_validator import validate_email, EmailNotValidError

# Recognized CSV headers (case-insensitive); mapped to model fields.
HEADER_ALIASES = {
    "email": "email", "e-mail": "email", "emailaddress": "email", "email_address": "email",
    "firstname": "first_name", "first_name": "first_name", "fname": "first_name", "givenname": "first_name",
    "lastname": "last_name", "last_name": "last_name", "lname": "last_name", "surname": "last_name",
    "companyname": "company_name", "company_name": "company_name", "company": "company_name",
    "companyphone": "company_phone", "company_phone": "company_phone", "phone": "company_phone",
    "customeremail": "customer_email", "customer_email": "customer_email",
}

EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")


class ImportReport:
    def __init__(self):
        self.rows_seen = 0
        self.valid: List[dict] = []
        self.invalid: List[dict] = []
        self.duplicates: List[dict] = []
        self.suppressed: List[dict] = []
        self.bounced: List[dict] = []
        self.errors: List[str] = []

    def summary(self) -> dict:
        return {
            "total_imported": self.rows_seen,
            "valid_recipients": len(self.valid),
            "invalid_recipients": len(self.invalid),
            "duplicate_recipients": len(self.duplicates),
            "suppressed_recipients": len(self.suppressed),
            "bounced_recipients": len(self.bounced),
            "errors": self.errors,
        }


def is_valid_email(addr: str) -> bool:
    if not addr or len(addr) > 320:
        return False
    try:
        v = validate_email(addr, check_deliverability=False)
        return True
    except EmailNotValidError:
        return EMAIL_RE.match(addr) is not None


def parse_txt(content: str) -> List[str]:
    """One email per line (optionally 'email, First, Last' comma-separated)."""
    rows = []
    for line in content.splitlines():
        line = line.strip().strip(",")
        if not line or line.startswith("#"):
            continue
        rows.append(line)
    return rows


def parse_csv(content: str) -> tuple:
    """Parse CSV with header mapping. Returns (records, headers, errors)."""
    reader = csv.reader(io.StringIO(content))
    rows = [r for r in reader]
    if not rows:
        return [], [], ["File is empty"]
    header = [h.strip() for h in rows[0]]
    norm = {h.lower().replace(" ", "_").replace("-", "_"): h for h in header if h}
    field_map = {}
    custom_cols = []
    for norm_name, orig in norm.items():
        # accept both hyphen and underscore spellings of aliases (e-mail / e_mail)
        alias = HEADER_ALIASES.get(norm_name) or HEADER_ALIASES.get(norm_name.replace("_", "-"))
        if alias:
            field_map[orig] = alias
        else:
            custom_cols.append(orig)
    records, errors = [], []
    for i, row in enumerate(rows[1:], start=2):
        if not any(cell.strip() for cell in row):
            errors.append(f"Row {i}: empty row skipped")
            continue
        rec = {}
        for idx, orig in enumerate(header):
            val = row[idx].strip() if idx < len(row) else ""
            target = field_map.get(orig)
            if target:
                rec[target] = val
            elif orig:
                cf = rec.setdefault("custom_fields", {})
                cf[orig] = val
        rec["_row"] = i
        records.append(rec)
    return records, header, errors


def validate_rows(records: List[dict], suppression: set, bounced: set,
                  existing_emails: Optional[set] = None, max_recipients: int = 2000) -> ImportReport:
    """Validate parsed rows against all rules. Returns an ImportReport."""
    rpt = ImportReport()
    seen: set = set()
    for rec in records:
        rpt.rows_seen += 1
        email = (rec.get("email") or rec.get("customer_email") or "").strip().lower()
        row = rec.get("_row")
        base = {k: v for k, v in rec.items() if k not in ("_row", "custom_fields")}
        if not email:
            rpt.invalid.append({**base, "reason": "Missing email address", "row": row})
            continue
        if not is_valid_email(email):
            rpt.invalid.append({**base, "email": email, "reason": "Invalid email syntax", "row": row})
            continue
        if email in seen or (existing_emails and email in existing_emails):
            rpt.duplicates.append({**base, "email": email, "reason": "Duplicate address in import", "row": row})
            continue
        if email in suppression:
            rpt.suppressed.append({**base, "email": email, "reason": "On suppression list", "row": row})
            continue
        if email in bounced:
            rpt.bounced.append({**base, "email": email, "reason": "Previously bounced", "row": row})
            continue
        seen.add(email)
        rpt.valid.append({
            "email": email,
            "first_name": (rec.get("first_name") or "").strip(),
            "last_name": (rec.get("last_name") or "").strip(),
            "company_name": (rec.get("company_name") or "").strip(),
            "company_phone": (rec.get("company_phone") or "").strip(),
            "customer_email": (rec.get("customer_email") or rec.get("email") or "").strip(),
            "custom_fields": rec.get("custom_fields", {}),
        })
    if len(rpt.valid) > max_recipients:
        rpt.errors.append(f"Import exceeds maximum of {max_recipients} recipients; only first {max_recipients} kept.")
        rpt.valid = rpt.valid[:max_recipients]
    return rpt


def make_unsub_token() -> str:
    return secrets.token_urlsafe(24)
