"""Tool 2: create_campaign — Create a LinkedIn outreach campaign from a natural language description.

Takes a target description like "Find me fintech CTOs" and:
1. Generates an ICP (Ideal Customer Profile) via LLM
2. Searches LinkedIn for matching prospects
3. Scores and ranks prospects by fit
4. Creates a campaign with queued contacts
5. Shows a preview for confirmation
"""

from __future__ import annotations

import json
import logging
from typing import Any

from ..ai.icp_generator import generate_icp
from ..config import get_tier, load_config
from ..constants import (
    FREE_MAX_CAMPAIGNS,
    FREE_MAX_CONTACTS_ANALYZED,
    STATUS_ACTIVE,
    STATUS_DRAFT,
    TIER_PRO,
)
from ..db.queries import (
    create_campaign,
    create_outreach,
    get_monthly_usage,
    get_setting,
    increment_usage,
    list_campaigns,
    save_contact,
    update_campaign,
)
from ..formatter import stars, table
from ..linkedin import (
    UnipileAuthError,
    UnipileError,
    get_account_id,
    get_linkedin_client,
)

logger = logging.getLogger(__name__)


def _score_prospect(prospect: dict, icp_segment: dict) -> float:
    """Score a prospect (0.0 - 1.0) against an ICP segment."""
    score = 0.0
    max_score = 0.0

    # Title match (highest weight)
    max_score += 3.0
    target_titles = [t.lower() for t in icp_segment.get("titles", [])]
    prospect_title = prospect.get("title", "").lower()
    if any(t in prospect_title for t in target_titles):
        score += 3.0
    elif any(word in prospect_title for t in target_titles for word in t.split()):
        score += 1.5

    # Keyword match in headline
    max_score += 2.0
    keywords = icp_segment.get("keywords", "").lower().split(",")
    headline = prospect.get("headline", "").lower()
    keyword_matches = sum(1 for kw in keywords if kw.strip() and kw.strip() in headline)
    if keywords:
        score += min(2.0, (keyword_matches / max(len(keywords), 1)) * 2.0)

    # Has company info
    max_score += 1.0
    if prospect.get("company"):
        score += 1.0

    # Has location info
    max_score += 0.5
    if prospect.get("location"):
        score += 0.5

    # Has public_id (can be reached)
    max_score += 0.5
    if prospect.get("public_id"):
        score += 0.5

    return score / max_score if max_score > 0 else 0.0


async def run_create_campaign(
    target_description: str,
    campaign_name: str = "",
) -> str:
    """Create a new outreach campaign.

    Flow:
    1. Check setup is complete + free tier limits
    2. Generate ICP from description using LLM
    3. Search LinkedIn for matching prospects
    4. Score and rank by fit
    5. Create campaign + queued contacts in DB
    6. Return preview for user confirmation
    """

    # ── Step 0: Check setup ──
    setup_done = get_setting("setup_complete", False)
    if not setup_done:
        return (
            "❌ Please run setup_profile first!\n\n"
            "I need to analyze your LinkedIn profile before creating campaigns.\n"
            "This ensures outreach messages sound like you."
        )

    # ── Step 1: Free tier limits ──
    tier = get_tier()
    if tier != TIER_PRO:
        existing = list_campaigns()
        active_campaigns = [c for c in existing if c["status"] in (STATUS_ACTIVE, STATUS_DRAFT)]
        if len(active_campaigns) >= FREE_MAX_CAMPAIGNS:
            return (
                f"⚠️ Free tier limit: {FREE_MAX_CAMPAIGNS} active campaign(s).\n\n"
                "You already have an active campaign. Options:\n"
                "├── Complete or pause your current campaign first\n"
                "└── Upgrade to Pro ($29/mo) for unlimited campaigns\n\n"
                "Tip: Say 'show_status' to see your current campaign."
            )

    # ── Step 2: Get Unipile account ──
    account_id = get_account_id()
    if not account_id:
        return (
            "❌ No LinkedIn account connected.\n\n"
            "Run setup_profile first to connect your LinkedIn account."
        )

    try:
        client = get_linkedin_client()
    except UnipileError as e:
        return f"❌ {e}"

    # ── Step 3: Generate ICP ──
    profile = get_setting("profile", {})
    expertise = get_setting("expertise_map", {})

    # Merge profile + expertise for context
    user_context = {**profile, **expertise}

    try:
        icp = await generate_icp(target_description, user_context)
    except Exception as e:
        logger.error(f"ICP generation failed: {e}")
        return (
            f"❌ Failed to generate ICP: {e}\n\n"
            "Check your LLM API key in ~/.heylead/config.json"
        )

    # ── Step 4: Search LinkedIn via Unipile ──
    all_prospects: list[dict] = []
    segments = icp.get("segments", [])

    for segment in segments:
        keywords = segment.get("keywords", "")
        titles = segment.get("titles", [])

        # Search with keywords, optionally adding top title
        search_keywords = keywords
        if titles and titles[0].lower() not in keywords.lower():
            search_keywords = f"{titles[0]} {keywords}"

        try:
            prospects = await client.search_people(
                account_id=account_id,
                keywords=search_keywords,
                count=25,
            )
            all_prospects.extend(prospects)
        except UnipileAuthError:
            await client.close()
            return (
                "🔑 LinkedIn account disconnected.\n\n"
                "Run setup_profile() again to reconnect."
            )
        except Exception as e:
            logger.warning(f"Search failed for segment '{segment.get('name')}': {e}")

    await client.close()

    if not all_prospects:
        return (
            "😕 No prospects found on LinkedIn for this description.\n\n"
            f"Searched for: \"{target_description}\"\n\n"
            "Try:\n"
            "├── Use broader keywords (e.g., 'startup founders' instead of 'fintech CTO Series A')\n"
            "├── Check your LinkedIn connection (run setup_profile)\n"
            "└── Try a different target description"
        )

    # Deduplicate by public_id or linkedin_url
    seen = set()
    unique_prospects = []
    for p in all_prospects:
        key = p.get("public_id") or p.get("linkedin_url") or p.get("name")
        if key and key not in seen:
            seen.add(key)
            unique_prospects.append(p)

    # ── Step 5: Score prospects ──
    primary_segment = segments[0] if segments else {}
    for prospect in unique_prospects:
        prospect["fit_score"] = _score_prospect(prospect, primary_segment)

    # Sort by score, highest first
    unique_prospects.sort(key=lambda p: p.get("fit_score", 0), reverse=True)

    # Free tier: cap contacts
    max_contacts = FREE_MAX_CONTACTS_ANALYZED if tier != TIER_PRO else 1000
    prospects_to_save = unique_prospects[:max_contacts]

    # ── Step 6: Create campaign in DB ──
    final_name = campaign_name or icp.get("campaign_name", target_description[:40])

    campaign_id = create_campaign(
        name=final_name,
        icp_json=json.dumps(icp),
        status=STATUS_ACTIVE,
        mode="copilot",
        config_json=json.dumps({
            "target_description": target_description,
            "prospect_count": len(prospects_to_save),
        }),
    )

    # Save contacts + create outreach records
    for prospect in prospects_to_save:
        contact_id = save_contact(
            campaign_id=campaign_id,
            name=prospect.get("name", ""),
            title=prospect.get("title", ""),
            company=prospect.get("company", ""),
            linkedin_url=prospect.get("linkedin_url", ""),
            linkedin_id=prospect.get("public_id", ""),
            profile_json=json.dumps(prospect),
            fit_score=prospect.get("fit_score", 0.0),
        )
        create_outreach(
            campaign_id=campaign_id,
            contact_id=contact_id,
            status="pending",
        )

    # Track usage
    increment_usage("campaigns_created")

    # ── Step 7: Format preview ──
    output_lines = [
        f"✅ Campaign created: **{final_name}**",
        f"📋 Campaign ID: `{campaign_id[:8]}...`",
        "",
        "ICP Summary:",
        f"├── {icp.get('summary', target_description)}",
    ]

    if icp.get("relevance_hook"):
        output_lines.append(f"├── Hook: {icp['relevance_hook']}")

    output_lines.extend([
        f"└── {len(prospects_to_save)} prospects queued",
        "",
        f"Top prospects (of {len(prospects_to_save)}):",
    ])

    # Show top 5 prospects
    for i, p in enumerate(prospects_to_save[:5]):
        is_last = i == min(4, len(prospects_to_save) - 1)
        prefix = "└──" if is_last else "├──"
        name = p.get("name", "Unknown")
        title = p.get("title", "")
        company = p.get("company", "")
        score = p.get("fit_score", 0)
        star_rating = stars(score)

        role = f"{title}" if title else ""
        if company:
            role += f" at {company}" if role else company

        output_lines.append(f"{prefix} {i+1}. {name} — {role} (fit: {star_rating})")

    if len(prospects_to_save) > 5:
        output_lines.append(f"    ... and {len(prospects_to_save) - 5} more")

    output_lines.extend([
        "",
        "📌 Mode: Copilot (I'll show you each message before sending)",
        "",
        "Ready to start? Say:",
        "  → \"send messages\" or use generate_and_send",
        "  → \"show status\" to see campaign details",
    ])

    if tier != TIER_PRO and len(unique_prospects) > max_contacts:
        output_lines.extend([
            "",
            f"💡 Found {len(unique_prospects)} total matches but free tier caps at {max_contacts}.",
            "   Upgrade to Pro ($29/mo) for unlimited contacts.",
        ])

    return "\n".join(output_lines)
