"""
AnyCost Stream API client for direct billing data uploads.

Handles HTTP communication with CloudZero AnyCost Stream API for
immediate data ingestion without S3 intermediate storage.
"""

import json
import requests
from typing import Dict, Any, List
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type


class AnyCostAPIError(Exception):
    """Custom exception for AnyCost API errors"""
    pass


class AnyCostClient:
    """
    HTTP client for CloudZero AnyCost Stream API.

    Single Responsibility: Make HTTP POST requests to AnyCost endpoints
    with retry logic and error handling.
    """

    ANYCOST_ENDPOINT = "/v2/connections/billing/anycost"

    def __init__(self, api_key: str, base_url: str = "https://api.cloudzero.com"):
        """
        Initialize AnyCost API client.

        Args:
            api_key: CloudZero organization API key
            base_url: CloudZero API base URL
        """
        self.api_key = api_key
        self.base_url = base_url.rstrip("/")
        self.session = requests.Session()
        self.session.headers.update({
            "Authorization": f"Bearer {api_key}",
            "accept": "application/json",
            "content-type": "application/json"
        })

    @retry(
        stop=stop_after_attempt(3),
        wait=wait_exponential(multiplier=1, min=2, max=10),
        retry=retry_if_exception_type((requests.exceptions.RequestException,)),
        reraise=True
    )
    def post_billing_drop(
        self,
        connection_id: str,
        payload: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        POST billing data to AnyCost Stream API.

        Args:
            connection_id: CloudZero connection ID
            payload: Billing data payload with month, operation, and data

        Returns:
            API response dictionary

        Raises:
            AnyCostAPIError: If request fails after retries
        """
        endpoint = f"{self.ANYCOST_ENDPOINT}/{connection_id}/billing_drops"
        url = f"{self.base_url}{endpoint}"

        try:
            response = self.session.post(url, json=payload, timeout=60)

            # Check status before raise_for_status() to capture response details
            if not response.ok:
                raise self._create_http_error(response, connection_id)

            return response.json()
        except requests.exceptions.Timeout as e:
            raise AnyCostAPIError(
                f"AnyCost API timeout after 60s for connection {connection_id}"
            ) from e
        except AnyCostAPIError:
            # Re-raise our custom errors
            raise
        except requests.exceptions.RequestException as e:
            raise AnyCostAPIError(f"AnyCost API request failed: {str(e)}") from e

    def _create_http_error(self, response: requests.Response, connection_id: str) -> AnyCostAPIError:
        """Create detailed error from HTTP response."""
        status_code = response.status_code

        # Extract detailed response information
        response_text = response.text
        response_headers = dict(response.headers)

        # Try to parse JSON error response
        error_details = None
        try:
            error_details = response.json()
        except (ValueError, json.JSONDecodeError):
            # Not JSON, use text
            pass

        # Build detailed error message
        error_parts = [f"AnyCost API error ({status_code})"]

        if error_details:
            # Structured error from CloudZero
            error_parts.append(f"\n📋 Error Details: {json.dumps(error_details, indent=2)}")
        else:
            # Raw text response
            error_parts.append(f"\n📋 Response: {response_text}")

        # Add relevant headers
        if response_headers:
            relevant_headers = {k: v for k, v in response_headers.items()
                              if k.lower() in ['content-type', 'x-request-id', 'x-error-code']}
            if relevant_headers:
                error_parts.append(f"\n📋 Headers: {json.dumps(relevant_headers, indent=2)}")

        error_message = "".join(error_parts)

        if status_code == 429:
            return AnyCostAPIError(
                f"Rate limit exceeded for connection {connection_id}. "
                f"Try using multiple connections or --method s3.\n{error_message}"
            )
        elif status_code >= 500:
            return AnyCostAPIError(
                f"AnyCost API server error for connection {connection_id}.\n{error_message}"
            )
        else:
            return AnyCostAPIError(
                f"AnyCost API request failed for connection {connection_id}.\n{error_message}"
            )

    def estimate_payload_size_mb(self, payload: Dict[str, Any]) -> float:
        """
        Estimate payload size in MB.

        Args:
            payload: Billing data payload

        Returns:
            Estimated size in MB
        """
        json_bytes = json.dumps(payload).encode("utf-8")
        return len(json_bytes) / (1024 * 1024)
