"""ForemanAgent — agentic loop with OpenRouter + tool use."""

from __future__ import annotations

import asyncio
import json
import logging
from typing import Any, Optional

import httpx

from easyclaw_tui.agent.config import AgentConfig
from easyclaw_tui.agent.prompt import build_system_prompt
from easyclaw_tui.agent.tools.registry import dispatch, get_tool_definitions

OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions"

log = logging.getLogger("foreman")


class ForemanAgent:
    """AI agent with tool use via OpenRouter."""

    def __init__(self, config: AgentConfig) -> None:
        self.config = config
        self.messages: list[dict[str, Any]] = []
        self._system_prompt = build_system_prompt(config)
        self._init_messages()
        # Callback for tool-call visibility in REPL
        self.on_tool_call: Optional[Any] = None  # async callable(name, args, result)

        # Initialize MCP tools if API key is available
        if config.api_key:
            from easyclaw_tui.agent.tools import mcp as mcp_tools
            mcp_tools.init_mcp(config.api_key)

    def _init_messages(self) -> None:
        self.messages = [{"role": "system", "content": self._system_prompt}]

    def _trim_history(self) -> None:
        """Keep conversation within max_history limit."""
        max_msgs = self.config.max_history
        if len(self.messages) <= max_msgs + 1:
            return
        # Keep system prompt + last max_msgs messages
        self.messages = [self.messages[0]] + self.messages[-max_msgs:]

    async def chat(self, user_message: str) -> str:
        """Process a user message through the agentic loop.

        Returns the final text response after all tool calls are resolved.
        """
        self.messages.append({"role": "user", "content": user_message})
        self._trim_history()

        # Import tools module to ensure registration happened
        import easyclaw_tui.agent.tools  # noqa: F401

        include_mcp = bool(self.config.api_key)
        tools = get_tool_definitions(include_mcp=include_mcp)

        for _iteration in range(self.config.max_tool_iterations):
            response = await self._call_openrouter(tools)

            if response is None:
                return "Error: Failed to get a response from OpenRouter. Check your API key and network."

            choice = response.get("choices", [{}])[0]
            message = choice.get("message", {})

            tool_calls = message.get("tool_calls")
            if not tool_calls:
                # Pure text response — done
                content = message.get("content", "")
                self.messages.append({"role": "assistant", "content": content})
                return content

            # Has tool calls — execute them
            self.messages.append(message)

            for tc in tool_calls:
                func = tc.get("function", {})
                tool_name = func.get("name", "unknown")
                try:
                    args = json.loads(func.get("arguments", "{}"))
                except json.JSONDecodeError:
                    args = {}

                log.info("Tool call: %s(%s)", tool_name, args)
                # Human confirmation can take longer than normal tools
                timeout = 300.0 if tool_name == "ask_user" else 45.0
                try:
                    result = await asyncio.wait_for(
                        dispatch(tool_name, args), timeout=timeout
                    )
                except asyncio.TimeoutError:
                    result = f"Error: Tool '{tool_name}' timed out after {timeout:.0f} seconds."
                    log.warning("Tool %s timed out", tool_name)

                # Notify REPL callback if set
                if self.on_tool_call:
                    try:
                        await self.on_tool_call(tool_name, args, result)
                    except Exception:
                        pass

                # Truncate large results
                if len(result) > 4000:
                    result = result[:4000] + f"\n... [truncated, {len(result)} chars total]"

                self.messages.append(
                    {
                        "role": "tool",
                        "tool_call_id": tc.get("id", ""),
                        "content": result,
                    }
                )

            self._trim_history()

        # Hit max iterations — force a text summary
        self.messages.append(
            {
                "role": "system",
                "content": (
                    "You have reached the maximum number of tool calls for this turn. "
                    "Summarize your findings and provide your final answer now."
                ),
            }
        )
        response = await self._call_openrouter(tools=[])
        if response:
            content = (
                response.get("choices", [{}])[0]
                .get("message", {})
                .get("content", "")
            )
            self.messages.append({"role": "assistant", "content": content})
            return content
        return "Error: Could not generate a final response."

    async def _call_openrouter(
        self, tools: list[dict[str, Any]], retry: int = 0
    ) -> Optional[dict[str, Any]]:
        """Make a single API call to OpenRouter."""
        headers = {
            "Authorization": f"Bearer {self.config.openrouter_key}",
            "Content-Type": "application/json",
            "HTTP-Referer": "https://easyclaw.help",
            "X-Title": "EasyClaw Agent Johnny 5",
        }
        payload: dict[str, Any] = {
            "model": self.config.model,
            "messages": self.messages,
            "max_tokens": 4096,
        }
        if tools:
            payload["tools"] = tools
            payload["tool_choice"] = "auto"

        try:
            async with httpx.AsyncClient(timeout=60.0) as client:
                resp = await client.post(
                    OPENROUTER_URL, headers=headers, json=payload
                )

                if resp.status_code == 429 and retry < 3:
                    wait = int(resp.headers.get("retry-after", "10"))
                    log.warning("Rate limited, waiting %ds (retry %d/3)", wait, retry + 1)
                    await asyncio.sleep(wait)
                    return await self._call_openrouter(tools, retry + 1)

                if resp.status_code >= 500 and retry < 1:
                    log.warning("Server error %d, retrying in 5s", resp.status_code)
                    await asyncio.sleep(5)
                    return await self._call_openrouter(tools, retry + 1)

                if resp.status_code != 200:
                    body = resp.text[:500]
                    log.error(
                        "OpenRouter error %d: %s", resp.status_code, body
                    )
                    return None

                return resp.json()

        except httpx.TimeoutException:
            log.error("OpenRouter request timed out")
            return None
        except Exception as e:
            log.error("OpenRouter API error: %s", e)
            return None

    def reset(self) -> None:
        """Clear conversation history, keeping system prompt."""
        self._init_messages()
