"""OpenAI Agent Client using the Responses API (non-streaming).

This is the recommended client for non-streaming use cases. It uses the newer
OpenAI Responses API (client.responses.create) instead of the legacy Chat
Completions API.
"""
from typing import Dict, List, Tuple
import time
import json
import logging

from openai import AsyncOpenAI

from animuz_core.genai.base import BaseAgent
from animuz_core.genai.tools import OpenAITool, MCPTools

logger = logging.getLogger(__name__)


def _truncate_for_log(history: List[Dict], max_content: int = 200) -> str:
    """Truncate long system messages and return JSON string for debug logging."""
    truncated = []
    for msg in (history or []):
        m = msg.copy()
        if m.get("role") == "system" and len(m.get("content", "")) > max_content:
            m["content"] = m["content"][:max_content] + "...[truncated]"
        truncated.append(m)
    return json.dumps(truncated, ensure_ascii=False)


class OpenAIAgentClientResponses(BaseAgent):
    """Non-streaming OpenAI Agent using the Responses API with tool loop.

    Supports both local tool dispatch and MCP-based tool execution.

    Args:
        user_chat_id: User/tenant ID for multi-tenant tool calls
        tools: Local tool dict (name -> (schema, callable))
        model: OpenAI model name (default: gpt-4o-mini)
        max_token: Max output tokens
        mcp_url: MCP server URL. If provided, tools are fetched from MCP.
        user_context: User context dict passed to MCP tool executions
        mcp_api_key: API key for MCP server. Defaults to MCP_API_KEY env var.
    """
    def __init__(
        self,
        user_chat_id: str = None,
        tools: Dict[str, Tuple[OpenAITool, callable]] = None,
        model: str = None,
        max_token: int = 2000,
        mcp_url: str = None,
        user_context: Dict = None,
        mcp_api_key: str = None,
        profiler=None,
        base_url: str = None,
    ) -> None:
        self.client = AsyncOpenAI(base_url=base_url)
        self.temperature = 0.0
        self.model = model if model else "gpt-4o-mini"
        self.max_token = max_token
        self.profiler = profiler
        self.user_chat_id = user_chat_id
        self.mcp = None
        if mcp_url:
            self.mcp = MCPTools(mcp_url, user_chat_id, user_context, mcp_api_key, profiler=profiler)
        elif tools:
            self.add_tools(tools)

    def add_tools(self, tools: Dict[str, Tuple[OpenAITool, callable]]):
        self.tools = tools
        self.tool_dump = [
            {"type": "function", **tool[0].model_dump()}
            for tool in self.tools.values()
        ]

    def _filter_for_openai(self, messages: List[Dict]) -> List[Dict]:
        """Remove non-OpenAI fields (metadata, complete) before sending to API."""
        clean_messages = []
        for msg in messages:
            clean_msg = msg.copy()
            clean_msg.pop("metadata", None)
            clean_msg.pop("complete", None)

            if clean_msg.get("type") == "message":
                clean_messages.append({
                    "type": "message",
                    "role": clean_msg.get("role"),
                    "content": clean_msg.get("content")
                })
            elif clean_msg.get("type") == "function_call":
                clean_messages.append({
                    "type": "function_call",
                    "name": clean_msg.get("name"),
                    "arguments": clean_msg.get("arguments"),
                    "call_id": clean_msg.get("call_id")
                })
            elif clean_msg.get("type") == "function_call_output":
                clean_messages.append({
                    "type": "function_call_output",
                    "call_id": clean_msg.get("call_id"),
                    "output": clean_msg.get("output")
                })
            else:
                clean_messages.append(clean_msg)

        return clean_messages

    async def get_reply_frontend(
        self,
        messages: List[Dict[str, str]],
        system_prompt: str = None
    ) -> List[Dict]:
        """Get a complete reply with automatic tool calling loop.

        Args:
            messages: Conversation messages (OpenAI Responses API format)
            system_prompt: System prompt to prepend

        Returns:
            List of output items (message dicts, function_call dicts,
            function_call_output dicts) representing the full conversation turn.
        """
        messages_with_prompt = [{"role": "system", "content": system_prompt}] + messages
        user_message = messages[-1]["content"]

        res = await self._create_message(self._filter_for_openai(messages_with_prompt))

        output = []
        has_function_call = False

        for item in res.output:
            if item.type == "message":
                msg_dict = {"type": "message", "role": "assistant", "content": res.output_text}
                messages_with_prompt.append(msg_dict)
                output.append(msg_dict)
            elif item.type == "function_call":
                has_function_call = True
                func_call_dict = {
                    "type": "function_call",
                    "name": item.name,
                    "arguments": item.arguments,
                    "call_id": item.call_id
                }
                messages_with_prompt.append(func_call_dict)
                output.append(func_call_dict)

        max_iterations = 10
        iteration_count = 0

        while has_function_call and iteration_count < max_iterations:
            iteration_count += 1

            for item in res.output:
                if item.type == "function_call":
                    args = json.loads(item.arguments)
                    function_name = item.name
                    logger.info(f"CALLING FUNCTION: {function_name} WITH ARGS: {str(args)}")

                    if self.mcp:
                        result, metadata = await self.process_tool_use_mcp(function_name, args, user_message)
                    else:
                        result, metadata = await self.process_tool_use(args, function_name, self.user_chat_id)

                    tool_result = {
                        "type": "function_call_output",
                        "call_id": item.call_id,
                        "output": result,
                        "metadata": metadata
                    }
                    output.append(tool_result)
                    messages_with_prompt.append(tool_result)

            res = await self._create_message(self._filter_for_openai(messages_with_prompt))

            has_function_call = False
            for item in res.output:
                if item.type == "message":
                    msg_dict = {"type": "message", "role": "assistant", "content": res.output_text}
                    messages_with_prompt.append(msg_dict)
                    output.append(msg_dict)
                elif item.type == "function_call":
                    has_function_call = True
                    func_call_dict = {
                        "type": "function_call",
                        "name": item.name,
                        "arguments": item.arguments,
                        "call_id": item.call_id
                    }
                    messages_with_prompt.append(func_call_dict)
                    output.append(func_call_dict)

        if iteration_count >= max_iterations:
            logger.warning(f"Max function call iterations ({max_iterations}) reached.")
            output.append({
                "type": "message",
                "role": "assistant",
                "content": "I apologize, but I've reached the maximum number of tool calls. Please try rephrasing your request."
            })

        return output

    async def _create_message(self, history: List[Dict]):
        """Call OpenAI Responses API."""
        start = time.time()
        logger.debug("Message History: " + _truncate_for_log(history))

        create_kwargs = dict(
            model=self.model,
            max_output_tokens=self.max_token,
            input=self.message_history if history is None else history,
            tools=self.mcp.tools if self.mcp else getattr(self, 'tool_dump', []),
        )
        if self.model not in ["gpt-5", "gpt-5-mini", "gpt-5-nano"]:
            create_kwargs["temperature"] = self.temperature

        res = await self.client.responses.create(**create_kwargs)

        openai_duration = time.time() - start
        logger.info(json.dumps({
            "timestamp": time.strftime('[%Y-%b-%d %H:%M:%S %z]'),
            "message": "OpenAI API latency",
            "latency": round(openai_duration, 4),
        }))
        if self.profiler:
            self.profiler.record_duration("openai_api", openai_duration)
        return res

    async def process_tool_use_mcp(self, tool_name: str, tool_input: dict, user_message: str):
        """Execute tool via MCP and return (result, metadata)."""
        start = time.time()
        try:
            response = self.mcp.execute_tool(tool_name, user_message=user_message, **tool_input)
            result, metadata = self.mcp.parse_response(tool_name, response, tool_input)

            logger.info(json.dumps({
                "timestamp": time.strftime('[%Y-%b-%d %H:%M:%S %z]'),
                "message": f"Tool {tool_name} used successfully",
                "latency": round(time.time() - start, 4),
            }, ensure_ascii=False))
            return result, metadata
        except Exception as e:
            logger.error(f"Tool {tool_name} error: {str(e)}")
            return "ERROR", None
