"""Research capability — per-lead website + Google Business research.

Generates a structured research note from a business's website content
and Google Business Profile data (via Brave Search). The note surfaces
specific, concrete details the LLM drafter can use to write bespoke
outreach emails.

Research is gated: the lead must have a live website or substantive
Brave data. Leads without a web presence are skipped gracefully.
"""

import json
import re
from datetime import datetime
from typing import Dict, List, Optional

from integrations.domain import fetch_website_content
from integrations.llm import call_llm
from integrations.brave import brave_search
from state.backlog import get_lead, update_lead


RESEARCH_PROMPT = """You are researching a business for a sales representative who sells office equipment (copiers, printers, multifunction devices).

Extract 3-5 SPECIFIC, CONCRETE details about this business that a salesperson could reference in a personalized cold email. Focus on details that:

1. Make this business DISTINCT from others in the same category (not "they're a dental practice" — that's the NAICS code)
2. Suggest something about their DOCUMENT or PRINT NEEDS (team size, multiple locations, specific services that generate paperwork, patient/client volume indicators)
3. Indicate GROWTH or CHANGE (hiring, new location, new services, expansion, awards, recent news on their site)
4. Could be referenced naturally in one sentence of an email without sounding like you're reading from a dossier

DO NOT extract:
- Address, phone number, hours (we already have these)
- Generic descriptions that match any business in the category
- Mission statements or marketing fluff
- Anything that would be creepy to reference in a cold email (personal details about the owner, family info, etc.)

Also suggest ONE angle for the salesperson — the single most relevant connection between what you found and why this business might need better print/scan equipment.

Respond in this exact JSON format:

{{
  "findings": ["Detail 1", "Detail 2", "Detail 3"],
  "suggested_angle": "One sentence connecting findings to equipment need",
  "team_size": null,
  "locations": null,
  "growth_signals": false
}}

team_size: number or null, from content clues (staff page, "we have X therapists", etc.)
locations: number or null, from content clues (multiple offices, serving X communities, etc.)
growth_signals: true if site shows hiring, expansion, new services, awards, etc.

BUSINESS CONTEXT:
{context}

WEBSITE CONTENT:
{website_content}

GOOGLE BUSINESS:
{gb_data}

Respond ONLY with valid JSON. No preamble, no markdown, no code fences."""


def _gate_check(lead: dict) -> tuple:
    """Check if a lead passes research gates.

    Gate 1: Real business (not burned/shell/paused, domestic, has NAICS)
    Gate 3: Has website or substantive Brave data to research

    Returns (passed, reason).
    """
    status = lead.get("lead_status", "")
    if status in ("burned", "paused"):
        return (False, f"Lead is {status}")

    if lead.get("is_shell"):
        return (False, "Shell business")

    citizenship = (lead.get("citizenship") or "").lower()
    if citizenship and citizenship not in ("domestic", "us", "ct", "united states", "connecticut"):
        return (False, f"Non-domestic citizenship: {citizenship}")

    if not lead.get("naics"):
        return (False, "No NAICS code")

    # Gate 3: has something to research
    has_website = lead.get("website_exists") is True
    brave_summary = lead.get("brave_summary") or ""
    has_brave = len(brave_summary.strip()) > 30

    if not has_website and not has_brave:
        return (False, "No website and insufficient Brave data")

    return (True, "")


def _fetch_google_business(lead: dict) -> dict:
    """Search Brave for Google Business Profile data.

    One Brave API call. Extracts rating, review count, top review snippets,
    and business category from search results.

    Falls back to empty dict if no Google Business card found in results.
    """
    name = (lead.get("name") or "").strip()
    city = (lead.get("city") or "CT").strip()

    if not name:
        return {}

    query = f"{name} {city} CT"
    try:
        results = brave_search(query, max_results=5)
    except Exception:
        return {}

    if not results:
        return {}

    gb = {"rating": None, "review_count": None, "snippets": [], "category": None}

    for r in results:
        desc = (r.get("description") or "").lower()
        title = (r.get("title") or "").lower()
        url = (r.get("url") or "").lower()

        # Look for Google Business / Maps results
        is_gb = (
            "google.com/maps" in url
            or "google" in title and "rating" in desc
            or "reviews" in desc and "stars" in desc
        )

        if is_gb:
            # Extract rating: "4.8" pattern near "star" or "rating"
            m = re.search(r"(\d+\.?\d*)\s*(?:stars?|rating)", desc)
            if m and gb["rating"] is None:
                gb["rating"] = m.group(1)

            # Extract review count: "42 reviews" pattern
            m = re.search(r"(\d+)\s*reviews?", desc)
            if m and gb["review_count"] is None:
                gb["review_count"] = int(m.group(1))

            # Extract snippets from description
            snippets = [s.strip() for s in re.split(r"[.;]", desc) if len(s.strip()) > 10]
            if not gb["snippets"]:
                gb["snippets"] = snippets[:3]

    return gb


def _build_research_prompt(
    lead: dict, website_content: str, google_business: dict
) -> str:
    """Build the LLM research extraction prompt with lead context."""
    naics_raw = lead.get("naics", "") or ""
    # Extract description from NAICS label
    naics_label = naics_raw
    if "(" in naics_raw:
        naics_label = naics_raw.split("(")[0].strip()

    filing_date = lead.get("filing_date", "")
    age_months = lead.get("business_age_months", "")

    context_lines = [
        f"Name: {lead.get('name', '')}",
        f"Industry: {naics_label}",
        f"City: {lead.get('city', '')}, {lead.get('state', 'CT')}",
    ]
    if filing_date:
        context_lines.append(f"Filed: {filing_date}")
    if age_months:
        context_lines.append(f"Age: {age_months} months")

    context = "\n".join(context_lines)

    # Format Google Business data
    gb_parts = []
    if google_business.get("rating"):
        gb_parts.append(f"Rating: {google_business['rating']}")
    if google_business.get("review_count"):
        gb_parts.append(f"Reviews: {google_business['review_count']}")
    if google_business.get("snippets"):
        gb_parts.append(f"Snippets: {'; '.join(google_business['snippets'][:3])}")

    gb_data = "\n".join(gb_parts) if gb_parts else "No Google Business data found."

    website_text = website_content or "(No website content available)"

    return RESEARCH_PROMPT.format(
        context=context,
        website_content=website_text,
        gb_data=gb_data,
    )


def _parse_research_response(response_text: str) -> dict:
    """Parse LLM response as JSON. Falls back to empty dict on failure."""
    text = response_text.strip()

    # Strip markdown code fences if present
    if text.startswith("```"):
        text = re.sub(r"^```(?:json)?\s*", "", text)
        text = re.sub(r"\s*```$", "", text)

    try:
        return json.loads(text)
    except json.JSONDecodeError:
        pass

    # Try to extract JSON from within text
    m = re.search(r"\{.*\}", text, re.DOTALL)
    if m:
        try:
            return json.loads(m.group(0))
        except json.JSONDecodeError:
            pass

    return {}


def _extract_research_note(
    lead: dict, website_content: str, google_business: dict
) -> dict:
    """Send fetched content to LLM for structured extraction.

    Retries once on JSON parse failure with explicit instruction.
    Returns research note dict with findings, suggested_angle, etc.
    """
    prompt = _build_research_prompt(lead, website_content, google_business)

    # First attempt
    try:
        response, _metadata = call_llm(prompt)
    except Exception:
        return {
            "findings": [],
            "suggested_angle": "",
            "team_size": None,
            "locations": None,
            "growth_signals": False,
            "_error": "LLM call failed",
        }

    parsed = _parse_research_response(response)

    if parsed.get("findings") and isinstance(parsed.get("findings"), list):
        return parsed

    # Retry with explicit instruction
    retry_prompt = prompt + "\n\nIMPORTANT: Your previous response was not valid JSON. Respond ONLY with a JSON object. No preamble, no markdown, no explanation. Just the JSON."

    try:
        response2, _ = call_llm(retry_prompt)
    except Exception:
        return {
            "findings": [],
            "suggested_angle": "",
            "team_size": None,
            "locations": None,
            "growth_signals": False,
            "_error": "LLM retry failed",
        }

    parsed2 = _parse_research_response(response2)

    if parsed2.get("findings") and isinstance(parsed2.get("findings"), list):
        return parsed2

    return {
        "findings": [],
        "suggested_angle": "",
        "team_size": None,
        "locations": None,
        "growth_signals": False,
        "_error": "Could not parse LLM response as JSON",
    }


def _format_research_for_zoho(lead: dict, note: dict) -> str:
    """Format research note as plain text for Zoho textarea field.

    No HTML. Uses caps for section headers, dashes for bullets, blank
    lines between sections. Readable during a phone call.
    """
    name = lead.get("name", "Unknown")
    researched_at_raw = note.get("researched_at", "Unknown")
    # Truncate to date portion
    researched_at = researched_at_raw[:10] if len(str(researched_at_raw)) > 10 else str(researched_at_raw)
    sources = note.get("sources", [])

    lines = [
        f"RESEARCH NOTE -- {name}",
        f"Researched: {researched_at}",
        f"Sources: {', '.join(sources) if sources else 'None'}",
        "",
        "FINDINGS:",
    ]

    for finding in note.get("findings", []) or []:
        lines.append(f"- {finding}")

    lines.append("")
    lines.append("SUGGESTED ANGLE:")
    lines.append(note.get("suggested_angle", "Not available"))
    lines.append("")
    lines.append("SIGNALS:")

    team = note.get("team_size")
    locs = note.get("locations")
    growth = note.get("growth_signals")

    lines.append(f"Team size: {team if team is not None else 'unknown'}")
    lines.append(f"Locations: {locs if locs is not None else 'unknown'}")
    lines.append(f"Growth signals: {'Yes' if growth else 'No'}")

    return "\n".join(lines)


def _push_research_to_zoho(lead: dict) -> dict:
    """Push research note to Zoho Lead record's Research_Note field.

    Only pushes if lead has a zoho_id (already pushed to Zoho).
    """
    from shared.zoho.client import api as _zoho_api

    zoho_id = lead.get("zoho_id")
    if not zoho_id:
        return {"status": "skipped", "error": "Lead not pushed to Zoho yet"}

    note = lead.get("research_note")
    if not note:
        return {"status": "skipped", "error": "No research note on lead"}

    html = _format_research_for_zoho(lead, note)

    try:
        _zoho_api("PUT", f"Leads/{zoho_id}", payload={
            "data": [{"LLMResearch_Note": html}]
        })
        return {"status": "updated"}
    except Exception as e:
        return {"status": "error", "error": str(e)}


def research_lead(lead_id: str, force: bool = False) -> dict:
    """Generate a research note for a lead.

    Pipeline:
    1. Gate check (real business, has website or Brave data)
    2. Fetch website content (if website_exists)
    3. Fetch Google Business data via Brave (1 API call)
    4. Extract research note via LLM (1 API call)
    5. Store on lead
    6. Push to Zoho Research_Note field (if lead has zoho_id)

    Args:
        lead_id: Lead UUID
        force: If True, re-research even if existing note exists

    Returns:
        Dict with status, note, error details

    Raises:
        KeyError if lead not found
        ValueError if gate check fails
    """
    lead = get_lead(lead_id)
    if not lead:
        raise KeyError(f"Lead {lead_id} not found")

    if not force and lead.get("has_research"):
        raise ValueError("Already researched. Use --force to re-research.")

    passed, reason = _gate_check(lead)
    if not passed:
        raise ValueError(f"Gate check failed: {reason}")

    now = datetime.utcnow().isoformat() + "Z"

    # Step 1: Fetch website content
    website_content = ""
    pages_fetched = []
    fetch_errors = []

    if lead.get("website_exists") and lead.get("website_url"):
        wc = fetch_website_content(lead["website_url"])
        website_content = wc.get("content", "")
        pages_fetched = wc.get("pages_fetched", [])
        fetch_errors = wc.get("fetch_errors", [])

    # Step 2: Fetch Google Business via Brave
    google_business = _fetch_google_business(lead)

    # Step 3: Extract research note via LLM
    sources = []
    if website_content:
        sources.append("website")
    if google_business:
        sources.append("google_business")

    extracted = _extract_research_note(lead, website_content, google_business)

    # Step 4: Build and store the full note
    note = {
        "researched_at": now,
        "sources": sources,
        "findings": extracted.get("findings", []),
        "suggested_angle": extracted.get("suggested_angle", ""),
        "team_size": extracted.get("team_size"),
        "locations": extracted.get("locations"),
        "growth_signals": extracted.get("growth_signals", False),
        "pages_fetched": pages_fetched,
    }

    if extracted.get("_error"):
        note["_error"] = extracted["_error"]

    update_lead(lead_id, {
        "research_note": note,
        "has_research": True,
        "researched_at": now,
        "last_updated": now,
    })

    # Step 5: Push to Zoho
    lead_updated = get_lead(lead_id)
    zoho_result = _push_research_to_zoho(lead_updated) if lead_updated.get("zoho_id") else {"status": "skipped", "error": "Not in Zoho"}

    return {
        "status": "researched",
        "lead_id": lead_id,
        "name": lead.get("name", ""),
        "note": note,
        "zoho_push": zoho_result,
        "website_fetch": {
            "pages_fetched": pages_fetched,
            "errors": fetch_errors,
        },
    }


def research_batch(
    lead_ids: List[str], force: bool = False
) -> Dict:
    """Research multiple leads. Sequential — each lead gets its own
    website fetch + LLM call.

    Args:
        lead_ids: List of lead UUIDs
        force: If True, re-research even if existing notes exist

    Returns:
        Dict with researched, skipped, errors, details
    """
    result = {
        "researched": 0,
        "skipped": 0,
        "errors": 0,
        "details": [],
    }

    for lead_id in lead_ids:
        try:
            r = research_lead(lead_id, force=force)
            result["researched"] += 1
            result["details"].append({
                "lead_id": lead_id,
                "name": r.get("name", ""),
                "findings_count": len((r.get("note") or {}).get("findings", [])),
                "zoho_push": (r.get("zoho_push") or {}).get("status"),
            })
        except ValueError as e:
            result["skipped"] += 1
            result["details"].append({
                "lead_id": lead_id,
                "name": "",
                "skipped_reason": str(e),
            })
        except Exception as e:
            result["errors"] += 1
            result["details"].append({
                "lead_id": lead_id,
                "error": str(e),
            })

    return result