#!/usr/bin/env python3
"""
HTTP client for CAST.AI External MCP Server.

Handles authentication with API key and makes requests to the CAST.AI API.
"""

from typing import Any, cast

import httpx

from src.config import API_BASE_URL, API_KEY
from src.logger import logger


def get_headers() -> dict[str, str]:
    """
    Get HTTP headers with API key authentication.

    Returns:
        Dictionary of HTTP headers with API key authentication
    """
    return {
        "Content-Type": "application/json",
        "Accept": "application/json",
        "X-API-Key": API_KEY,
    }


async def make_request(
    method: str,
    path: str,
    params: dict[str, Any] | None = None,
    body: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """
    Make HTTP request to CAST.AI API.

    Args:
        method: HTTP method
        path: API endpoint path
        params: Query parameters
        body: Request body

    Returns:
        Response data as dictionary
    """
    url = f"{API_BASE_URL}{path}"
    headers = get_headers()

    try:
        async with httpx.AsyncClient(timeout=30.0) as client:
            response = await client.request(
                method=method,
                url=url,
                headers=headers,
                params=params,
                json=body if body else None,
            )
            response.raise_for_status()

            # Handle empty responses
            if response.status_code == 204 or not response.content:
                return {"status": "success", "status_code": response.status_code}

            return cast(dict[str, Any], response.json())
    except httpx.HTTPStatusError as e:
        # Provide helpful authentication guidance for 401 errors
        if e.response.status_code == 401:
            auth_error_msg = (
                "Authentication failed. Your API key may be invalid or expired.\n\n"
                "To fix this:\n"
                "1. Visit https://console.cast.ai\n"
                "2. Navigate to Settings > API Keys\n"
                "3. Generate a new API key\n"
                "4. Update your CASTAI_API_KEY environment variable\n"
                "5. Restart Claude Desktop\n"
            )
            logger.error("Authentication failed (401)", method=method, path=path)
            raise Exception(auth_error_msg) from e

        # Try to extract error details from response body
        error_detail = str(e)
        try:
            error_body = e.response.json()
            if "message" in error_body:
                error_detail = error_body["message"]
            elif "error" in error_body:
                error_detail = error_body["error"]
            else:
                error_detail = str(error_body)
        except Exception:
            # If we can't parse JSON, use the text response
            try:
                error_detail = e.response.text or str(e)
            except Exception:
                error_detail = str(e)

        logger.error("API request failed", method=method, path=path, status_code=e.response.status_code, error=error_detail)
        raise Exception(f"API request failed ({e.response.status_code}): {error_detail}") from e
    except Exception as e:
        logger.error("API request error", method=method, path=path, error=str(e))
        raise


async def make_paginated_request(
    method: str,
    path: str,
    params: dict[str, Any] | None = None,
    body: dict[str, Any] | None = None,
    page_limit: int | None = None,
    max_pages: int | None = None,
    auto_paginate: bool = False,
) -> dict[str, Any]:
    """
    Make HTTP request to CAST.AI API with pagination support.

    CAST.AI uses cursor-based pagination with:
    - Request params: page.cursor and page.limit
    - Response fields: items/workloads (array) and nextCursor/nextPageCursor (string)

    Args:
        method: HTTP method
        path: API path
        params: Query parameters
        body: Request body
        page_limit: Number of items per page (page.limit)
        max_pages: Maximum number of pages to fetch (None = fetch all)
        auto_paginate: If True, automatically fetch all pages and combine results

    Returns:
        Response dict with items/workloads array and pagination info
    """
    params = params or {}
    all_items = []
    page_count = 0
    next_cursor = None
    items_key = None  # Track which key contains the array (items or workloads)

    # Set page limit if provided
    if page_limit:
        params["page.limit"] = page_limit

    while True:
        # Add cursor to params if we have one
        if next_cursor:
            params["page.cursor"] = next_cursor

        # Make the request
        response = await make_request(method, path, params, body)

        # Extract items (handle both "items" and "workloads" keys)
        if "items" in response:
            items_key = "items"
            items = response["items"]
        elif "workloads" in response:
            items_key = "workloads"
            items = response["workloads"]
        else:
            items = []

        all_items.extend(items)
        page_count += 1

        # Check for next cursor (handle both nextCursor and nextPageCursor)
        next_cursor = response.get("nextCursor") or response.get("nextPageCursor")

        # Stop if no auto-pagination or reached max pages or no more pages
        if not auto_paginate or not next_cursor:
            # Return original response if not auto-paginating
            if not auto_paginate:
                return response
            break

        if max_pages and page_count >= max_pages:
            break

    # Return combined results for auto-pagination
    result = {
        "totalItems": len(all_items),
        "pageCount": page_count,
        "hasMore": bool(next_cursor),
        "nextCursor": next_cursor,
    }

    # Use the same key that was in the original response
    if items_key:
        result[items_key] = all_items
    else:
        result["items"] = all_items

    return result
