"""
OpenAI LLM Provider Adapter

Adapter for OpenAI's GPT-5 models.

v2.0: Includes event emission for tool calls and real-time visibility.
"""

import time
from typing import Callable

from openai import AsyncOpenAI, APIError, RateLimitError, APIConnectionError

from honeydb_ai.domain.exceptions import (
    LLMAPIError,
    LLMProviderError,
    LLMRateLimitError,
    LLMRequestCancelledError,
)
from honeydb_ai.domain.models import Event, LLMResponse, Message
from honeydb_ai.infrastructure.llm_adapters.base import BaseLLMAdapter


class OpenAIAdapter(BaseLLMAdapter):
    """
    OpenAI provider adapter.

    Supports: gpt-5.4-pro, gpt-5.4, and gpt-5-mini (as returned by `get_available_models()`).
    """

    def __init__(self, api_key: str):
        """
        Initialize OpenAI adapter.

        Args:
            api_key: OpenAI API key
        """
        super().__init__(api_key)
        self.client = AsyncOpenAI(api_key=api_key)

    async def send_message(
        self,
        messages: list[Message],
        model: str | None = None,
        temperature: float | None = None,
        max_tokens: int | None = None,
        stream: bool = False,
        event_callback: Callable[[Event], None] | None = None,
    ) -> LLMResponse:
        """
        Send messages to OpenAI API.

        v2.0: Emits events for request lifecycle and tool calls.

        Args:
            messages: List of messages in the conversation
            model: Model identifier (e.g., "gpt-5.4")
            temperature: Sampling temperature (0.0-2.0)
            max_tokens: Maximum tokens in response
            stream: Whether to stream response (Phase 1: False)
            event_callback: Optional callback for events

        Returns:
            LLMResponse with the assistant's response

        Raises:
            LLMProviderError: If the provider returns an error
            LLMAPIError: If the API call fails
            LLMRateLimitError: If rate limit is exceeded
            LLMRequestCancelledError: If user cancels the request
        """
        model = model or "gpt-5.4"
        start_time = time.time()

        # Emit request started event
        self._emit_request_started(event_callback, model, len(messages))

        # Convert messages to OpenAI format
        api_messages = self._messages_to_dict(messages)

        # Build request parameters
        params = {
            "model": model,
            "messages": api_messages,
        }

        if temperature is not None:
            params["temperature"] = temperature

        if max_tokens is not None:
            params["max_tokens"] = max_tokens

        try:
            # Make API call
            response = await self.client.chat.completions.create(**params)

            # Extract response
            choice = response.choices[0]
            content = choice.message.content or ""
            finish_reason = choice.finish_reason

            # Check for tool calls (if model supports function calling)
            if hasattr(choice.message, 'tool_calls') and choice.message.tool_calls:
                for tool_call in choice.message.tool_calls:
                    self._emit_tool_call(
                        event_callback,
                        tool_call.function.name,
                        {"args": tool_call.function.arguments}
                    )
                    # Note: In a full implementation, we'd execute the tool and emit tool_result
                    # For now, this is just visibility into the tool call

            # Calculate duration
            duration_ms = (time.time() - start_time) * 1000

            # Emit response received event
            self._emit_response_received(event_callback, finish_reason, duration_ms)

            # Log (metadata only)
            self.logger.info(
                f"OpenAI request completed | Model: {model} | "
                f"Duration: {duration_ms:.0f}ms | Finish: {finish_reason}"
            )

            # Build response
            usage = {}
            if hasattr(response, 'usage') and response.usage:
                usage = {
                    "prompt_tokens": response.usage.prompt_tokens,
                    "completion_tokens": response.usage.completion_tokens,
                    "total_tokens": response.usage.total_tokens,
                }

            return LLMResponse(
                content=content,
                model=model,
                finish_reason=finish_reason,
                usage=usage,
                metadata={
                    "response_id": response.id,
                    "created": response.created,
                }
            )

        except RateLimitError as e:
            self.logger.warning(f"OpenAI rate limit exceeded: {e}")
            # Try to extract retry_after from error
            retry_after = getattr(e, 'retry_after', None)
            raise LLMRateLimitError("OpenAI", retry_after)

        except APIConnectionError as e:
            self.logger.error(f"OpenAI connection error: {e}")
            raise LLMAPIError("OpenAI", 0, f"Connection error: {e}")

        except APIError as e:
            self.logger.error(f"OpenAI API error: {e}")
            status_code = getattr(e, 'status_code', 0)
            raise LLMAPIError("OpenAI", status_code, str(e))

        except KeyboardInterrupt:
            self.logger.info("OpenAI request cancelled by user")
            raise LLMRequestCancelledError()

        except Exception as e:
            self.logger.error(f"Unexpected OpenAI error: {e}")
            raise LLMProviderError("OpenAI", str(e))

    def validate_api_key(self) -> bool:
        """
        Validate that the API key is configured.

        Returns:
            True if API key is present and non-empty
        """
        return bool(self.api_key and len(self.api_key.strip()) > 0)

    def get_provider_name(self) -> str:
        """Get the provider name."""
        return "openai"

    def get_available_models(self) -> list[str]:
        """
        Get list of available OpenAI models.

        Returns:
            List of model identifiers
        """
        return [
            "gpt-5.4-pro",
            "gpt-5.4",
            "gpt-5-mini",
        ]
