"""Competitive Intelligence — read-only queries against enriched backlog.

Produces reports on agent displacement rates, principal overlap, address
clustering, and win-rate tracking. Queries 2 and 3 write flags back to
leads when overlap is found.

Commands:
  absel intel agents [--min-filings N]
  absel intel principals [--competitor-only]
  absel intel addresses [--min-businesses N]
  absel intel wins
"""

import re
from collections import Counter, defaultdict
from typing import Optional

from state.backlog import load_backlog, update_lead


def _normalize_address(address: str) -> str:
    """Normalize address for clustering comparison.
    
    Lowercase, strip punctuation, expand common abbreviations.
    """
    if not address:
        return ""
    
    s = address.lower().strip()
    s = re.sub(r'[^\w\s]', '', s)
    
    abbreviations = {
        r'\bst\b': 'street',
        r'\bave\b': 'avenue',
        r'\bste\b': 'suite',
        r'\bblvd\b': 'boulevard',
        r'\bdr\b': 'drive',
        r'\brd\b': 'road',
        r'\bln\b': 'lane',
        r'\bct\b': 'court',
        r'\bpl\b': 'place',
        r'\bapt\b': 'apartment',
        r'\bunit\b': 'unit',
        r'\bfl\b': 'floor',
        r'\bn\b': 'north',
        r'\bs\b': 'south',
        r'\be\b': 'east',
        r'\bw\b': 'west',
        r'\bne\b': 'northeast',
        r'\bnw\b': 'northwest',
        r'\bse\b': 'southeast',
        r'\bsw\b': 'southwest',
    }
    
    for pattern, replacement in abbreviations.items():
        s = re.sub(pattern, replacement, s)
    
    s = re.sub(r'\s+', ' ', s).strip()
    
    return s


def _get_competitor_brands(lead: dict) -> list:
    """Get competitor brands from a lead."""
    brands = lead.get("competitor_brands", [])
    if isinstance(brands, list):
        return brands
    return []


def _extract_naics_category(naics: str) -> str:
    """Extract a human-readable category from NAICS code/description."""
    if not naics:
        return "Unknown"
    
    naics_clean = naics.split('(')[0].strip() if '(' in naics else naics.strip()
    
    category_map = {
        "Dentist": "Dental",
        "Dentists": "Dental",
        "Physician": "Medical",
        "Physicians": "Medical",
        "Medical": "Medical",
        "Health": "Medical",
        "Legal": "Legal",
        "Law": "Legal",
        "Attorney": "Legal",
        "Accounting": "Accounting",
        "CPA": "Accounting",
        "Real Estate": "Real Estate",
        "Construction": "Construction",
        "Restaurant": "Food Service",
        "Retail": "Retail",
    }
    
    for keyword, category in category_map.items():
        if keyword.lower() in naics_clean.lower():
            return category
    
    if len(naics_clean) > 40:
        naics_clean = naics_clean[:40] + "..."
    
    return naics_clean or "Unknown"


def agent_displacement_report(min_filings: int = 10) -> str:
    """Generate agent competitive intelligence report.
    
    Groups leads by agent_name, calculates displacement rates,
    and identifies high-displacement agents.
    
    Args:
        min_filings: Minimum filings for an agent to appear in report
    
    Returns:
        Formatted report string
    """
    backlog = load_backlog()
    
    active_leads = [
        l for l in backlog
        if l.get("lead_status") == "active"
        and l.get("agent_name")
    ]
    
    agent_groups = defaultdict(list)
    for lead in active_leads:
        agent_groups[lead["agent_name"]].append(lead)
    
    total_leads = len(active_leads)
    total_displaced = sum(1 for l in active_leads if l.get("competitor_displacement"))
    baseline_rate = (total_displaced / total_leads * 100) if total_leads > 0 else 0
    
    agent_stats = []
    for agent_name, leads in agent_groups.items():
        n_filings = len(leads)
        if n_filings < min_filings:
            continue
        
        n_displaced = sum(1 for l in leads if l.get("competitor_displacement"))
        displacement_rate = (n_displaced / n_filings * 100) if n_filings > 0 else 0
        
        all_brands = []
        for l in leads:
            all_brands.extend(_get_competitor_brands(l))
        brand_counts = Counter(all_brands)
        
        naics_categories = [_extract_naics_category(l.get("naics", "")) for l in leads]
        naics_counts = Counter(naics_categories)
        
        cities = [l.get("city", "") for l in leads if l.get("city")]
        city_counts = Counter(cities)
        
        agent_stats.append({
            "name": agent_name,
            "filings": n_filings,
            "displaced": n_displaced,
            "displacement_rate": displacement_rate,
            "brands": brand_counts,
            "naics": naics_counts,
            "cities": city_counts,
        })
    
    agent_stats.sort(key=lambda x: -x["filings"])
    
    lines = []
    lines.append(f"=== Agent Competitive Intelligence (agents with {min_filings}+ filings) ===")
    lines.append(f"Baseline displacement rate: {baseline_rate:.0f}%")
    lines.append("")
    
    for i, stats in enumerate(agent_stats, 1):
        name = stats["name"]
        filings = stats["filings"]
        rate = stats["displacement_rate"]
        displaced = stats["displaced"]
        
        flag = ""
        if baseline_rate > 0 and rate > baseline_rate * 1.5:
            flag = " HIGH"
        
        lines.append(f" #{i:2d}  {name:40s}  {filings} filings")
        lines.append(f"     Displacement: {rate:.0f}% ({displaced}/{filings}) vs {baseline_rate:.0f}% baseline{flag}")
        
        if stats["brands"]:
            top_brands = stats["brands"].most_common(5)
            brand_str = ", ".join(f"{b} ({c})" for b, c in top_brands)
            lines.append(f"     Brands: {brand_str}")
        
        if stats["naics"]:
            top_naics = stats["naics"].most_common(3)
            naics_str = ", ".join(f"{cat} ({pct:.0f}%)" for cat, pct in [
                (c, cnt / filings * 100) for c, cnt in top_naics
            ])
            lines.append(f"     Industries: {naics_str}")
        
        if stats["cities"]:
            top_cities = stats["cities"].most_common(3)
            city_str = ", ".join(f"{city} ({cnt})" for city, cnt in top_cities)
            lines.append(f"     Cities: {city_str}")
        
        lines.append("")
    
    if not agent_stats:
        lines.append("No agents with enough filings to report.")
    
    return "\n".join(lines)


def principal_overlap_report(competitor_only: bool = False) -> str:
    """Generate principal overlap report.
    
    For leads with competitor_displacement, looks up their principals
    and cross-references against all other leads.
    
    When overlap is found, updates the linked leads with flags.
    
    Args:
        competitor_only: If True, only show principals from competitor-flagged leads
    
    Returns:
        Formatted report string
    """
    backlog = load_backlog()
    
    active_leads = [l for l in backlog if l.get("lead_status") == "active"]
    
    competitor_leads = [l for l in active_leads if l.get("competitor_displacement")]
    
    principal_to_leads = defaultdict(list)
    for lead in active_leads:
        for pname in lead.get("principal_names", []):
            if pname:
                principal_to_leads[pname.lower()].append(lead)
    
    overlaps = {}
    for lead in competitor_leads:
        for pname in lead.get("principal_names", []):
            if not pname:
                continue
            key = pname.lower()
            if key not in overlaps:
                overlaps[key] = {
                    "name": pname,
                    "competitor_leads": [],
                    "other_leads": [],
                }
            overlaps[key]["competitor_leads"].append(lead)
    
    for key in overlaps:
        all_leads = principal_to_leads.get(key, [])
        for lead in all_leads:
            if lead.get("competitor_displacement"):
                continue
            if lead not in overlaps[key]["competitor_leads"]:
                overlaps[key]["other_leads"].append(lead)
    
    overlaps = {k: v for k, v in overlaps.items() if v["other_leads"]}
    
    if not overlaps:
        return "=== Principal Overlap with Competitor-Flagged Businesses ===\n\nNo overlaps found."
    
    lines = []
    lines.append("=== Principal Overlap with Competitor-Flagged Businesses ===")
    lines.append("(Updates principal_competitor_overlap flags on linked leads)")
    lines.append("")
    
    flagged_count = 0
    for key, data in sorted(overlaps.items()):
        name = data["name"]
        
        lines.append(name)
        
        all_linked = data["competitor_leads"] + data["other_leads"]
        for i, lead in enumerate(all_linked):
            is_competitor = lead.get("competitor_displacement", False)
            filed = lead.get("filing_date", "")[:10]
            city = lead.get("city", "")
            competitor_brand = ""
            if is_competitor:
                brands = _get_competitor_brands(lead)
                competitor_brand = f" -- COMPETITOR: {', '.join(brands[:2])}" if brands else " -- COMPETITOR"
            
            marker = " -- COMPETITOR" if is_competitor else ""
            lines.append(f"  |- {lead['name']} (filed {filed}, {city}){competitor_brand}")
        
        if data["other_leads"]:
            n_others = len(data["other_leads"])
            lines.append(f"      -> Flag: principal has competitor equipment at another entity ({n_others} linked)")
            
            for lead in data["other_leads"]:
                if not lead.get("principal_competitor_overlap"):
                    competitor_names = [l["name"] for l in data["competitor_leads"][:2]]
                    detail = f"{name} also principal of {', '.join(competitor_names)} (competitor equipment)"
                    update_lead(lead["id"], {
                        "principal_competitor_overlap": True,
                        "principal_competitor_detail": detail,
                    })
                    flagged_count += 1
        
        lines.append("")
    
    lines.append(f"Flagged {flagged_count} leads with principal_competitor_overlap.")
    
    return "\n".join(lines)


def address_clustering_report(min_businesses: int = 2) -> str:
    """Generate address clustering report.
    
    Groups leads by normalized street address. For addresses with 2+
    businesses, checks if ANY has a competitor flag and flags the others.
    
    Args:
        min_businesses: Minimum businesses at an address to report
    
    Returns:
        Formatted report string
    """
    backlog = load_backlog()
    
    active_leads = [l for l in backlog if l.get("lead_status") == "active"]
    
    address_groups = defaultdict(list)
    for lead in active_leads:
        street = lead.get("street_address", "")
        city = lead.get("city", "")
        state = lead.get("state", "CT")
        
        full_address = f"{street}, {city} {state}".strip(", ")
        normalized = _normalize_address(full_address)
        
        if normalized:
            address_groups[normalized].append({
                "lead": lead,
                "display_address": full_address,
            })
    
    clusters = []
    for normalized, entries in address_groups.items():
        if len(entries) < min_businesses:
            continue
        
        has_competitor = any(e["lead"].get("competitor_displacement") for e in entries)
        if has_competitor:
            clusters.append(entries)
    
    if not clusters:
        return "=== Address Clusters with Competitor Presence ===\n\nNo multi-business addresses with competitor presence found."
    
    lines = []
    lines.append("=== Address Clusters with Competitor Presence ===")
    lines.append("(Updates address_competitor_overlap flags on non-competitor businesses)")
    lines.append("")
    
    flagged_count = 0
    for cluster in clusters:
        display_addr = cluster[0]["display_address"]
        lines.append(display_addr)
        
        for entry in cluster:
            lead = entry["lead"]
            is_competitor = lead.get("competitor_displacement", False)
            competitor_brand = ""
            if is_competitor:
                brands = _get_competitor_brands(lead)
                competitor_brand = f" -- COMPETITOR: {', '.join(brands[:2])}" if brands else " -- COMPETITOR"
            
            lines.append(f"  |- {lead['name']}{competitor_brand}")
        
        non_competitors = [e for e in cluster if not e["lead"].get("competitor_displacement")]
        if non_competitors:
            competitor_names = [e["lead"]["name"] for e in cluster if e["lead"].get("competitor_displacement")][:2]
            for entry in non_competitors:
                lead = entry["lead"]
                if not lead.get("address_competitor_overlap"):
                    detail = f"Shares address with {', '.join(competitor_names)} (competitor equipment)"
                    update_lead(lead["id"], {
                        "address_competitor_overlap": True,
                        "address_competitor_detail": detail,
                    })
                    flagged_count += 1
            
            lines.append(f"      -> Flagged {len(non_competitors)} non-competitor businesses at this address")
        
        lines.append("")
    
    lines.append(f"Flagged {flagged_count} leads with address_competitor_overlap.")
    
    return "\n".join(lines)


def win_rate_report() -> str:
    """Stub for win-rate tracking.
    
    Future: when Zoho integration tracks deal outcomes, show conversion
    rates by agent.
    
    Returns:
        Stub message string
    """
    return "Win-rate tracking requires Zoho deal outcome integration. Not yet available."
