"""Main polling loop for Sprocket, the sales goblin agent.

Sprocket runs a continuous polling loop that:
  1. Fetches unresearched leads from the Peon REST API.
  2. Researches each lead via the Claude Agent SDK (WebSearch + WebFetch).
  3. Scores and stores enrichment data back on the lead.
  4. Posts a batch summary to the Discord #sales channel.
"""

from __future__ import annotations

import asyncio
import logging
import os
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from peon_mcp.goblin.api import GoblinAPIClient
from peon_mcp.goblin.personality import SPROCKET_SYSTEM_PROMPT
from peon_mcp.goblin.researcher import LeadResearcher

log = logging.getLogger("peon-goblin")

# Guard the Agent SDK import — it may not be installed everywhere.
try:
    from claude_agent_sdk import ClaudeAgentOptions, query  # type: ignore[import-untyped]

    _HAS_AGENT_SDK = True
except ModuleNotFoundError:
    _HAS_AGENT_SDK = False
    ClaudeAgentOptions = None  # type: ignore[assignment,misc]
    query = None  # type: ignore[assignment]

# Discord config for Sprocket (#sales channel).
_DISCORD_CONFIG_ID = 2
_DISCORD_SALES_CHANNEL_ID = "1479171459384610887"  # #sales channel ID


async def run_agent_loop(
    *,
    api_url: str,
    api_key: str,
    model: str,
    poll_interval: int,
    project_id: str,
) -> None:
    """Main Sprocket polling loop.

    Parameters
    ----------
    api_url:
        Base URL for the Peon REST API.
    api_key:
        Optional API key for authenticated requests.
    model:
        Anthropic model identifier.
    poll_interval:
        Polling interval in seconds between research cycles.
    project_id:
        Project ID to fetch leads from (e.g. ``pyrabbit/teamroom``).
    """
    client = GoblinAPIClient(api_url, api_key)
    researcher = LeadResearcher()

    # ---- Start Sprocket's Discord bot ----
    try:
        from peon_mcp.grukk.discord.bot import BotManager
        from pathlib import Path

        sprocket_bot_manager = BotManager()
        tokens_dir = Path(os.environ.get("DISCORD_BOT_TOKENS_DIR", "/etc/peon/discord"))

        # Retry config fetch — API may still be starting
        sprocket_config = None
        for attempt in range(10):
            configs_resp = client._request("GET", "/api/discord/configs")
            if isinstance(configs_resp, dict):
                configs = configs_resp.get("items", [])
            elif isinstance(configs_resp, list):
                configs = configs_resp
            else:
                configs = []
            sprocket_config = next((c for c in configs if c.get("id") == _DISCORD_CONFIG_ID and c.get("enabled")), None)
            if sprocket_config:
                break
            log.info("Waiting for Discord config (attempt %d/10)...", attempt + 1)
            await asyncio.sleep(5)

        if sprocket_config:
            token_file = tokens_dir / str(_DISCORD_CONFIG_ID)
            if token_file.exists():
                sprocket_config["bot_token"] = token_file.read_text().strip()
                await sprocket_bot_manager.start_from_configs([sprocket_config])
                log.info("Started Sprocket Discord bot (config_id=%d)", _DISCORD_CONFIG_ID)
            else:
                log.warning("No token file at %s — Sprocket bot not started", token_file)
        else:
            log.info("No Discord config for Sprocket — bot not started")
    except Exception as exc:
        log.warning("Failed to start Sprocket Discord bot: %s", exc)

    log.info("Heh heh!  Sprocket ready to hustle!  Watching project: %s", project_id)

    while True:
        try:
            leads = client.get_unresearched_leads(project_id, limit=3)

            if leads:
                log.info("Psst!  Sprocket found %d unresearched lead(s).  Time is money!", len(leads))
                results: list[tuple[dict, dict]] = []

                for lead in leads:
                    lead_name = lead.get("name", "Unknown")
                    lead_id = str(lead.get("id", ""))
                    log.info("Researching lead: %s (%s)", lead_name, lead_id)

                    # Build research prompt.
                    prompt = researcher.build_research_prompt(lead)

                    # Call Agent SDK to research.
                    response = await _research_lead(prompt, model)

                    # Parse and score.
                    enrichment = researcher.parse_enrichment(response)
                    enrichment["score"] = researcher.calculate_score(enrichment)
                    enrichment["researched_at"] = datetime.now(timezone.utc).isoformat()
                    enrichment["researched_by"] = "sprocket"

                    # Merge with existing metadata and update.
                    existing_metadata = lead.get("metadata") or {}
                    existing_metadata.update(enrichment)
                    client.update_lead_metadata(lead_id, project_id, existing_metadata)

                    results.append((lead, enrichment))
                    log.info(
                        "Lead %s scored %d/10: %s",
                        lead_name,
                        enrichment.get("score", 0),
                        enrichment.get("score_reasoning", "no reasoning"),
                    )

                # Report to Discord.
                _post_discord_summary(client, results)
            else:
                log.debug("No unresearched leads.  Sprocket take a smoke break.")

        except Exception:
            log.exception("Sprocket brain hurt!  Error in research cycle.")

        await asyncio.sleep(poll_interval)


# ---------------------------------------------------------------------------
# Agent SDK helper
# ---------------------------------------------------------------------------


async def _research_lead(prompt: str, model: str) -> str:
    """Use the Agent SDK to research a lead via WebSearch and WebFetch.

    Falls back to an error JSON if the SDK is not installed.
    """
    if not _HAS_AGENT_SDK or query is None or ClaudeAgentOptions is None:
        log.warning("Agent SDK not available — Sprocket running in basic mode.")
        return '{"error": "Agent SDK not available"}'

    options = ClaudeAgentOptions(
        model=model,
        system_prompt=SPROCKET_SYSTEM_PROMPT,
        allowed_tools=["WebSearch", "WebFetch"],
        max_turns=10,
        permission_mode="bypassPermissions",
    )

    result = ""
    try:
        async for msg in query(prompt=prompt, options=options):
            if hasattr(msg, "result"):
                result = msg.result
    except Exception:
        log.exception("Agent SDK query failed during lead research.")
        result = '{"error": "Agent SDK query failed"}'

    return result


# ---------------------------------------------------------------------------
# Discord reporting
# ---------------------------------------------------------------------------


def _post_discord_summary(
    client: GoblinAPIClient,
    results: list[tuple[dict, dict]],
) -> None:
    """Post a batch research summary to the Discord #sales channel."""
    if not results:
        return

    hot_leads = [(lead, enr) for lead, enr in results if enr.get("score", 0) >= 8]
    warm_leads = [(lead, enr) for lead, enr in results if 5 <= enr.get("score", 0) < 8]
    cold_leads = [(lead, enr) for lead, enr in results if enr.get("score", 0) < 5]

    lines = [
        f"Heh heh, boss!  Sprocket researched {len(results)} lead(s).",
    ]

    if hot_leads:
        lines.append(f"Found {len(hot_leads)} HOT one(s) (score 8+):")
        for lead, enr in hot_leads:
            name = lead.get("name", "Unknown")
            company = lead.get("company") or ""
            score = enr.get("score", 0)
            reasoning = enr.get("score_reasoning", "")
            label = f"{name}"
            if company:
                label = f"{company} ({name})"
            lines.append(f"  - {label} -- score {score}/10 -- {reasoning}")

    if warm_leads:
        lines.append(f"Found {len(warm_leads)} warm lead(s) (score 5-7).")

    if cold_leads:
        lines.append(f"Found {len(cold_leads)} cold lead(s) (score <5).")

    content = "\n".join(lines)

    try:
        client.send_discord_message(_DISCORD_CONFIG_ID, _DISCORD_SALES_CHANNEL_ID, content)
        log.info("Posted research summary to Discord #sales.")
    except Exception:
        log.warning("Failed to post Discord summary (non-fatal).", exc_info=True)
