"""
Remote storage implementation.

Provides storage that calls Backend APIs.
Used when chat_shell runs as HTTP service and needs to access Backend's data.
"""

import json
import logging
from typing import Any, Optional

import httpx

from chat_shell.storage.interfaces import (
    HistoryStoreInterface,
    Message,
    StorageProvider,
    ToolResultStoreInterface,
)

logger = logging.getLogger(__name__)


class RemoteHistoryStore(HistoryStoreInterface):
    """
    Remote history storage implementation (calls Backend API).

    Used for Backend integration scenarios:
    - chat_shell queries history by calling Backend's /internal/chat/history API
    - This way chat_shell doesn't need direct access to Backend's database

    Authentication:
    - Uses Internal Service Token (not user Token)
    - Generated by Backend at startup for internal service communication
    - Token passed to chat_shell via environment variables or config
    - Recommended: long expiration (e.g., 24h) or non-expiring service key
    """

    def __init__(
        self,
        base_url: str,
        auth_token: str,
        timeout: float = 30.0,
    ):
        """
        Initialize remote storage.

        Args:
            base_url: Backend internal API address, e.g., "http://backend:8000/internal"
            auth_token: Internal Service Token for service-to-service auth
            timeout: Request timeout in seconds
        """
        self.base_url = base_url.rstrip("/")
        self.auth_token = auth_token
        self.timeout = timeout
        self._client: Optional[httpx.AsyncClient] = None

    async def _get_client(self) -> httpx.AsyncClient:
        """Get or create shared HTTP client (connection pool reuse)."""
        if self._client is None:
            # Build headers - only add Authorization if token is provided
            headers = {
                "X-Service-Name": "chat-shell",
                "Content-Type": "application/json",
            }
            if self.auth_token:
                headers["Authorization"] = f"Bearer {self.auth_token}"

            self._client = httpx.AsyncClient(
                base_url=self.base_url,
                headers=headers,
                timeout=self.timeout,
                limits=httpx.Limits(
                    max_keepalive_connections=10,
                    max_connections=20,
                    keepalive_expiry=30.0,
                ),
            )
        return self._client

    async def close(self) -> None:
        """Close HTTP client."""
        if self._client:
            await self._client.aclose()
            self._client = None

    async def get_history(
        self,
        session_id: str,
        limit: Optional[int] = None,
        before_message_id: Optional[str] = None,
        is_group_chat: bool = False,
    ) -> list[Message]:
        """Get chat history for a session."""
        client = await self._get_client()
        params = {}
        if limit:
            params["limit"] = limit
        if before_message_id:
            params["before_message_id"] = before_message_id
        # Pass is_group_chat to API for proper username prefix handling
        params["is_group_chat"] = str(is_group_chat).lower()

        url = f"/chat/history/{session_id}"
        full_url = f"{self.base_url}{url}"
        logger.debug(
            "[RemoteHistoryStore] >>> GET %s params=%s",
            full_url,
            params,
        )

        try:
            response = await client.get(url, params=params)

            logger.debug(
                "[RemoteHistoryStore] <<< Response status=%d, content_length=%s",
                response.status_code,
                response.headers.get("content-length", "unknown"),
            )

            response.raise_for_status()
            data = response.json()

            messages = [Message.from_dict(m) for m in data.get("messages", [])]
            logger.debug(
                "[RemoteHistoryStore] Loaded %d messages for session_id=%s",
                len(messages),
                session_id,
            )
            return messages
        except Exception as e:
            logger.error(
                "[RemoteHistoryStore] Request failed: url=%s, error=%s",
                full_url,
                e,
                exc_info=True,
            )
            raise

    async def append_message(
        self,
        session_id: str,
        message: Message,
    ) -> str:
        """Append a message to session history."""
        client = await self._get_client()
        response = await client.post(
            f"/chat/history/{session_id}/messages",
            json={
                "role": message.role,
                "content": message.content,
                "name": message.name,
                "tool_call_id": message.tool_call_id,
                "tool_calls": message.tool_calls,
                "metadata": message.metadata,
            },
        )
        response.raise_for_status()
        return response.json().get("message_id", "")

    async def append_messages(
        self,
        session_id: str,
        messages: list[Message],
    ) -> list[str]:
        """Batch append messages to session history."""
        client = await self._get_client()
        response = await client.post(
            f"/chat/history/{session_id}/messages/batch",
            json={
                "messages": [
                    {
                        "role": m.role,
                        "content": m.content,
                        "name": m.name,
                        "tool_call_id": m.tool_call_id,
                        "tool_calls": m.tool_calls,
                        "metadata": m.metadata,
                    }
                    for m in messages
                ]
            },
        )
        response.raise_for_status()
        return response.json().get("message_ids", [])

    async def clear_history(self, session_id: str) -> bool:
        """Clear all history for a session."""
        client = await self._get_client()
        response = await client.delete(f"/chat/history/{session_id}")
        response.raise_for_status()
        return response.json().get("success", False)

    async def list_sessions(
        self,
        limit: int = 100,
        offset: int = 0,
    ) -> list[str]:
        """List all session IDs."""
        client = await self._get_client()
        response = await client.get(
            "/chat/sessions",
            params={"limit": limit, "offset": offset},
        )
        response.raise_for_status()
        return response.json().get("sessions", [])

    async def update_message(
        self,
        session_id: str,
        message_id: str,
        content: Any,
    ) -> bool:
        """Update an existing message's content."""
        client = await self._get_client()
        response = await client.patch(
            f"/chat/history/{session_id}/messages/{message_id}",
            json={"content": content},
        )
        response.raise_for_status()
        return response.json().get("success", False)

    async def delete_message(
        self,
        session_id: str,
        message_id: str,
    ) -> bool:
        """Delete a message."""
        client = await self._get_client()
        response = await client.delete(
            f"/chat/history/{session_id}/messages/{message_id}"
        )
        response.raise_for_status()
        return response.json().get("success", False)


class RemoteToolResultStore(ToolResultStoreInterface):
    """Remote tool result storage implementation."""

    def __init__(
        self,
        base_url: str,
        auth_token: str,
        timeout: float = 30.0,
    ):
        self.base_url = base_url.rstrip("/")
        self.auth_token = auth_token
        self.timeout = timeout
        self._client: Optional[httpx.AsyncClient] = None

    async def _get_client(self) -> httpx.AsyncClient:
        """Get or create shared HTTP client."""
        if self._client is None:
            # Build headers - only add Authorization if token is provided
            headers = {
                "X-Service-Name": "chat-shell",
                "Content-Type": "application/json",
            }
            if self.auth_token:
                headers["Authorization"] = f"Bearer {self.auth_token}"

            self._client = httpx.AsyncClient(
                base_url=self.base_url,
                headers=headers,
                timeout=self.timeout,
                limits=httpx.Limits(
                    max_keepalive_connections=10,
                    max_connections=20,
                    keepalive_expiry=30.0,
                ),
            )
        return self._client

    async def close(self) -> None:
        """Close HTTP client."""
        if self._client:
            await self._client.aclose()
            self._client = None

    async def save_tool_result(
        self,
        session_id: str,
        tool_call_id: str,
        result: Any,
        ttl: Optional[int] = None,
    ) -> bool:
        """Save tool execution result."""
        client = await self._get_client()
        response = await client.post(
            f"/chat/tool-results/{session_id}",
            json={
                "tool_call_id": tool_call_id,
                "result": result,
                "ttl": ttl,
            },
        )
        response.raise_for_status()
        return response.json().get("success", False)

    async def get_tool_result(
        self,
        session_id: str,
        tool_call_id: str,
    ) -> Optional[Any]:
        """Get tool execution result."""
        client = await self._get_client()
        try:
            response = await client.get(
                f"/chat/tool-results/{session_id}/{tool_call_id}"
            )
            response.raise_for_status()
            data = response.json()
            return data.get("result")
        except httpx.HTTPStatusError as e:
            if e.response.status_code == 404:
                return None
            raise

    async def get_pending_tool_calls(
        self,
        session_id: str,
    ) -> list[dict]:
        """Get pending tool calls."""
        client = await self._get_client()
        response = await client.get(f"/chat/pending-tool-calls/{session_id}")
        response.raise_for_status()
        return response.json().get("tool_calls", [])

    async def save_pending_tool_call(
        self,
        session_id: str,
        tool_call: dict,
    ) -> bool:
        """Save a pending tool call."""
        client = await self._get_client()
        response = await client.post(
            f"/chat/pending-tool-calls/{session_id}",
            json=tool_call,
        )
        response.raise_for_status()
        return response.json().get("success", False)

    async def clear_pending_tool_calls(
        self,
        session_id: str,
    ) -> bool:
        """Clear pending tool calls for a session."""
        client = await self._get_client()
        response = await client.delete(f"/chat/pending-tool-calls/{session_id}")
        response.raise_for_status()
        return response.json().get("success", False)


class RemoteStorageProvider(StorageProvider):
    """Remote storage provider (calls Backend API)."""

    def __init__(
        self,
        base_url: str,
        auth_token: str,
        timeout: float = 30.0,
    ):
        """
        Initialize remote storage provider.

        Args:
            base_url: Backend internal API address
            auth_token: Internal Service Token
            timeout: Request timeout in seconds
        """
        self.base_url = base_url
        self.auth_token = auth_token
        self.timeout = timeout
        self._history = RemoteHistoryStore(base_url, auth_token, timeout)
        self._tool_results = RemoteToolResultStore(base_url, auth_token, timeout)
        self._initialized = False

    @property
    def history(self) -> HistoryStoreInterface:
        """Get history storage."""
        return self._history

    @property
    def tool_results(self) -> Optional[ToolResultStoreInterface]:
        """Get tool results storage."""
        return self._tool_results

    async def initialize(self) -> None:
        """Initialize storage (verify connectivity)."""
        # Test connection
        client = await self._history._get_client()
        try:
            response = await client.get("/health")
            response.raise_for_status()
            self._initialized = True
        except Exception as e:
            # Allow initialization even if health check fails
            # The actual requests will fail with better error messages
            self._initialized = True

    async def close(self) -> None:
        """Close storage connections."""
        await self._history.close()
        await self._tool_results.close()
        self._initialized = False

    async def health_check(self) -> dict:
        """Check storage health."""
        try:
            client = await self._history._get_client()
            response = await client.get("/health")
            response.raise_for_status()
            return {
                "status": "ok",
                "type": "remote",
                "base_url": self.base_url,
            }
        except Exception as e:
            return {
                "status": "error",
                "type": "remote",
                "base_url": self.base_url,
                "error": str(e),
            }
