"""Enrichment capability - orchestrates domain and Brave enrichment.

Pattern A (standard) for baseline 1.0:
- Domain enrichment (website check, phone scrape)
- Brave Search (description, phone, county)
- Competitor check (if website found)
- Equipment context (NAICS lookup)
- Agent/principal data (from CT SoS datasets)
- Filing history signals (for historical/high-score leads)
"""

import json
import os
import re
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional

from integrations.brave import brave_enrich_lead
from integrations.domain import (
    extract_domain_from_email,
    check_website_exists,
    scrape_phones_from_website,
    competitor_check,
)
from integrations.ctsos import FILING_SERVICE_DOMAINS, fetch_agents_bulk, fetch_principals_bulk
from state.backlog import get_lead, update_lead


CREDENTIAL_SUFFIXES = [
    "M.D.", "MD", "D.M.D.", "DMD", "D.D.S.", "DDS", "D.O.", "DO",
    "Ph.D.", "PhD", "J.D.", "JD", "Esq.", "Esq",
    "MBA", "CPA", "RN", "NP", "PA", "Psy.D.", "PsyD",
    "DPT", "OTD", "Au.D.", "AuD",
]


def _normalize_greeting_name(name: str) -> str:
    """Normalize a name for use as a greeting.

    1. Title-case: "BUHLE MHLONGO" → "Buhle Mhlongo"
    2. Strip credential suffixes: "Anisha Singh, M.D." → "Anisha Singh"
    3. Extract first name only: "Anisha Singh" → "Anisha"

    Returns the cleaned first name.
    """
    if not name:
        return ""

    s = name.strip()

    # Title-case normalization (handles ALL CAPS and mixed case)
    if s and any(c.isalpha() for c in s):
        s = s.title()

    # Strip credential suffixes
    for cred in CREDENTIAL_SUFFIXES:
        # With comma: "Anisha Singh, M.D."
        s = re.sub(rf',\s*{re.escape(cred)}\b', '', s, flags=re.I)
        # Without comma: "Anisha Singh M.D."
        s = re.sub(rf'\s+{re.escape(cred)}\b', '', s, flags=re.I)

    s = s.strip()

    # Extract first name only
    parts = s.split()
    if parts:
        return parts[0]

    return s


EQUIPMENT_MAPPING_PATH = Path("./config/equipment-mapping.json")


def _load_equipment_mapping() -> Dict:
    """Load equipment mapping from JSON file."""
    if not EQUIPMENT_MAPPING_PATH.exists():
        return {}
    
    try:
        with open(EQUIPMENT_MAPPING_PATH, 'r') as f:
            return json.load(f)
    except (json.JSONDecodeError, IOError):
        return {}


def get_equipment_for_naics(naics: str) -> Dict:
    """Look up equipment context for a NAICS code.
    
    Args:
        naics: NAICS code string
    
    Returns:
        Dict with:
        - talk_track: str
        - typical_volume: str
        - recommended_models: list
    """
    mapping = _load_equipment_mapping()
    
    if not naics:
        return {}
    
    naics_clean = naics.split('(')[0].strip()
    
    if naics_clean in mapping:
        return mapping[naics_clean]
    
    for code, equip in mapping.items():
        if naics_clean.startswith(code[:4]):
            return equip
    
    return {}


def enrich_lead(lead_id: str, pattern: str = "standard", agent_data: Optional[Dict] = None, principals_data: Optional[List[Dict]] = None, filing_history: Optional[List[Dict]] = None) -> Dict:
    """Enrich a lead in-place. Returns the updated lead dict.
    
    Pattern A (standard):
    - Domain enrichment (website check, phone scrape)
    - Brave Search (description, phone, county)
    - Competitor check (if website found)
    - Equipment context (NAICS lookup)
    - Agent/principal data (from CT SoS datasets)
    - Filing history signals (for historical/high-score leads)
    
    Updates timestamped fields:
    - website_last_checked
    - brave_last_searched
    
    Args:
        lead_id: Lead ID to enrich
        pattern: Enrichment pattern (only "standard" supported in baseline 1.0)
        agent_data: Pre-fetched agent record (from bulk fetch)
        principals_data: Pre-fetched principal records (from bulk fetch)
        filing_history: Pre-fetched filing history records (from bulk fetch)
    
    Returns:
        Updated lead dict
    """
    from datetime import datetime
    
    lead = get_lead(lead_id)
    if not lead:
        raise KeyError(f"Lead {lead_id} not found")
    
    email = lead.get("email", "")
    now = datetime.utcnow().isoformat() + "Z"

    # Filing-service email detection (second line of defense)
    if email and "@" in email and not lead.get("email_is_filing_service"):
        email_domain = email.split("@")[-1].lower().strip()
        if email_domain in FILING_SERVICE_DOMAINS:
            lead["email_is_filing_service"] = True

    domain = extract_domain_from_email(email)
    
    if domain:
        lead["domain"] = domain
        
        exists, url = check_website_exists(domain, timeout=3)
        lead["website_exists"] = exists
        lead["website_url"] = url
        lead["website_last_checked"] = now
        
        if exists and url:
            phones = scrape_phones_from_website(url, timeout=3)
            if phones:
                lead["domain_phone"] = phones[0]
            
            comp = competitor_check(domain, timeout=3)
            lead["competitor_displacement"] = comp.get("displacement", False)
            lead["competitor_brands"] = comp.get("brands_found", [])
            lead["competitor_summary"] = comp.get("summary", "")
    
    if lead.get("name", "").strip():
        brave_result = brave_enrich_lead(lead)
        if brave_result:
            lead["brave_phone"] = brave_result.get("brave_phone", "")
            lead["brave_summary"] = brave_result.get("brave_summary", "")
            lead["brave_descriptions"] = brave_result.get("brave_descriptions", [])
            lead["brave_website"] = brave_result.get("brave_website", "")
            lead["county"] = brave_result.get("county", "")
            lead["brave_last_searched"] = now

    # Phase 3.5: Prospeo contact enrichment (after Brave so brave_website is available)
    prospeo_key = os.environ.get("PROSPEO_API_KEY")
    if prospeo_key and (lead.get("principal_name") or lead.get("primary_principal_name")) and not lead.get("email_verified"):
        from capabilities.prospeo import enrich_via_prospeo
        result = enrich_via_prospeo(lead, prospeo_key)
        if result.get("prospeo_hit"):
            lead["email"] = result["prospeo_email"]
            lead["email_verified"] = True
            lead["email_source"] = "prospeo"
            if result.get("prospeo_first_name"):
                lead["greeting_name"] = result["prospeo_first_name"]
                lead["greeting_name_source"] = "prospeo"
        lead["prospeo_result"] = result
    
    naics = lead.get("naics", "")
    equipment = get_equipment_for_naics(naics)
    if equipment:
        lead["equipment"] = equipment
    
    # Agent/principal data from CT SoS datasets
    if agent_data:
        lead["agent_name"] = agent_data.get("name", lead.get("agent_name", ""))
        lead["agent_type"] = agent_data.get("type", "")
        lead["agent_is_self"] = agent_data.get("type") == "individual"
    elif not lead.get("agent_name") and lead.get("business_id"):
        from integrations.ctsos import fetch_agent_for_business
        agent = fetch_agent_for_business(lead["business_id"])
        if agent:
            lead["agent_name"] = agent["name"]
            lead["agent_type"] = agent["type"]
            lead["agent_is_self"] = agent["type"] == "individual"
    
    if principals_data:
        lead["principals"] = principals_data
        lead["principal_names"] = [p["name"] for p in principals_data if p.get("name")]
        if principals_data:
            # If multiple principals, prefer one whose name matches the email prefix
            if len(principals_data) > 1:
                match = _find_principal_matching_email(lead, principals_data)
                primary = match if match else principals_data[0]
            else:
                primary = principals_data[0]
            _set_principal_greeting(lead, primary)
    elif not lead.get("principals") and lead.get("business_id"):
        from integrations.ctsos import fetch_principals_for_business
        principals = fetch_principals_for_business(lead["business_id"])
        if principals:
            lead["principals"] = principals
            lead["principal_names"] = [p["name"] for p in principals if p.get("name")]
            if len(principals) > 1:
                match = _find_principal_matching_email(lead, principals)
                primary = match if match else principals[0]
            else:
                primary = principals[0]
            _set_principal_greeting(lead, primary)
    
    # Filing history signals (only for historical leads or high-score leads)
    if (lead.get("temporal_category") == "historical" or lead.get("score", 0) >= 70):
        if not lead.get("last_filing_date") and lead.get("business_id"):
            if filing_history:
                history = filing_history
            else:
                from integrations.ctsos import fetch_filing_history
                history = fetch_filing_history(lead["business_id"])
            
            if history:
                most_recent = history[0]
                lead["last_filing_date"] = most_recent.get("filing_date", "")
                lead["last_filing_type"] = most_recent.get("filing_type", "")
                lead["filing_count"] = len(history)
                lead["has_recent_amendment"] = any(
                    "amendment" in (f.get("filing_type", "").lower())
                    and _is_within_days(f.get("filing_date"), 90)
                    for f in history
                )
                lead["uses_paper_filing"] = most_recent.get("physical_filing", False) == True
                current_year = str(datetime.now().year)
                prev_year = str(datetime.now().year - 1)
                lead["annual_report_current"] = any(
                    "annual report" in f.get("filing_type", "").lower()
                    and f.get("report_year", "") in [current_year, prev_year]
                    for f in history
                )
    
    update_fields = {
        "domain": lead.get("domain"),
        "website_exists": lead.get("website_exists"),
        "website_url": lead.get("website_url"),
        "website_last_checked": lead.get("website_last_checked"),
        "domain_phone": lead.get("domain_phone"),
        "competitor_displacement": lead.get("competitor_displacement"),
        "competitor_brands": lead.get("competitor_brands"),
        "competitor_summary": lead.get("competitor_summary"),
        "brave_phone": lead.get("brave_phone"),
        "brave_summary": lead.get("brave_summary"),
        "brave_descriptions": lead.get("brave_descriptions"),
        "brave_website": lead.get("brave_website"),
        "county": lead.get("county"),
        "brave_last_searched": lead.get("brave_last_searched"),
        "equipment": lead.get("equipment"),
        "prospeo_result": lead.get("prospeo_result"),
        "email_verified": lead.get("email_verified"),
        "email_source": lead.get("email_source"),
        "email_is_filing_service": lead.get("email_is_filing_service"),
        "agent_name": lead.get("agent_name"),
        "agent_type": lead.get("agent_type"),
        "agent_is_self": lead.get("agent_is_self"),
        "principals": lead.get("principals"),
        "principal_names": lead.get("principal_names"),
        "primary_principal_name": lead.get("primary_principal_name"),
        "principal_designation": lead.get("principal_designation"),
        "last_filing_date": lead.get("last_filing_date"),
        "last_filing_type": lead.get("last_filing_type"),
        "filing_count": lead.get("filing_count"),
        "has_recent_amendment": lead.get("has_recent_amendment"),
        "uses_paper_filing": lead.get("uses_paper_filing"),
        "annual_report_current": lead.get("annual_report_current"),
        "last_updated": now,
    }
    
    update_lead(lead_id, update_fields)

    # Enrichment status tracking
    lead["enrichment_status"] = "enriched"
    lead["enriched_at"] = now
    lead["enrichment_phases"] = {
        "domain": bool(lead.get("domain")),
        "prospeo": bool(lead.get("prospeo_result", {}).get("prospeo_hit")),
        "brave": bool(lead.get("brave_last_searched")),
        "competitor": bool(lead.get("competitor_summary")),
        "equipment": bool((lead.get("equipment") or {}).get("talk_track")),
        "agent_principal": bool(lead.get("agent_name") or lead.get("principals")),
        "filing_history": bool(lead.get("filing_count")),
    }
    update_lead(lead_id, {
        "enrichment_status": "enriched",
        "enriched_at": now,
        "enrichment_phases": lead["enrichment_phases"],
    })
    
    return lead


def _find_principal_matching_email(lead: dict, principals: list) -> Optional[dict]:
    """Find a principal whose normalized first name matches the email prefix.

    When there are multiple principals, check if any principal's first name
    matches the local part of the email address. This prevents greeting a
    lead as a different person than who the email is addressed to.
    """
    email = lead.get("email", "")
    if not email or "@" not in email:
        return None

    prefix = email.split("@")[0].lower()
    email_first = prefix.split(".")[0].strip("0123456789_-")

    if len(email_first) < 3:
        return None

    for p in principals:
        name = p.get("name", "")
        first = p.get("first_name", "") or p.get("firstname", "")

        if name:
            name_first = _normalize_greeting_name(name)
            if name_first and name_first.lower() == email_first:
                return p

        if first:
            candidate = first.strip().title()
            if candidate and candidate.lower() == email_first:
                return p

    return None


def _set_principal_greeting(lead: dict, principal: dict) -> None:
    """Set primary_principal_name from a principal record.

    Principals dataset has no type/designation field — all entries are
    individuals (natural persons). Greeting is derived from full name.
    """
    _type = (principal.get("type") or "").lower()
    is_individual = _type in ("", "individual", "person")

    if not is_individual:
        return

    # Use first_name+last_name if available, else fall back to name__c
    name = principal.get("name", "")
    first = principal.get("first_name", "") or principal.get("firstname", "")
    last = principal.get("last_name", "") or principal.get("lastname", "")
    full_name = name
    if not full_name and (first or last):
        full_name = f"{first} {last}".strip()

    if not full_name:
        return

    normalized = _normalize_greeting_name(full_name)
    if normalized:
        lead["primary_principal_name"] = normalized
        lead["principal_designation"] = principal.get("designation", "")


def _is_within_days(date_str: str, days: int) -> bool:
    """Check if a date string is within N days of today."""
    if not date_str:
        return False
    try:
        filing_date = datetime.fromisoformat(date_str.replace('Z', '+00:00'))
        days_diff = (datetime.utcnow() - filing_date).days
        return abs(days_diff) <= days
    except Exception:
        return False


def enrich_batch(lead_ids: List[str], pattern: str = "standard") -> Dict:
    """Enrich multiple leads. Returns counts and any errors.
    
    Uses bulk fetch for agent/principal/filing-history data to minimize API calls.
    
    Args:
        lead_ids: List of lead IDs to enrich
        pattern: Enrichment pattern
    
    Returns:
        Dict with:
        - enriched_count: int
        - error_count: int
        - errors: list of error messages
    """
    from state.backlog import get_lead
    from integrations.ctsos import fetch_filing_history_bulk
    
    result = {
        "enriched_count": 0,
        "error_count": 0,
        "errors": [],
    }
    
    # Bulk fetch agent and principal data
    business_ids = []
    filing_history_ids = []
    for lead_id in lead_ids:
        try:
            lead = get_lead(lead_id)
            if lead and lead.get("business_id"):
                business_ids.append(lead["business_id"])
                # Filing history only for historical or high-score leads
                if lead.get("temporal_category") == "historical" or lead.get("score", 0) >= 70:
                    filing_history_ids.append(lead["business_id"])
        except Exception:
            pass
    
    agent_map = fetch_agents_bulk(business_ids) if business_ids else {}
    principals_map = fetch_principals_bulk(business_ids) if business_ids else {}
    filing_history_map = fetch_filing_history_bulk(filing_history_ids) if filing_history_ids else {}
    
    # Build lead_id -> business_id mapping
    lead_to_business = {}
    for lead_id in lead_ids:
        try:
            lead = get_lead(lead_id)
            if lead and lead.get("business_id"):
                lead_to_business[lead_id] = lead["business_id"]
        except Exception:
            pass
    
    for lead_id in lead_ids:
        try:
            business_id = lead_to_business.get(lead_id, "")
            agent_data = agent_map.get(business_id) if business_id else None
            principals_data = principals_map.get(business_id, []) if business_id else None
            filing_history = filing_history_map.get(business_id, []) if business_id else None
            
            enrich_lead(lead_id, pattern, agent_data=agent_data, principals_data=principals_data, filing_history=filing_history)
            result["enriched_count"] += 1
        except Exception as e:
            result["error_count"] += 1
            result["errors"].append(f"{lead_id}: {str(e)}")
    
    return result