"""Lead capture, duplicate flagging, spam checks, and outbound routing."""

from __future__ import annotations

import csv
import io
import json
import re
import smtplib
import threading
import uuid
from datetime import datetime, timezone
from email.message import EmailMessage
from pathlib import Path
from typing import Any
from urllib.request import Request, urlopen

import config

DATA_DIR = Path(__file__).resolve().parent / "data"
LEADS_PATH = DATA_DIR / "leads.json"
LOCK = threading.Lock()

EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
PHONE_RE = re.compile(r"[0-9]")
NC_ZIP_RE = re.compile(r"^\d{5}$")

CONSUMER_REQUIRED = [
    "first_name",
    "last_name",
    "phone",
    "email",
    "zip_code",
    "age_range",
    "tobacco_use",
    "coverage_amount",
    "best_time",
]

AGENT_REQUIRED = [
    "first_name",
    "last_name",
    "phone",
    "email",
    "city_state",
    "license_status",
    "resident_states",
    "years_experience",
    "market_focus",
    "sales_model",
    "support_needed",
    "best_time",
]


def _now() -> datetime:
    return datetime.now(timezone.utc)


def _load() -> list[dict[str, Any]]:
    if not LEADS_PATH.exists():
        return []
    try:
        return json.loads(LEADS_PATH.read_text(encoding="utf-8"))
    except json.JSONDecodeError:
        return []


def _save(leads: list[dict[str, Any]]) -> None:
    DATA_DIR.mkdir(parents=True, exist_ok=True)
    tmp = LEADS_PATH.with_suffix(".json.tmp")
    tmp.write_text(json.dumps(leads, indent=2), encoding="utf-8")
    tmp.replace(LEADS_PATH)


def normalize_phone(value: str) -> str:
    digits = re.sub(r"\D", "", value or "")
    if len(digits) == 11 and digits.startswith("1"):
        digits = digits[1:]
    return digits


def normalize_email(value: str) -> str:
    return (value or "").strip().lower()


def source_label_for(form_type: str, landing_path: str, utm_source: str) -> str:
    if form_type == "agent":
        return "Agent Interest Lead"
    source = (utm_source or "").strip().lower()
    if "youtube" in source or source in {"yt", "youtu.be"}:
        return "YouTube Consumer Lead"
    if "facebook" in source or source in {"fb", "meta", "ig", "instagram"}:
        return "Facebook Consumer Lead"
    if landing_path.rstrip("/") == "/nc-final-expense-quote":
        if "youtube" in source:
            return "YouTube Consumer Lead"
        return "Facebook Consumer Lead"
    return "Consumer Website Lead"


def validate_consumer(data: dict[str, str]) -> dict[str, str]:
    errors: dict[str, str] = {}
    for field in CONSUMER_REQUIRED:
        if not (data.get(field) or "").strip():
            errors[field] = "This field is required."
    email = data.get("email", "").strip()
    if email and not EMAIL_RE.match(email):
        errors["email"] = "Enter an email address in the format name@example.com."
    phone = normalize_phone(data.get("phone", ""))
    if data.get("phone") and len(phone) != 10:
        errors["phone"] = "Enter a 10-digit U.S. phone number."
    zip_code = (data.get("zip_code") or "").strip()
    if zip_code and not NC_ZIP_RE.match(zip_code):
        errors["zip_code"] = "Enter a 5-digit ZIP code."
    if data.get("age_range") and data["age_range"] not in config.AGE_RANGES:
        errors["age_range"] = "Choose an age range from the list."
    if data.get("tobacco_use") and data["tobacco_use"] not in config.TOBACCO_OPTIONS:
        errors["tobacco_use"] = "Choose a tobacco-use option from the list."
    if data.get("coverage_amount") and data["coverage_amount"] not in config.COVERAGE_AMOUNTS:
        errors["coverage_amount"] = "Choose a coverage amount from the list."
    if data.get("best_time") and data["best_time"] not in config.CONTACT_TIMES:
        errors["best_time"] = "Choose a contact time from the list."
    return errors


def validate_agent(data: dict[str, str]) -> dict[str, str]:
    errors: dict[str, str] = {}
    for field in AGENT_REQUIRED:
        if not (data.get(field) or "").strip():
            errors[field] = "This field is required."
    email = data.get("email", "").strip()
    if email and not EMAIL_RE.match(email):
        errors["email"] = "Enter an email address in the format name@example.com."
    phone = normalize_phone(data.get("phone", ""))
    if data.get("phone") and len(phone) != 10:
        errors["phone"] = "Enter a 10-digit U.S. phone number."
    return errors


def spam_flags(form: dict[str, str], started_at: str) -> tuple[bool, str]:
    honeypot = (form.get("website_url") or form.get("company_website") or "").strip()
    if honeypot:
        return True, "honeypot"
    try:
        started = float(started_at or "0")
        elapsed_ms = (_now().timestamp() * 1000) - started
        if started and elapsed_ms < 2500:
            return True, "too_fast"
    except ValueError:
        pass
    return False, ""


def is_duplicate(leads: list[dict[str, Any]], form_type: str, email: str, phone: str) -> bool:
    email_n = normalize_email(email)
    phone_n = normalize_phone(phone)
    for lead in leads:
        if lead.get("form_type") != form_type:
            continue
        if lead.get("spam"):
            continue
        fields = lead.get("fields") or {}
        if email_n and normalize_email(fields.get("email", "")) == email_n:
            return True
        if phone_n and normalize_phone(fields.get("phone", "")) == phone_n:
            return True
    return False


def _post_webhook(url: str, payload: dict[str, Any]) -> tuple[bool, str]:
    if not url:
        return False, "not configured"
    try:
        body = json.dumps(payload).encode("utf-8")
        req = Request(
            url,
            data=body,
            headers={"Content-Type": "application/json", "User-Agent": "NCSBFC-Leads/1.0"},
            method="POST",
        )
        with urlopen(req, timeout=8) as resp:
            return True, f"HTTP {resp.status}"
    except Exception as exc:  # noqa: BLE001 - routing should never break form capture
        return False, str(exc)


def send_lead_email(lead: dict[str, Any]) -> tuple[bool, str]:
    to_addr = config.LEAD_NOTIFY_EMAIL
    if not to_addr:
        return False, "LEAD_NOTIFY_EMAIL not set"
    fields = lead.get("fields") or {}
    utm = lead.get("utm") or {}
    kind = "Quote request" if lead.get("form_type") == "consumer" else "Agent interest"
    lines = [
        f"{kind} from the NCSBFC website",
        "",
        f"Name: {fields.get('first_name', '')} {fields.get('last_name', '')}",
        f"Phone: {fields.get('phone', '')}",
        f"Email: {fields.get('email', '')}",
        f"ZIP: {fields.get('zip_code', '')}",
        f"Age range: {fields.get('age_range', '')}",
        f"Tobacco: {fields.get('tobacco_use', '')}",
        f"Coverage: {fields.get('coverage_amount', '')}",
        f"Best time: {fields.get('best_time', '')}",
        f"City/state: {fields.get('city_state', '')}",
        f"License status: {fields.get('license_status', '')}",
        f"Note: {fields.get('notes', '') or fields.get('support_needed', '')}",
        "",
        f"Source: {lead.get('source_label', '')}",
        f"Page: {lead.get('landing_page_url', '')}",
        f"UTM: {utm.get('utm_source', '')} / {utm.get('utm_campaign', '')}",
        f"Submitted: {lead.get('submitted_at', '')}",
        f"Duplicate: {lead.get('duplicate', False)}",
    ]
    msg = EmailMessage()
    msg["Subject"] = f"NCSBFC {kind}: {fields.get('first_name', '')} {fields.get('last_name', '')}".strip()
    msg["From"] = config.SMTP_FROM or to_addr
    msg["To"] = to_addr
    if fields.get("email"):
        msg["Reply-To"] = fields["email"]
    msg.set_content("\n".join(lines))
    try:
        with smtplib.SMTP(config.SMTP_HOST, config.SMTP_PORT, timeout=12) as smtp:
            if config.SMTP_USE_TLS:
                smtp.starttls()
            if config.SMTP_USER and config.SMTP_PASSWORD:
                smtp.login(config.SMTP_USER, config.SMTP_PASSWORD)
            smtp.send_message(msg)
        return True, f"sent to {to_addr}"
    except Exception as exc:  # noqa: BLE001
        return False, str(exc)


def route_lead(lead: dict[str, Any]) -> dict[str, str]:
    payload = {
        "source_system": "ncsbfc-families",
        "form_type": lead["form_type"],
        "source_label": lead["source_label"],
        "submitted_at": lead["submitted_at"],
        "landing_page_url": lead["landing_page_url"],
        "referrer_url": lead["referrer_url"],
        "utm": lead["utm"],
        "fields": lead["fields"],
        "duplicate": lead["duplicate"],
        "id": lead["id"],
    }
    results: dict[str, str] = {}
    targets = {
        "webhook": config.LEAD_WEBHOOK_URL,
        "zapier": config.ZAPIER_WEBHOOK_URL,
        "make": config.MAKE_WEBHOOK_URL,
        "google_sheets": config.GOOGLE_SHEETS_WEBHOOK_URL,
        "crm": config.CRM_WEBHOOK_URL,
    }
    for name, url in targets.items():
        if not url:
            results[name] = "placeholder — not configured"
            continue
        ok, detail = _post_webhook(url, payload)
        results[name] = "sent: " + detail if ok else "failed: " + detail
    ok, detail = send_lead_email(lead)
    results["email_notify"] = ("sent: " if ok else "failed: ") + detail
    return results


def store_lead(
    *,
    form_type: str,
    fields: dict[str, str],
    utm: dict[str, str],
    landing_page_url: str,
    referrer_url: str,
    source_label: str,
    spam: bool,
    spam_reason: str,
) -> dict[str, Any]:
    with LOCK:
        leads = _load()
        duplicate = is_duplicate(leads, form_type, fields.get("email", ""), fields.get("phone", ""))
        lead = {
            "id": str(uuid.uuid4()),
            "form_type": form_type,
            "source_label": source_label,
            "submitted_at": _now().isoformat(),
            "landing_page_url": landing_page_url,
            "referrer_url": referrer_url,
            "utm": {
                "utm_source": utm.get("utm_source", ""),
                "utm_medium": utm.get("utm_medium", ""),
                "utm_campaign": utm.get("utm_campaign", ""),
                "utm_content": utm.get("utm_content", ""),
                "utm_term": utm.get("utm_term", ""),
            },
            "fields": fields,
            "duplicate": duplicate,
            "spam": spam,
            "spam_reason": spam_reason,
            "routing": {},
        }
        if not spam:
            lead["routing"] = route_lead(lead)
        leads.append(lead)
        _save(leads)
        return lead


def list_leads(include_spam: bool = True) -> list[dict[str, Any]]:
    with LOCK:
        leads = _load()
    if not include_spam:
        leads = [lead for lead in leads if not lead.get("spam")]
    return list(reversed(leads))


def export_csv(leads: list[dict[str, Any]]) -> str:
    output = io.StringIO()
    fieldnames = [
        "id",
        "form_type",
        "source_label",
        "submitted_at",
        "duplicate",
        "spam",
        "first_name",
        "last_name",
        "phone",
        "email",
        "zip_code",
        "city_state",
        "landing_page_url",
        "referrer_url",
        "utm_source",
        "utm_medium",
        "utm_campaign",
        "utm_content",
        "utm_term",
    ]
    writer = csv.DictWriter(output, fieldnames=fieldnames)
    writer.writeheader()
    for lead in leads:
        fields = lead.get("fields") or {}
        utm = lead.get("utm") or {}
        writer.writerow(
            {
                "id": lead.get("id"),
                "form_type": lead.get("form_type"),
                "source_label": lead.get("source_label"),
                "submitted_at": lead.get("submitted_at"),
                "duplicate": lead.get("duplicate"),
                "spam": lead.get("spam"),
                "first_name": fields.get("first_name", ""),
                "last_name": fields.get("last_name", ""),
                "phone": fields.get("phone", ""),
                "email": fields.get("email", ""),
                "zip_code": fields.get("zip_code", ""),
                "city_state": fields.get("city_state", ""),
                "landing_page_url": lead.get("landing_page_url", ""),
                "referrer_url": lead.get("referrer_url", ""),
                "utm_source": utm.get("utm_source", ""),
                "utm_medium": utm.get("utm_medium", ""),
                "utm_campaign": utm.get("utm_campaign", ""),
                "utm_content": utm.get("utm_content", ""),
                "utm_term": utm.get("utm_term", ""),
            }
        )
    return output.getvalue()
