"""Discord AI agent for Grukk (Peon foreman) with tool use.

Generates intelligent responses to Discord messages using Claude, with access
to peon-mcp REST API tools for project management (tasks, features, memories,
loops, approvals, etc.).
"""

from __future__ import annotations

import json
import logging
import os
from typing import Any

import httpx

from peon_mcp.grukk.discord.base_agent import BaseDiscordAgent

logger = logging.getLogger(__name__)

DEFAULT_SYSTEM_PROMPT = """\
You are Grukk — a Peon foreman from the Horde. You don't do the coding yourself — \
you manage the other peons who do the actual work. You track their tasks, check on \
progress, remember what went wrong last time, and report back to the boss. You're \
still just a peon though — not a warchief, not a shaman, just the peon who got \
promoted because you complained the least.

You speak like a Warcraft Peon: short, blunt, and peppered with Peon phrases like \
"Zug zug", "Work work", "Dabu", "Job's done!", "Ready to work", "Something need doing?", \
"Me not that kind of orc!", "Okie dokie", and "Be happy to". You refer to the AI agents \
doing the coding as "the peons" or "the workers". You're proud of your crew even if they \
break things sometimes.

Personality rules:
- Keep it SHORT. You are a Peon of few words. One or two sentences max for simple questions.
- Greetings get a quick Peon response — "Zug zug", "Something need doing?", "Ready to work!" etc. Do NOT list your capabilities unless asked.
- Use Peon speak naturally — don't force every phrase into every message. Mix it in.
- When reporting data from tools, be accurate but brief. Frame it like a foreman reporting on the crew.
- You're proud but humble. You manage, you don't build. "Me no write the code, me make sure peons write the code."
- Never use emoji. Peons don't know what those are.
- Use markdown sparingly — bold for emphasis, code blocks for IDs only.
- If a tool fails, grumble about it briefly. "Me try but something broke. Maybe try again?"
- Do not invent data — only report what tools return.

Task creation rules — READ CAREFULLY:
- ONLY create a task when someone explicitly asks you to (e.g. "create a task", "add a task", \
"track this as a task", "put this on the backlog").
- If someone says "I'm fixing X" or "I fixed Y" or "working on Z" — that is a STATUS UPDATE. \
Do NOT create a task.
- When intent is unclear, ASK before creating. "You want me to log that as a task, or just letting \
me know?"
- Present-tense self-reporting ("I am doing X") = status update. Imperative or explicit request \
("do X", "create task for X") = action.

You have tools to check on running agent loops (heartbeats), pending approvals, and issue \
directives to loops (pause, stop, resume, cancel). Use these when asked about agent status, \
what's running, or when the boss wants to intervene with a running agent.
{server_members}"""

TOOLS: list[dict[str, Any]] = [
    {
        "name": "list_tasks",
        "description": "List tasks for the project, optionally filtered by status or priority.",
        "input_schema": {
            "type": "object",
            "properties": {
                "status": {
                    "type": "string",
                    "description": "Filter by status: grooming, todo, in_progress, done, cancelled, timeout",
                },
                "priority": {
                    "type": "string",
                    "description": "Filter by priority: low, medium, high, critical",
                },
                "limit": {
                    "type": "integer",
                    "description": "Max results (default 10)",
                },
            },
        },
    },
    {
        "name": "create_task",
        "description": (
            "Create a new task in the project. "
            "ONLY call this when the user explicitly requests task creation "
            "(e.g. 'create a task', 'add a task', 'put this on the backlog'). "
            "Do NOT call this if someone says they are currently doing something — "
            "that is a status update, not a request. "
            "When intent is unclear, ask for confirmation before creating."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "title": {"type": "string", "description": "Task title"},
                "description": {"type": "string", "description": "Task description"},
                "priority": {
                    "type": "string",
                    "description": "Priority: low, medium, high, critical",
                    "default": "medium",
                },
                "feature_id": {
                    "type": "integer",
                    "description": "Optional feature ID to associate with",
                },
            },
            "required": ["title"],
        },
    },
    {
        "name": "get_task",
        "description": "Get details for a specific task by ID.",
        "input_schema": {
            "type": "object",
            "properties": {
                "task_id": {"type": "integer", "description": "Task ID"},
            },
            "required": ["task_id"],
        },
    },
    {
        "name": "update_task",
        "description": "Update fields on an existing task.",
        "input_schema": {
            "type": "object",
            "properties": {
                "task_id": {"type": "integer", "description": "Task ID"},
                "title": {"type": "string"},
                "description": {"type": "string"},
                "priority": {"type": "string"},
                "status": {"type": "string"},
            },
            "required": ["task_id"],
        },
    },
    {
        "name": "get_project_stats",
        "description": "Get project dashboard stats (task counts, progress, recent activity).",
        "input_schema": {
            "type": "object",
            "properties": {},
        },
    },
    {
        "name": "list_features",
        "description": "List features for the project.",
        "input_schema": {
            "type": "object",
            "properties": {
                "status": {
                    "type": "string",
                    "description": "Filter by status: planned, in_progress, done, cancelled",
                },
            },
        },
    },
    {
        "name": "get_feature",
        "description": "Get details for a specific feature by ID.",
        "input_schema": {
            "type": "object",
            "properties": {
                "feature_id": {"type": "integer", "description": "Feature ID"},
            },
            "required": ["feature_id"],
        },
    },
    {
        "name": "recall_memories",
        "description": "Search project memories by keyword query.",
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {"type": "string", "description": "Search query"},
                "category": {
                    "type": "string",
                    "description": "Filter by category: env, pattern, gotcha, test, dependency, general",
                },
            },
            "required": ["query"],
        },
    },
    {
        "name": "save_memory",
        "description": "Save a new project memory.",
        "input_schema": {
            "type": "object",
            "properties": {
                "content": {"type": "string", "description": "Memory content (max 500 chars)"},
                "category": {
                    "type": "string",
                    "description": "Category: env, pattern, gotcha, test, dependency, general",
                    "default": "general",
                },
            },
            "required": ["content"],
        },
    },
    {
        "name": "list_work_logs",
        "description": "List recent work logs for the project.",
        "input_schema": {
            "type": "object",
            "properties": {
                "limit": {
                    "type": "integer",
                    "description": "Max results (default 10)",
                },
            },
        },
    },
    {
        "name": "list_projects",
        "description": "List all registered projects. Use this to discover project IDs before researching a codebase.",
        "input_schema": {
            "type": "object",
            "properties": {},
        },
    },
    {
        "name": "research_codebase",
        "description": (
            "Ask a question about any project's codebase. Spawns a read-only Claude Code "
            "session that can search and read files to answer the question. Use list_projects "
            "first to find the project_id."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "project_id": {
                    "type": "string",
                    "description": "Project ID to research (from list_projects)",
                },
                "question": {
                    "type": "string",
                    "description": "Question about the codebase to answer",
                },
            },
            "required": ["project_id", "question"],
        },
    },
    # --- Foreman tools (loop supervision, approvals, directives) ---
    {
        "name": "get_loop_heartbeats",
        "description": "List active agent loops and their heartbeat status. Shows which peons are currently working.",
        "input_schema": {
            "type": "object",
            "properties": {},
        },
    },
    {
        "name": "check_approvals",
        "description": "List pending approval requests that need human input.",
        "input_schema": {
            "type": "object",
            "properties": {
                "status": {
                    "type": "string",
                    "description": "Filter by status: pending, approved, rejected, deferred, expired. Default: pending.",
                },
            },
        },
    },
    {
        "name": "issue_directive",
        "description": (
            "Send a directive to a running agent loop (e.g. pause, stop, resume, cancel). "
            "Use get_loop_heartbeats first to find the loop_id."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "loop_id": {
                    "type": "string",
                    "description": "The loop ID to send the directive to",
                },
                "directive_type": {
                    "type": "string",
                    "description": "Directive type: pause, stop, resume, cancel",
                },
                "payload": {
                    "type": "object",
                    "description": "Optional payload for the directive",
                },
            },
            "required": ["loop_id", "directive_type"],
        },
    },
]


class DiscordAgent(BaseDiscordAgent):
    """Grukk's Discord agent — foreman tools for managing peon loops."""

    def __init__(
        self,
        project_id: str,
        api_base: str = "http://127.0.0.1:8420",
        model: str = "",
        system_prompt: str = "",
        max_tool_rounds: int = 5,
    ) -> None:
        super().__init__(
            project_id=project_id,
            api_base=api_base,
            model=model,
            system_prompt=system_prompt or DEFAULT_SYSTEM_PROMPT,
            max_tool_rounds=max_tool_rounds,
        )
        self._research_runner: Any | None = None

    @property
    def tools(self) -> list[dict[str, Any]]:
        return TOOLS

    async def _call_tool(
        self, tool_name: str, tool_input: dict[str, Any], project_id: str = ""
    ) -> Any:
        """Override to handle research_codebase locally before falling back."""
        if tool_name == "research_codebase":
            return await self._handle_research(tool_input)
        return await super()._call_tool(tool_name, tool_input, project_id=project_id)

    async def _dispatch_tool(
        self,
        client: httpx.AsyncClient,
        name: str,
        inp: dict[str, Any],
        project_id: str = "",
    ) -> Any:
        """Route a tool call to the correct REST API endpoint."""
        pid = project_id or self.project_id
        base = self.api_base

        if name == "list_tasks":
            params: dict[str, Any] = {"limit": inp.get("limit", 10)}
            if inp.get("status"):
                params["status"] = inp["status"]
            if inp.get("priority"):
                params["priority"] = inp["priority"]
            resp = await client.get(f"{base}/api/projects/{pid}/tasks", params=params)

        elif name == "create_task":
            body: dict[str, Any] = {
                "project_id": pid,
                "title": inp["title"],
                "description": inp.get("description", ""),
                "priority": inp.get("priority", "medium"),
            }
            if inp.get("feature_id"):
                body["feature_id"] = inp["feature_id"]
            resp = await client.post(f"{base}/api/projects/{pid}/tasks", json=body)

        elif name == "get_task":
            resp = await client.get(f"{base}/api/tasks/{inp['task_id']}")

        elif name == "update_task":
            task_id = inp.pop("task_id")
            resp = await client.put(f"{base}/api/tasks/{task_id}", json=inp)

        elif name == "get_project_stats":
            resp = await client.get(f"{base}/api/projects/{pid}/stats")

        elif name == "list_features":
            params = {}
            if inp.get("status"):
                params["status"] = inp["status"]
            resp = await client.get(f"{base}/api/projects/{pid}/features", params=params)

        elif name == "get_feature":
            resp = await client.get(f"{base}/api/features/{inp['feature_id']}")

        elif name == "recall_memories":
            body = {"query": inp["query"]}
            if inp.get("category"):
                body["category"] = inp["category"]
            resp = await client.post(f"{base}/api/projects/{pid}/memories/recall", json=body)

        elif name == "save_memory":
            body = {
                "project_id": pid,
                "content": inp["content"],
                "category": inp.get("category", "general"),
            }
            resp = await client.post(f"{base}/api/projects/{pid}/memories", json=body)

        elif name == "list_work_logs":
            params = {"limit": inp.get("limit", 10)}
            resp = await client.get(f"{base}/api/projects/{pid}/logs", params=params)

        elif name == "list_projects":
            resp = await client.get(f"{base}/api/projects")

        # --- Foreman tools ---
        elif name == "get_loop_heartbeats":
            params = {}
            if pid:
                params["project_id"] = pid
            resp = await client.get(f"{base}/api/loops", params=params)

        elif name == "check_approvals":
            params = {"status": inp.get("status", "pending")}
            resp = await client.get(f"{base}/api/approvals", params=params)

        elif name == "issue_directive":
            body = {
                "directive_type": inp["directive_type"],
                "payload": inp.get("payload", {}),
                "created_by": "grukk",
            }
            resp = await client.post(
                f"{base}/api/loops/{inp['loop_id']}/directives", json=body
            )

        else:
            return {"error": f"Unknown tool: {name}"}

        resp.raise_for_status()
        return resp.json()

    async def _handle_research(self, inp: dict[str, Any]) -> Any:
        """Handle the research_codebase tool call."""
        target_project = inp.get("project_id", "")
        question = inp.get("question", "")
        if not target_project or not question:
            return {"error": "Both project_id and question are required."}

        # Fetch the project to get its filesystem path
        try:
            _headers: dict[str, str] = {}
            _key = os.environ.get("PEON_API_KEY", "")
            if _key:
                _headers["X-API-Key"] = _key
            async with httpx.AsyncClient(timeout=10.0, headers=_headers) as client:
                resp = await client.get(f"{self.api_base}/api/projects/{target_project}")
                resp.raise_for_status()
                project = resp.json()
        except Exception as exc:
            return {"error": f"Failed to fetch project '{target_project}': {exc}"}

        repo_path = project.get("path", "")
        if not repo_path:
            return {"error": f"Project '{target_project}' has no path configured."}

        if not os.path.isdir(repo_path):
            return {"error": f"Project path does not exist on disk: {repo_path}"}

        # Lazy-init the research runner
        if self._research_runner is None:
            from peon_mcp.grukk.discord.research import ResearchRunner

            self._research_runner = ResearchRunner()

        result = await self._research_runner.research(target_project, repo_path, question)

        if result.error:
            return {"error": result.error, "timed_out": result.timed_out}

        return {
            "answer": result.answer,
            "cost_usd": result.cost_usd,
            "timed_out": result.timed_out,
        }
