"""Orchestration capability - end-to-end pipeline for baseline 1.0.

Runs: harvest → score → enrich → draft → format brief
"""

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

from capabilities.harvest import harvest_recent
from capabilities.scoring import score_lead, calculate_priority
from capabilities.enrichment import enrich_lead
from capabilities.drafting import draft_touch, _compute_signal_tier
from state.backlog import load_backlog, get_leads_by_status, count_by_stage
from state.gate import is_gate_open, gate_status


def _get_outreach_window_label(window_name: str) -> str:
    """Convert window name to emoji label."""
    labels = {
        "first_contact": "🔔 CONTACT TODAY",
        "follow_up": "📬 FOLLOW UP",
        "nurture": "📌 NURTURE",
        "content": "📚 CONTENT",
        "silent": "⏳ WAIT",
    }
    return labels.get(window_name, "")


def _get_equipment_for_naics(naics: str) -> Dict:
    """Load equipment mapping and lookup by NAICS."""
    from pathlib import Path

    mapping_path = Path("./config/equipment-mapping.json")
    if not mapping_path.exists():
        return {}

    try:
        with open(mapping_path, 'r') as f:
            mapping = json.load(f)

        naics_clean = naics.split('(')[0].strip() if naics else ""

        if naics_clean in mapping:
            return mapping[naics_clean]

        for code, equip in mapping.items():
            if naics_clean.startswith(code[:4]):
                return equip

        return {}
    except (json.JSONDecodeError, IOError):
        return {}


def _format_lead_brief(lead: Dict, include_draft: bool = True) -> List[str]:
    """Format a single lead for the markdown brief."""
    lines = []

    name = lead.get("name", "")
    city = lead.get("city", "?")
    score = lead.get("score", 0)
    readiness = lead.get("readiness_weight", 0) * 100 if "readiness_weight" in lead else "?"
    tier = lead.get("tier", "?")
    naics = (lead.get("naics", ""))[:60]
    filing_date = lead.get("filing_date", "")[:10]

    website = "🌐" if lead.get("website_exists") else ""
    shell = "⚠️ SHELL" if lead.get("is_shell") else ""

    days_old = "?"
    window_label = ""
    if filing_date:
        try:
            filed = datetime.strptime(filing_date, "%Y-%m-%d")
            days_old = (datetime.utcnow() - filed).days

            if days_old <= 3:
                window_name = "silent"
            elif days_old <= 7:
                window_name = "first_contact"
            elif days_old <= 14:
                window_name = "follow_up"
            elif days_old <= 30:
                window_name = "nurture"
            else:
                window_name = "content"

            window_label = _get_outreach_window_label(window_name)
            if window_label:
                window_label = f" {window_label} ({days_old}d)"
        except Exception:
            pass

    lines.append(f"### {name} — {score}/100 quality · {readiness}/100 ready{window_label}")

    draft_status = lead.get("draft_status", "none")
    draft_indicator = "✓ drafted" if draft_status == "drafted" else "○ no draft"
    lines.append(f"   {draft_indicator}")

    stars, label = _compute_signal_tier(lead)
    lines.append(f"   {stars} {label}")

    if include_draft and lead.get("draft_subject"):
        lines.append(f"   💡 {lead.get('draft_anchor', '')}")

    lines.append(f"   {city} | {tier} | {naics}")
    lines.append(f"   Filed: {filing_date} {website} {shell}")

    if lead.get("competitor_displacement"):
        lines.append(f"   ⚠️ {lead.get('competitor_summary', 'Displacement opportunity')}")

    equipment = lead.get("equipment") or {}
    if equipment.get("talk_track"):
        lines.append(f"   🎯 Talk track: {equipment['talk_track']}")
    if equipment and equipment.get("recommended_models"):
        models = ", ".join(equipment["recommended_models"])
        volume = equipment.get("typical_volume", "")
        lines.append(f"   🖨️ Models: {models} — {volume}")

    if lead.get("email"):
        email = lead["email"]
        domain = email.split("@")[-1] if "@" in email else ""
        professional_tlds = [".law", ".dental", ".doctor", ".cpa"]
        if any(domain.endswith(tld) for tld in professional_tlds):
            tier_desc = "pro TLD"
        elif domain not in ["gmail.com", "yahoo.com", "hotmail.com"]:
            tier_desc = "custom domain"
        else:
            tier_desc = "free email"
        lines.append(f"   Email: {email} ({tier_desc})")

    if lead.get("domain_phone") or lead.get("brave_phone"):
        phone = lead.get("domain_phone") or lead.get("brave_phone")
        lines.append(f"   Phone: {phone}")

    if lead.get("website_url"):
        lines.append(f"   Website: {lead['website_url']}")

    lines.append("")

    if include_draft and lead.get("draft_body"):
        lines.append(f"   **Subject:** {lead['draft_subject']}")
        lines.append("")
        for body_line in lead["draft_body"].split("\n"):
            lines.append(f"   {body_line}")
        lines.append("")

    lines.append("---")
    lines.append("")

    return lines


def _select_leads(
    backlog: List[Dict],
    limit: int,
    category: Optional[str] = None,
    drafted_only: bool = False,
    undrafted_only: bool = False,
    strong_only: bool = False,
) -> List[Dict]:
    """Select top leads by priority score with optional filters.

    Args:
        backlog: Full lead list
        limit: Max leads to return
        category: Filter by temporal_category ("fresh", "recent", "historical")
        drafted_only: Only include leads with draft_status == "drafted"
        undrafted_only: Only include leads with draft_status == "none"
        strong_only: Only include leads with ★★+ signal tier

    Returns:
        Sorted list of leads (by priority descending), filtered and limited.
    """
    blocked_statuses = {"burned", "paused", "imported_legacy", "dead"}

    candidates = [
        lead for lead in backlog
        if lead.get("lead_status", "active") not in blocked_statuses
        and "priority" in lead
    ]

    if category:
        candidates = [l for l in candidates if l.get("temporal_category") == category]

    if drafted_only:
        candidates = [l for l in candidates if l.get("draft_status") == "drafted"]

    if undrafted_only:
        candidates = [l for l in candidates if l.get("draft_status", "none") == "none"]

    if strong_only:
        candidates = [
            l for l in candidates
            if _compute_signal_tier(l)[1] in ("Strong", "Good")
        ]

    candidates.sort(key=lambda x: -x.get("priority", 0))

    return candidates[:limit]


def _sequence_health_section() -> list[str]:
    """Generate sequence health section for the morning brief."""
    from state.backlog import load_backlog
    from datetime import date as _date

    backlog = load_backlog()
    today = _date.today()
    lines = []

    # ── Stale touches ──
    stale = []
    for lead in backlog:
        seq = lead.get("sequence") or {}
        for touch in seq.get("touches", []):
            sched = touch.get("scheduled_date", "")
            status = touch.get("status", "")
            if sched and status not in ("sent", "cancelled", "skipped"):
                try:
                    sched_date = _date.fromisoformat(sched)
                    if sched_date < today:
                        days_overdue = (today - sched_date).days
                        stale.append({
                            "name": lead.get("name", "?"),
                            "touch": touch.get("position", "?"),
                            "due": sched,
                            "days_overdue": days_overdue,
                        })
                except (ValueError, TypeError):
                    pass

    if stale:
        stale.sort(key=lambda x: -x["days_overdue"])
        lines.append(f"STALE SEQUENCES ({len(stale)} touches past due):")
        for s in stale[:5]:
            flag = " ⚠" if s["days_overdue"] >= 2 else ""
            lines.append(f"  {s['name']} — Touch {s['touch']}, due {s['due']}, {s['days_overdue']}d overdue{flag}")
        if len(stale) > 5:
            lines.append(f"  ... and {len(stale) - 5} more")
        lines.append("  → Run 'absel sequence due --push' to send")
        lines.append("")
    else:
        lines.append("STALE SEQUENCES: none — all touches current")
        lines.append("")

    # ── Reply backlog ──
    unhandled = []
    for lead in backlog:
        if lead.get("reply_received") and not lead.get("response_drafted"):
            replied_at = lead.get("reply_received_at", "")
            days_ago = 0
            if replied_at:
                try:
                    replied_date = _date.fromisoformat(replied_at[:10])
                    days_ago = (today - replied_date).days
                except (ValueError, TypeError):
                    pass
            unhandled.append({
                "name": lead.get("name", "?"),
                "replied_at": replied_at[:10],
                "days_ago": days_ago,
            })

    if unhandled:
        unhandled.sort(key=lambda x: -x["days_ago"])
        lines.append(f"REPLY BACKLOG ({len(unhandled)} unhandled):")
        for u in unhandled[:5]:
            flag = " ⚠" if u["days_ago"] >= 2 else ""
            lines.append(f"  {u['name']} — replied {u['replied_at']}, {u['days_ago']}d unhandled{flag}")
        if len(unhandled) > 5:
            lines.append(f"  ... and {len(unhandled) - 5} more")
        lines.append("  → Use 'absel respond <id>' to draft responses")
        lines.append("")
    else:
        lines.append("REPLY BACKLOG: none — all replies handled")
        lines.append("")

    # ── Campaign health aggregates ──
    total_sequences = 0
    total_touches = 0
    sent_touches = 0
    replies = 0
    opt_outs = 0
    autoresponders = 0
    bounces = 0

    for lead in backlog:
        seq = lead.get("sequence") or {}
        touches = seq.get("touches", [])
        if touches:
            total_sequences += 1
            total_touches += len(touches)
            for t in touches:
                if t.get("status") == "sent":
                    sent_touches += 1
        if lead.get("reply_received"):
            action = lead.get("reply_action", "")
            if action == "burn":
                opt_outs += 1
            elif action == "continue":
                autoresponders += 1
            elif action == "flag_bounce":
                bounces += 1
            else:
                replies += 1

    if total_sequences > 0:
        reply_rate = f"{replies / sent_touches * 100:.1f}%" if sent_touches > 0 else "N/A"
        optout_rate = f"{opt_outs / sent_touches * 100:.1f}%" if sent_touches > 0 else "N/A"
        sent_pct = f"{sent_touches / total_touches * 100:.0f}%" if total_touches > 0 else "N/A"
        lines.append("CAMPAIGN HEALTH:")
        lines.append(f"  Active sequences: {total_sequences}")
        lines.append(f"  Touches sent: {sent_touches} / {total_touches} ({sent_pct})")

        today_name = datetime.utcnow().strftime("%A").lower()
        from capabilities.sequencing import _load_sequences_config, get_due_touches, get_throttled_due_touches
        throttle_cfg = _load_sequences_config().get("send_throttle", {})
        if throttle_cfg.get("enabled", True):
            day_pct = throttle_cfg.get("weekly_spread", {}).get(today_name, 0)
            max_day = throttle_cfg.get("max_per_day", 15)
            due_today = get_due_touches()
            _, held = get_throttled_due_touches(due_today, throttle_cfg)
            lines.append(f"  Today's allocation: {day_pct}% ({max_day} max) — {today_name}")
            lines.append(f"  Touches due today: {len(due_today)}")
            lines.append(f"  Held for later: {len(held)}")

        lines.append(f"  Replies: {replies} ({reply_rate} of sent)")
        lines.append(f"  Opt-outs: {opt_outs} ({optout_rate} of sent)")
        lines.append(f"  Autoresponders: {autoresponders} (filtered)")
        lines.append(f"  Bounces: {bounces}")
        lines.append("")
    else:
        lines.append("CAMPAIGN HEALTH: no active sequences")
        lines.append("")

    return lines


def run_pipeline(
    days: int = 1,
    limit: int = 10,
    draft: bool = True,
    output_format: str = "markdown",
    category: Optional[str] = None,
    drafted_only: bool = False,
    undrafted_only: bool = False,
    strong_only: bool = False,
) -> str:
    """End-to-end pipeline: harvest → score → enrich → draft → format.

    Args:
        days: Number of days back to harvest
        limit: Max leads to include in brief
        draft: Whether to generate LLM drafts
        output_format: "markdown" or "json"
        category: Filter by temporal_category ("fresh", "recent", "historical")
        drafted_only: Only include leads with draft_status == "drafted"
        undrafted_only: Only include leads with draft_status == "none"
        strong_only: Only include leads with ★★+ signal tier

    Returns:
        Formatted brief as string
    """
    start_time = datetime.utcnow()

    # Check gate state
    gate = gate_status()
    gate_open = gate["open"]

    if not gate_open:
        print(f"🔒 GATE CLOSED — harvesting, enrichment, and drafting continue.")
        print(f"   Outreach is paused. Reason: {gate.get('closed_reason', '(not specified)')}")
        print(f"   Run 'absel gate open' to resume outreach.")
        print()

    print(f"🌅 Generating morning brief...")

    print(f"   Harvesting CT filings (last {days} day(s))...")
    harvest_result = harvest_recent(days)
    print(f"   Harvested {harvest_result.total_fetched} filings, kept {harvest_result.total_kept}")

    print("   Loading backlog...")
    backlog = load_backlog()

    print(f"   Scoring {len(backlog)} leads...")
    for lead in backlog:
        if "score" not in lead or lead.get("score") is None:
            score_lead(lead)
            calculate_priority(lead)

    backlog_with_priority = [lead for lead in backlog if "priority" in lead]
    backlog_with_priority.sort(key=lambda x: -x.get("priority", 0))

    top_leads = backlog_with_priority[:limit * 2]

    print(f"   Enriching top {len(top_leads)} leads...")
    enriched_count = 0
    for i, lead in enumerate(top_leads, 1):
        try:
            if not lead.get("website_exists") or not lead.get("brave_summary"):
                enrich_lead(lead["id"])
            enriched_count += 1
            if i % 10 == 0:
                print(f"      Enriched {i}/{len(top_leads)}...")
        except Exception as e:
            pass

    # Recalculate priority AFTER enrichment — readiness signals (phone, website,
    # brave data, competitor displacement) only exist after enrichment
    print("   Recalculating priority with enrichment signals...")
    for lead in backlog:
        calculate_priority(lead)

    # Select leads by priority score with optional filters
    top_leads = _select_leads(
        backlog,
        limit,
        category=category,
        drafted_only=drafted_only,
        undrafted_only=undrafted_only,
        strong_only=strong_only,
    )

    if draft:
        print(f"   Generating LLM drafts...")
        drafted_count = 0
        for i, lead in enumerate(top_leads, 1):
            try:
                if not lead.get("draft_body"):
                    draft_touch(lead["id"])
                drafted_count += 1
                if i % 5 == 0:
                    print(f"      Drafted {i}/{len(top_leads)}...")
            except Exception as e:
                print(f"      Draft failed for {lead.get('name', 'unknown')}: {e}")

    # Gate check: if closed, skip brief generation
    if not gate_open:
        stages = count_by_stage()
        elapsed = (datetime.utcnow() - start_time).total_seconds()
        print(f"\n🔒 Gate is closed. Outreach paused.")
        print(f"   Pipeline completed in {elapsed:.1f}s")
        print(f"   Leads prepared: {len(top_leads)} drafted, {enriched_count} enriched")
        print(f"   fresh: {stages['fresh_none']} pending / {stages['fresh_drafted']} drafted | "
              f"recent: {stages['recent_none']} pending / {stages['recent_drafted']} drafted | "
              f"historical: {stages['historical_none']} pending / {stages['historical_drafted']} drafted")
        return ""

    print(f"   Formatting brief...")

    if output_format == "json":
        brief_data = {
            "date": datetime.utcnow().strftime("%Y-%m-%d"),
            "total_leads": len(backlog),
            "displayed": min(limit, len(top_leads)),
            "leads": top_leads[:limit],
        }
        return json.dumps(brief_data, indent=2)

    today = datetime.utcnow().strftime("%A, %B %d, %Y")
    lines = [
        f"# Morning Brief — {today}",
        "",
    ]

    high_score = [l for l in top_leads if l.get("score", 0) >= 80]
    has_website = [l for l in top_leads if l.get("website_exists")]

    stages = count_by_stage()

    filter_label = ""
    if category or drafted_only or undrafted_only or strong_only:
        parts = []
        if category:
            parts.append(category)
        if drafted_only:
            parts.append("drafted only")
        if undrafted_only:
            parts.append("undrafted only")
        if strong_only:
            parts.append("★★+ only")
        filter_label = f" (filtered: {', '.join(parts)})"

    from state.suppressions import suppression_count
    email_supp, domain_supp = suppression_count()
    supp_text = ""
    if email_supp or domain_supp:
        supp_text = f" | ⛔ {email_supp} emails, {domain_supp} domains suppressed"

    lines.extend([
        f"**{len(top_leads)}** leads scored{filter_label} | **{len(high_score)}** score 80+ | **{len(has_website)}** have live websites{supp_text}",
        "",
        f"fresh: {stages['fresh_none']} pending / {stages['fresh_drafted']} drafted | "
        f"recent: {stages['recent_none']} pending / {stages['recent_drafted']} drafted | "
        f"historical: {stages['historical_none']} pending / {stages['historical_drafted']} drafted",
        "",
        "---",
        "",
    ])

    for i, lead in enumerate(top_leads[:limit], 1):
        lead_lines = _format_lead_brief(lead, include_draft=draft)
        lines.extend(lead_lines)

    if len(top_leads) > limit:
        lines.append(f"_Showing {limit} of {len(top_leads)} leads. Use --limit to see more._")

    elapsed = (datetime.utcnow() - start_time).total_seconds()
    lines.append("")
    lines.append("---")
    lines.append("")

    # Sequence health section
    health_lines = _sequence_health_section()
    lines.extend(health_lines)

    lines.append("---")
    lines.append(f"Generated in {elapsed:.1f}s")

    return "\n".join(lines)
