"""Bulk CSV lead import — trade show lists, purchased lists, partner referrals.

Auto-detects column names, checks suppression registry and duplicates,
validates each row, and bulk-inserts into the backlog.
"""

import csv
import io
import uuid
from datetime import datetime, date, timezone
from pathlib import Path
from typing import Optional

from state.backlog import load_backlog, save_backlog
from state.schema import validate_lead
from state.suppressions import is_suppressed


# Known column name variants for auto-mapping
COLUMN_ALIASES = {
    "email": ["email", "email_address", "e-mail", "contact_email", "mail"],
    "name": ["name", "company", "company_name", "business_name", "business", "organization"],
    "contact_name": ["contact", "contact_name", "first_name", "person", "full_name"],
    "phone": ["phone", "phone_number", "telephone", "tel", "mobile"],
    "city": ["city", "town", "municipality"],
    "state": ["state", "st", "province"],
    "zip": ["zip", "zip_code", "postal", "postal_code"],
    "naics": ["naics", "naics_code", "industry_code", "sic"],
    "equipment_model": ["model", "equipment", "machine", "copier", "device"],
    "equipment_serial": ["serial", "serial_number", "sn", "asset_tag"],
    "contract_expiration": ["expiration", "contract_end", "maturity", "renewal_date", "end_date", "contract_expiry"],
    "notes": ["notes", "comment", "comments", "remarks"],
}


def _auto_map_columns(headers: list[str]) -> dict:
    """Map CSV headers to schema fields using known aliases.
    Returns {csv_column: schema_field} mapping.
    """
    mapping = {}
    header_lower = [h.strip().lower() for h in headers]
    for schema_field, aliases in COLUMN_ALIASES.items():
        for i, h in enumerate(header_lower):
            if h in aliases:
                mapping[headers[i]] = schema_field
                break
    return mapping


def _detect_encoding(path: Path) -> str:
    """Detect CSV encoding with fallback chain."""
    raw = path.read_bytes()
    for enc in ("utf-8-sig", "utf-8", "latin-1", "cp1252"):
        try:
            raw.decode(enc)
            return enc if enc != "utf-8-sig" else "utf-8-sig"
        except (UnicodeDecodeError, UnicodeError):
            continue
    return "latin-1"


def _build_lead_from_row(
    row: dict,
    column_mapping: dict,
    lead_type: str,
    intel: str,
) -> Optional[dict]:
    """Build a lead dict from a CSV row and column mapping.
    Returns None if the row is invalid (missing required fields).
    Returns a dict with the lead and any error message.
    """
    errors = []

    # Extract mapped fields
    email = ""
    name = ""
    contact_name = ""
    phone = ""
    city = ""
    state_val = ""
    zip_val = ""
    naics = ""
    equipment_model = ""
    equipment_serial = ""
    contract_expiration = ""
    notes = ""

    for csv_col, schema_field in column_mapping.items():
        raw = row.get(csv_col, "").strip()
        if schema_field == "email":
            email = raw
        elif schema_field == "name":
            name = raw
        elif schema_field == "contact_name":
            contact_name = raw
        elif schema_field == "phone":
            phone = raw
        elif schema_field == "city":
            city = raw
        elif schema_field == "state":
            state_val = raw
        elif schema_field == "zip":
            zip_val = raw
        elif schema_field == "naics":
            naics = raw
        elif schema_field == "equipment_model":
            equipment_model = raw
        elif schema_field == "equipment_serial":
            equipment_serial = raw
        elif schema_field == "contract_expiration":
            contract_expiration = raw
        elif schema_field == "notes":
            notes = raw

    # Required field: email
    if not email:
        return None

    # For renewal leads, equipment_model and contract_expiration are required
    if lead_type == "renewal":
        if not equipment_model:
            return None
        if not contract_expiration:
            return None

    # Build greeting name
    greeting_name = ""
    greeting_source = "contact_first"
    if contact_name:
        first = contact_name.split()[0]
        greeting_name = first.capitalize()
        greeting_source = "contact_first"

    now = datetime.now(timezone.utc).isoformat()
    today_iso = date.today().isoformat()

    lead = {
        "id": f"import_{uuid.uuid4().hex[:12]}",
        "name": name or "Unknown",
        "lead_type": lead_type,
        "lead_status": "active",

        "filing_date": today_iso,
        "harvested_at": now,
        "harvest_source": "csv_import",

        "email": email,
        "greeting_name": greeting_name,
        "greeting_name_source": greeting_source,
        "phone": phone,

        "city": city,
        "state": state_val,
        "zip": zip_val,
        "naics": naics,

        "notes": notes,
        "operator_intel": intel or "",

        "created_at": now,
        "last_updated": now,
        "temporal_category": "fresh",
        "equipment_model": equipment_model or "",
        "equipment_serial": equipment_serial or "",
        "contract_expiration": contract_expiration or "",
    }

    if lead_type == "renewal" and equipment_model:
        from capabilities.renewals import recommend_upgrade
        upgrade = recommend_upgrade(equipment_model)
        lead["recommended_upgrade"] = upgrade["recommended_model"]
        lead["upgrade_reason"] = upgrade["reason"]
        lead["meter_insight"] = upgrade["meter_insight"]
        lead["upgrade_urgency"] = upgrade["upgrade_urgency"]

    return lead


def import_from_csv(
    csv_path: str,
    lead_type: str = "cold",
    dry_run: bool = False,
    intel: Optional[str] = None,
    column_mapping: Optional[dict] = None,
    skip_zoho_check: bool = False,
) -> dict:
    """Import leads from a CSV file.

    Steps:
    1. Read CSV with auto-encoding detection
    2. Map columns to schema fields (auto + explicit overrides)
    3. For each row: build lead, check suppression, check duplicate, check Zoho, validate
    4. If not dry_run: append valid leads to backlog and save

    Returns summary dict.
    """
    path = Path(csv_path)
    if not path.exists():
        raise FileNotFoundError(f"CSV file not found: {csv_path}")

    encoding = _detect_encoding(path)
    raw = path.read_bytes()
    text = raw.decode(encoding)

    reader = csv.DictReader(io.StringIO(text))
    if not reader.fieldnames:
        raise ValueError("CSV has no headers")

    headers = reader.fieldnames

    # Auto-map columns, then apply explicit overrides
    auto_mapping = _auto_map_columns(headers)
    if column_mapping:
        auto_mapping.update(column_mapping)
    final_mapping = auto_mapping

    backlog = load_backlog()
    existing_emails = {l.get("email", "").lower().strip() for l in backlog if l.get("email")}

    # Pre-fetch Zoho email sets for batch dedup
    zoho_lead_emails = None
    zoho_contact_emails = None
    if not skip_zoho_check:
        from state.zoho_dedup import build_zoho_email_sets
        try:
            zoho_lead_emails, zoho_contact_emails = build_zoho_email_sets()
        except Exception:
            pass  # Fail open

    imported = []
    skipped_duplicate = 0
    skipped_suppressed = 0
    skipped_zoho = 0
    skipped_invalid = 0
    row_errors = []

    csv_email_set = set()

    for row_num, row in enumerate(reader, start=2):
        row_email = row.get(next((c for c, f in final_mapping.items() if f == "email"), ""), "").strip().lower()

        # Check suppression (before duplicates)
        if row_email:
            suppressed, reason = is_suppressed(row_email)
            if suppressed:
                skipped_suppressed += 1
                continue

        # Zoho dedup check (batch mode)
        if row_email and zoho_lead_emails is not None and zoho_contact_emails is not None:
            from state.zoho_dedup import check_email_in_sets
            in_zoho, _ = check_email_in_sets(row_email, zoho_lead_emails, zoho_contact_emails)
            if in_zoho:
                skipped_zoho += 1
                continue

        # Check duplicate within CSV (same email twice)
        if row_email:
            if row_email in csv_email_set:
                skipped_duplicate += 1
                continue
            csv_email_set.add(row_email)

        # Check duplicate against backlog
        if row_email and row_email in existing_emails:
            skipped_duplicate += 1
            continue

        # Build lead
        lead = _build_lead_from_row(row, final_mapping, lead_type, intel or "")
        if lead is None:
            skipped_invalid += 1
            row_errors.append(f"Row {row_num}: missing required field (email{' ,equipment model, expiration' if lead_type == 'renewal' else ''})")
            continue

        # Validate
        is_valid, errors = validate_lead(lead)
        if not is_valid:
            skipped_invalid += 1
            row_errors.append(f"Row {row_num}: validation failed: {', '.join(errors)}")
            continue

        imported.append(lead)

    result = {
        "imported": len(imported),
        "skipped_duplicate": skipped_duplicate,
        "skipped_suppressed": skipped_suppressed,
        "skipped_zoho": skipped_zoho,
        "skipped_invalid": skipped_invalid,
        "total_rows": len(imported) + skipped_duplicate + skipped_suppressed + skipped_zoho + skipped_invalid,
        "column_mapping": final_mapping,
        "row_errors": row_errors,
    }

    if not dry_run and imported:
        backlog.extend(imported)
        save_backlog(backlog)

    return result
