"""FlukeBase API client for communicating with the FlukeBase platform.

This is the minimal client version that proxies tool calls to the server.
All proprietary tool execution happens server-side.
"""

from dataclasses import dataclass
from datetime import datetime
from typing import TYPE_CHECKING, Any, Optional

from flukebase_connect.core.exceptions import (
    ApiConnectionError,
    AuthError,
    NotFoundError,
    ToolExecutionError,
)

if TYPE_CHECKING:
    pass


@dataclass
class Project:
    """Represents a FlukeBase project."""

    id: int
    name: str
    description: Optional[str]
    repository_url: Optional[str]
    framework: Optional[str]
    stage: Optional[str]
    created_at: datetime
    updated_at: datetime


@dataclass
class User:
    """Represents a FlukeBase user."""

    id: int
    email: str
    name: str
    project_count: int


class FlukeBaseClient:
    """Client for FlukeBase API communication.

    This minimal client provides:
    - Authentication and user management
    - Project listing
    - Tool execution proxy (tools run server-side)
    """

    def __init__(self, config):
        """Initialize the client with configuration."""
        self.base_url = config.flukebase.api_url
        self.token = config.flukebase.api_token
        self._client = None

    @property
    def is_configured(self) -> bool:
        """Check if the client is properly configured."""
        return bool(self.token)

    async def _get_client(self):
        """Get or create the HTTP client."""
        if self._client is None:
            import httpx

            self._client = httpx.AsyncClient(
                base_url=self.base_url,
                headers={
                    "Authorization": f"Bearer {self.token}",
                    "Content-Type": "application/json",
                    "User-Agent": "flukebase-connect-client/0.2.0",
                },
                timeout=60.0,  # Longer timeout for tool execution
            )
        return self._client

    async def _request(self, method: str, path: str, **kwargs) -> dict:
        """Make authenticated request."""
        import httpx

        if not self.is_configured:
            raise AuthError("FlukeBase API token not configured")

        try:
            client = await self._get_client()
            response = await client.request(method, path, **kwargs)

            if response.status_code == 401:
                raise AuthError("Invalid or expired API token")
            elif response.status_code == 403:
                raise AuthError("Access denied - insufficient permissions")
            elif response.status_code == 404:
                raise NotFoundError(f"Resource not found: {path}")
            elif response.status_code >= 500:
                raise ApiConnectionError(f"FlukeBase server error ({response.status_code})")

            response.raise_for_status()
            return response.json()

        except httpx.ConnectError as e:
            raise ApiConnectionError(f"Failed to connect to FlukeBase API: {e}")
        except httpx.TimeoutException as e:
            raise ApiConnectionError(f"Request timed out: {e}")
        except httpx.HTTPStatusError as e:
            raise ApiConnectionError(f"HTTP error: {e.response.status_code}")

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

    # === Authentication ===

    async def validate_token(self) -> dict:
        """Validate API token."""
        return await self._request("GET", "/flukebase_connect/auth/validate")

    async def get_current_user(self) -> User:
        """Get authenticated user info."""
        data = await self._request("GET", "/flukebase_connect/auth/me")
        user_data = data["user"]
        return User(
            id=user_data["id"],
            email=user_data["email"],
            name=user_data["name"],
            project_count=user_data["project_count"],
        )

    async def get_status(self) -> dict:
        """Get FlukeBase connection status."""
        if not self.is_configured:
            return {"connected": False, "project_id": None}

        try:
            user = await self.get_current_user()
            return {
                "connected": True,
                "user_email": user.email,
                "project_count": user.project_count,
            }
        except Exception:
            return {"connected": False, "project_id": None}

    # === Projects ===

    async def list_projects(self) -> list[Project]:
        """List all accessible projects."""
        data = await self._request("GET", "/flukebase_connect/projects")
        return [self._parse_project(p) for p in data["projects"]]

    async def get_project(self, project_id: int) -> Project:
        """Get project by ID."""
        data = await self._request("GET", f"/flukebase_connect/projects/{project_id}")
        return self._parse_project(data["project"])

    # === Tool Execution (Server-Side Proxy) ===

    async def execute_tool(
        self,
        tool_name: str,
        arguments: dict[str, Any],
        project_id: Optional[int] = None,
    ) -> dict:
        """Execute a tool on the FlukeBase server.

        All proprietary tools run server-side. This method sends the
        tool name and arguments to the server for execution.

        Args:
            tool_name: Name of the tool to execute (e.g., "mem_recall").
            arguments: Tool arguments dictionary.
            project_id: Optional project ID for context.

        Returns:
            Tool execution result from the server.

        Raises:
            ToolExecutionError: If tool execution fails.
            AuthError: If authentication fails.
            ApiConnectionError: If connection fails.
        """
        payload = {
            "tool": tool_name,
            "arguments": arguments,
        }
        if project_id:
            payload["project_id"] = project_id

        try:
            result = await self._request(
                "POST",
                "/mcp/execute",
                json=payload,
            )
            return result.get("result", {})
        except NotFoundError:
            raise ToolExecutionError(
                tool_name,
                "Tool not found",
                cause=f"The tool '{tool_name}' does not exist or is not available",
            )
        except ApiConnectionError as e:
            raise ToolExecutionError(
                tool_name,
                str(e),
                cause="Server-side execution failed",
            )

    async def list_available_tools(self) -> list[dict]:
        """List all available tools from the server.

        Returns:
            List of tool definitions with name, description, and parameters.
        """
        data = await self._request("GET", "/mcp/tools")
        return data.get("tools", [])

    # === Helpers ===

    def _parse_project(self, data: dict) -> Project:
        """Parse project data from API response."""
        return Project(
            id=data["id"],
            name=data["name"],
            description=data.get("description"),
            repository_url=data.get("repository_url"),
            framework=data.get("detected_framework"),
            stage=data.get("stage"),
            created_at=datetime.fromisoformat(data["created_at"].replace("Z", "+00:00")),
            updated_at=datetime.fromisoformat(data["updated_at"].replace("Z", "+00:00")),
        )
