"""HTTP client for the Savvy API with retry logic."""

from __future__ import annotations

import time
from typing import Any

import httpx

from savvy._config import get_api_key, get_api_url
from savvy.exceptions import (
    APIError,
    AuthenticationError,
    DataNotFoundError,
    RateLimitError,
    ValidationError,
)

# Default retry configuration
DEFAULT_MAX_RETRIES = 3
DEFAULT_RETRY_DELAY = 1.0  # seconds
DEFAULT_TIMEOUT = 60.0  # seconds


def call_tool(
    tool_name: str,
    *,
    max_retries: int = DEFAULT_MAX_RETRIES,
    retry_delay: float = DEFAULT_RETRY_DELAY,
    timeout: float = DEFAULT_TIMEOUT,
    **kwargs: Any,
) -> dict[str, Any]:
    """
    Call a Savvy tool via REST API with retry logic.

    Args:
        tool_name: Name of the tool to call (e.g., "economic_data", "stock_data").
        max_retries: Maximum number of retry attempts for transient failures.
        retry_delay: Delay between retries in seconds.
        timeout: Request timeout in seconds.
        **kwargs: Tool-specific arguments.

    Returns:
        The tool response as a dictionary.

    Raises:
        AuthenticationError: If API key is invalid or missing.
        RateLimitError: If rate limit is exceeded.
        DataNotFoundError: If requested data is not found.
        ValidationError: If input validation fails.
        APIError: For other API errors.
    """
    api_key = get_api_key()
    api_url = get_api_url()
    url = f"{api_url}/api/tools/{tool_name}"

    last_exception: Exception | None = None

    for attempt in range(max_retries):
        try:
            with httpx.Client(timeout=timeout) as client:
                response = client.post(
                    url,
                    json=kwargs,
                    headers={"X-API-Key": api_key},
                )

                # Handle HTTP errors
                if response.status_code == 401:
                    raise AuthenticationError("Invalid API key")
                elif response.status_code == 403:
                    raise AuthenticationError("Access denied")
                elif response.status_code == 404:
                    raise DataNotFoundError(
                        f"Tool '{tool_name}' not found or data not available"
                    )
                elif response.status_code == 422:
                    error_detail = _extract_error(response)
                    raise ValidationError(error_detail)
                elif response.status_code == 429:
                    retry_after = response.headers.get("Retry-After")
                    retry_seconds = int(retry_after) if retry_after else None
                    raise RateLimitError(retry_after=retry_seconds)
                elif response.status_code >= 500:
                    # Server error - retry
                    error_detail = _extract_error(response)
                    raise APIError(error_detail, status_code=response.status_code)
                elif response.status_code >= 400:
                    # Client error - don't retry
                    error_detail = _extract_error(response)
                    raise APIError(error_detail, status_code=response.status_code)

                return response.json()

        except (httpx.TimeoutException, httpx.ConnectError) as e:
            last_exception = APIError(f"Connection error: {e}")
            if attempt < max_retries - 1:
                time.sleep(retry_delay * (attempt + 1))  # Exponential backoff
                continue
            raise last_exception

        except RateLimitError as e:
            last_exception = e
            if attempt < max_retries - 1:
                wait_time = e.retry_after or retry_delay * (attempt + 1)
                time.sleep(wait_time)
                continue
            raise

        except APIError as e:
            if e.status_code and e.status_code >= 500:
                # Retry server errors
                last_exception = e
                if attempt < max_retries - 1:
                    time.sleep(retry_delay * (attempt + 1))
                    continue
            raise

    # Should not reach here, but just in case
    if last_exception:
        raise last_exception
    raise APIError("Unknown error")


def _extract_error(response: httpx.Response) -> str:
    """Extract error message from API response."""
    try:
        data = response.json()
        if isinstance(data, dict):
            # Try common error field names
            for field in ["detail", "error", "message"]:
                if field in data:
                    return str(data[field])
        return str(data)
    except Exception:
        return response.text or f"HTTP {response.status_code}"


def extract_data_from_response(response: dict[str, Any]) -> list[dict[str, Any]]:
    """
    Extract data records from a tool response.

    The API returns responses in MCP format:
    {"content": [{"type": "text", "text": "markdown with JSON"}]}

    This function extracts the JSON data from the markdown.

    Args:
        response: The raw API response.

    Returns:
        List of data records.
    """
    import json
    import re

    # Extract text content from MCP response format
    text = ""
    if "content" in response:
        for item in response.get("content", []):
            if item.get("type") == "text":
                text = item.get("text", "")
                break
    else:
        # Direct response (not wrapped)
        return response.get("data", [])

    # Try to extract JSON from markdown code block
    json_match = re.search(r"```(?:json)?\s*\n?(.*?)\n?```", text, re.DOTALL)
    if json_match:
        try:
            data = json.loads(json_match.group(1))
            if isinstance(data, list):
                return data
            if isinstance(data, dict) and "data" in data:
                return data["data"]
            return [data]
        except json.JSONDecodeError:
            pass

    # Try to parse raw JSON
    try:
        data = json.loads(text)
        if isinstance(data, list):
            return data
        if isinstance(data, dict) and "data" in data:
            return data["data"]
        return [data]
    except json.JSONDecodeError:
        pass

    return []


def extract_metadata_from_response(response: dict[str, Any]) -> dict[str, Any]:
    """
    Extract metadata from a tool response.

    Args:
        response: The raw API response.

    Returns:
        Metadata dictionary.
    """
    import json
    import re

    # Try to get metadata from response directly
    if "metadata" in response:
        return response["metadata"]

    # Extract from MCP response format
    text = ""
    if "content" in response:
        for item in response.get("content", []):
            if item.get("type") == "text":
                text = item.get("text", "")
                break

    # Look for metadata in the text
    if "Data ID:" in text:
        # Extract data_id from markdown
        match = re.search(r"Data ID:\s*`?([a-f0-9-]+)`?", text)
        if match:
            return {"data_id": match.group(1)}

    return {}
