"""ICP Generator V2 — rich multi-persona ICP generation.

Produces 2-4 ICPs with pain points, fears, barriers, include/exclude
LinkedIn params, and confidence scores.  Ported from the original
AI in Charge ICP Agent, adapted for HeyLead's lightweight MCP architecture.

Phase 1: Enhanced LLM prompt only (no RAG).
Phase 4 will add RAG pipeline integration when company_context is provided.
"""

from __future__ import annotations

import json
import logging
import time
from typing import Any

from .icp_schemas import (
    HeadcountParam,
    IcpResult,
    LinkedinSearchParam,
    SingleIcp,
    TenureParam,
)
from .llm import LLMClient

logger = logging.getLogger(__name__)

# ──────────────────────────────────────────────
# System prompt
# ──────────────────────────────────────────────

ICP_V2_SYSTEM = """\
You are an expert B2B sales strategist with 20+ years of experience in \
sales, marketing, and go-to-market strategy. You create psychologically \
rich Ideal Customer Profiles (ICPs) that combine LinkedIn targeting \
precision with deep buyer-persona insight.

You output structured JSON only. No markdown, no explanation."""

# ──────────────────────────────────────────────
# User prompt template
# ──────────────────────────────────────────────

ICP_V2_PROMPT = """\
Create 2-4 Ideal Customer Profiles (ICPs) targeting different market \
segments for the following outreach campaign.

## TARGET DESCRIPTION
"{target_description}"

## USER CONTEXT (the sender)
Name: {user_name}
Title: {user_title}
Company: {user_company}
Expertise: {user_expertise}
{company_context_section}
{focus_section}

## TASK
Generate 2-4 distinct ICPs. Each ICP targets a different market segment \
within the target description. For each ICP, provide:

### Identity
- **name**: Short segment name (e.g., "Enterprise Fintech CTOs")
- **description**: 2-3 sentence description of this target segment

### Buyer Psychology
- **pain_points**: 2-4 specific pain points this persona faces
- **fears**: 2-3 underlying fears driving buying decisions
- **barriers**: 2-3 obstacles preventing them from buying

### LinkedIn Search Parameters
Use include/exclude pattern for precise targeting:
- **industries**: {{"include": ["Financial Services", "Fintech"], "exclude": []}}
- **job_titles**: {{"include": ["CTO", "VP Engineering"], "exclude": ["Intern"]}}
- **locations**: {{"include": ["United States"], "exclude": []}}
- **departments**: {{"include": ["Engineering"], "exclude": []}} or null
- **company_headcount**: {{"min": 51, "max": 500}} (valid mins: 1,11,51,201,501,1001,5001,10001; valid maxes: 10,50,200,500,1000,5000,10000)
- **company_types**: list from: self_employed, self_owned, government_agency, public_company, privately_held, non_profit, educational_institution, partnership
- **company_locations**: {{"include": [...], "exclude": []}} or null
- **tenure**: {{"min": 1, "max": 5}} (valid mins: 0,1,3,6,10; valid maxes: 1,2,5,10)
- **seniority**: {{"include": ["cxo", "vp", "director"], "exclude": []}} (valid values: owner/partner, cxo, vice_president, director, experienced_manager, entry_level_manager, strategic, senior, entry_level, in_training)
- **keywords**: list of LinkedIn search keywords

### Confidence
Rate your confidence in this ICP (0.0-1.0):
- **overall_confidence**: How reliable is this ICP overall
- **data_completeness**: How many fields are meaningfully populated
- **evidence_strength**: Quality of reasoning behind the targeting

## RESPONSE FORMAT
Return ONLY valid JSON:
{{
    "summary": "One-line description of the ideal customer",
    "campaign_name": "Short campaign name (3-5 words)",
    "relevance_hook": "Why the sender is credible reaching out to this audience",
    "icps": [
        {{
            "name": "Segment Name",
            "description": "...",
            "pain_points": ["...", "..."],
            "fears": ["...", "..."],
            "barriers": ["...", "..."],
            "industries": {{"include": ["..."], "exclude": []}},
            "job_titles": {{"include": ["..."], "exclude": []}},
            "locations": {{"include": ["..."], "exclude": []}},
            "departments": {{"include": ["..."], "exclude": []}},
            "company_headcount": {{"min": 51, "max": 500}},
            "company_types": ["privately_held"],
            "company_locations": null,
            "tenure": {{"min": 1, "max": 5}},
            "seniority": {{"include": ["cxo", "vp"], "exclude": []}},
            "keywords": ["fintech", "payments"],
            "overall_confidence": 0.75,
            "data_completeness": 0.80,
            "evidence_strength": 0.65
        }}
    ]
}}"""


# ──────────────────────────────────────────────
# Generator
# ──────────────────────────────────────────────

async def generate_icp_v2(
    target_description: str,
    company_context: str = "",
    focus_query: str = "",
    user_profile: dict[str, Any] | None = None,
    confidence_threshold: float = 0.4,
) -> IcpResult:
    """Generate a rich multi-persona ICP.

    Phase 1: Uses enhanced LLM prompt only.
    Phase 4 will add RAG pipeline when company_context is a URL/text.

    Args:
        target_description: Natural language target (e.g., "CTOs at fintech startups")
        company_context: URL or text about the sender's company (Phase 2+)
        focus_query: Optional focus (e.g., "enterprise segment only")
        user_profile: Sender's profile + expertise data
        confidence_threshold: Min confidence to include an ICP (0.0-1.0)

    Returns:
        IcpResult with 2-4 SingleIcp personas.
    """
    start_time = time.time()

    # Route through backend if in backend mode and no local LLM key
    from ..config import has_local_llm_key, is_backend_mode
    if is_backend_mode() and not has_local_llm_key():
        result = await _generate_via_backend(
            target_description, company_context, focus_query, user_profile,
        )
        result.processing_time = time.time() - start_time
        return result

    # Build prompt
    user_name = ""
    user_title = ""
    user_company = ""
    user_expertise = ""

    if user_profile:
        user_name = user_profile.get("name", "")
        user_title = user_profile.get("title", "")
        user_company = user_profile.get("company", "")
        user_expertise = user_profile.get(
            "core", user_profile.get("headline", ""),
        )

    company_context_section = ""
    if company_context:
        company_context_section = (
            f"\n## COMPANY CONTEXT\n{company_context[:3000]}\n"
        )

    focus_section = ""
    if focus_query:
        focus_section = f"\n## FOCUS\n{focus_query}\n"

    prompt = ICP_V2_PROMPT.format(
        target_description=target_description,
        user_name=user_name or "Unknown",
        user_title=user_title or "Founder",
        user_company=user_company or "Unknown",
        user_expertise=user_expertise or "Not specified",
        company_context_section=company_context_section,
        focus_section=focus_section,
    )

    client = LLMClient()
    raw = await client.generate(
        prompt, system=ICP_V2_SYSTEM, temperature=0.4, max_tokens=4000,
    )

    result = _parse_icp_response(raw, target_description)
    result.processing_time = time.time() - start_time

    # Filter below-threshold ICPs
    if confidence_threshold > 0:
        result.icps = [
            icp for icp in result.icps
            if icp.overall_confidence >= confidence_threshold
        ]

    # Ensure at least 1 ICP survives filtering
    if not result.icps:
        logger.warning("All ICPs below confidence threshold, keeping best one")
        all_icps = _parse_icp_response(raw, target_description).icps
        if all_icps:
            best = max(all_icps, key=lambda x: x.overall_confidence)
            result.icps = [best]

    return result


async def _generate_via_backend(
    target_description: str,
    company_context: str,
    focus_query: str,
    user_profile: dict[str, Any] | None,
) -> IcpResult:
    """Generate ICP via the backend proxy.

    The backend returns v2 format (icps[] with pain_points, fears, barriers).
    Falls back to legacy conversion if the response has segments[] instead.
    """
    from ..linkedin import get_linkedin_client

    client = get_linkedin_client()
    try:
        icp_data = await client.generate_icp(
            target_description, user_profile or {},
        )
    finally:
        await client.close()

    # Backend returns v2 format (icps[]) — parse directly
    if "icps" in icp_data:
        return _dict_to_icp_result(icp_data, target_description)

    # Fallback: legacy format (segments[]) — convert
    return _convert_legacy_icp(icp_data, target_description)


# ──────────────────────────────────────────────
# Response parsing
# ──────────────────────────────────────────────

def _parse_icp_response(raw: str, target_description: str) -> IcpResult:
    """Parse LLM JSON response into IcpResult."""
    cleaned = raw.strip()
    if cleaned.startswith("```"):
        lines = cleaned.split("\n")
        cleaned = "\n".join(
            line for line in lines if not line.strip().startswith("```")
        )

    try:
        data = json.loads(cleaned)
    except json.JSONDecodeError:
        logger.warning("Failed to parse ICP V2 JSON, building fallback")
        return _build_fallback(target_description)

    return _dict_to_icp_result(data, target_description)


def _dict_to_icp_result(
    data: dict[str, Any], target_description: str,
) -> IcpResult:
    """Convert a parsed JSON dict into an IcpResult."""
    icps = []
    for icp_data in data.get("icps", []):
        icp = SingleIcp(
            name=icp_data.get("name", ""),
            description=icp_data.get("description", ""),
            pain_points=icp_data.get("pain_points", []),
            fears=icp_data.get("fears", []),
            barriers=icp_data.get("barriers", []),
            industries=_to_search_param(icp_data.get("industries")),
            job_titles=_to_search_param(icp_data.get("job_titles")),
            locations=_to_search_param(icp_data.get("locations")),
            departments=_to_search_param(icp_data.get("departments")),
            company_headcount=HeadcountParam(
                min=_safe_int(icp_data.get("company_headcount", {}).get("min")),
                max=_safe_int(icp_data.get("company_headcount", {}).get("max")),
            ) if isinstance(icp_data.get("company_headcount"), dict) else HeadcountParam(),
            company_types=icp_data.get("company_types", []),
            company_locations=_to_search_param(icp_data.get("company_locations")),
            tenure=TenureParam(
                min=_safe_int(icp_data.get("tenure", {}).get("min")),
                max=_safe_int(icp_data.get("tenure", {}).get("max")),
            ) if isinstance(icp_data.get("tenure"), dict) else TenureParam(),
            seniority=_to_search_param(icp_data.get("seniority")),
            keywords=icp_data.get("keywords", []),
            overall_confidence=float(icp_data.get("overall_confidence", 0.5)),
            data_completeness=float(icp_data.get("data_completeness", 0.5)),
            evidence_strength=float(icp_data.get("evidence_strength", 0.5)),
        )
        icps.append(icp)

    return IcpResult(
        icps=icps,
        summary=data.get("summary", f"Target: {target_description}"),
        campaign_name=data.get("campaign_name", target_description[:30]),
        relevance_hook=data.get("relevance_hook", ""),
    )


def _convert_legacy_icp(
    old_icp: dict[str, Any], target_description: str,
) -> IcpResult:
    """Convert the old-format ICP (from generate_icp v1) to IcpResult.

    Old format has: summary, campaign_name, relevance_hook, segments[]
    where each segment has: name, keywords, titles[], seniority[],
    industries[], company_size[], geography[], estimated_results
    """
    icps = []
    for seg in old_icp.get("segments", []):
        icp = SingleIcp(
            name=seg.get("name", "Primary"),
            description=f"Target: {seg.get('keywords', '')}",
            pain_points=[],
            fears=[],
            barriers=[],
            industries=LinkedinSearchParam(
                include=seg.get("industries", []),
            ),
            job_titles=LinkedinSearchParam(
                include=seg.get("titles", []),
            ),
            locations=LinkedinSearchParam(
                include=seg.get("geography", []),
            ),
            seniority=LinkedinSearchParam(
                include=seg.get("seniority", []),
            ),
            keywords=seg.get("keywords", "").split(",") if isinstance(seg.get("keywords"), str) else [],
            overall_confidence=0.4,
            data_completeness=0.4,
            evidence_strength=0.2,
        )
        icps.append(icp)

    return IcpResult(
        icps=icps,
        summary=old_icp.get("summary", f"Target: {target_description}"),
        campaign_name=old_icp.get("campaign_name", target_description[:30]),
        relevance_hook=old_icp.get("relevance_hook", ""),
    )


def _build_fallback(target_description: str) -> IcpResult:
    """Build a minimal fallback IcpResult when parsing fails."""
    return IcpResult(
        icps=[
            SingleIcp(
                name="Primary",
                description=f"Target: {target_description}",
                keywords=target_description.split()[:5],
                overall_confidence=0.2,
                data_completeness=0.2,
                evidence_strength=0.1,
            ),
        ],
        summary=f"Target: {target_description}",
        campaign_name=target_description[:30],
    )


# ──────────────────────────────────────────────
# Helpers
# ──────────────────────────────────────────────

def _to_search_param(data: Any) -> LinkedinSearchParam:
    """Convert a dict or None to LinkedinSearchParam."""
    if data is None:
        return LinkedinSearchParam()
    if isinstance(data, dict):
        return LinkedinSearchParam(
            include=data.get("include", []) or [],
            exclude=data.get("exclude", []) or [],
        )
    if isinstance(data, list):
        # Legacy format: bare list → include only
        return LinkedinSearchParam(include=data)
    return LinkedinSearchParam()


def _safe_int(value: Any) -> int | None:
    """Safely convert to int or None."""
    if value is None:
        return None
    try:
        return int(value)
    except (ValueError, TypeError):
        return None
