import httpx
import uuid
from typing import Dict, Any, Optional
from .exceptions import (
    AuthenticationError,
    NetworkError,
    RateLimitError,
    PolicyDeniedError,
    IdempotencyError,
    AgentRevokedError
)
from .retry import retry_with_backoff
from .config import AuthevoConfig


class AuthevoClient:
    """
    HTTP client for Authevo API with automatic retries and error handling.
    """
    
    SDK_VERSION = "1.0.0"
    
    def __init__(
        self, 
        config: Optional[AuthevoConfig] = None,
        base_url: Optional[str] = None
    ):
        """
        Initialize Authevo API client.
        
        Args:
            config: AuthevoConfig instance (optional, will use from_env if not provided)
            base_url: Deprecated, use config.api_url instead
        """
        if config is None:
            if base_url:
                # Backward compatibility
                from .config import AuthevoConfig
                config = AuthevoConfig(api_url=base_url)
            else:
                config = AuthevoConfig.from_env()
        
        self.config = config
        self.base_url = config.api_url
        
        # Create HTTP client with timeout
        self.client = httpx.AsyncClient(
            base_url=self.base_url,
            timeout=config.timeout,
            verify=config.verify_ssl
        )
    
    async def close(self):
        """Close HTTP client connection."""
        await self.client.aclose()
    
    def _get_headers(self, idempotency_key: Optional[str] = None) -> Dict[str, str]:
        """Build request headers with auth and SDK version."""
        headers = {
            "X-Authevo-SDK-Version": f"python/{self.SDK_VERSION}",
            "User-Agent": f"authevo-python/{self.SDK_VERSION}"
        }
        
        if self.config.api_key:
            headers["Authorization"] = f"Bearer {self.config.api_key}"
        
        if idempotency_key:
            headers["X-Idempotency-Key"] = idempotency_key
        
        return headers
    
    def _handle_error_response(self, response: httpx.Response):
        """Parse API error response and raise appropriate exception."""
        status = response.status_code
        
        try:
            error_data = response.json()
            error_type = error_data.get("error", "unknown")
            message = error_data.get("message", response.text)
        except:
            error_type = "unknown"
            message = response.text or f"HTTP {status}"
        
        # Authentication errors
        if status == 401:
            raise AuthenticationError(message)
        
        # Rate limiting
        elif status == 429:
            retry_after = int(response.headers.get("Retry-After", 60))
            raise RateLimitError(retry_after)
        
        # Policy denied
        elif status == 400 and error_type == "policy_denied":
            raise PolicyDeniedError(
                reason=error_data.get("reason", "Policy denied"),
                policy_id=error_data.get("policy_id", "unknown")
            )
        
        # Idempotency conflict
        elif status == 409 and error_type == "idempotency_conflict":
            raise IdempotencyError(message)
        
        # Agent revoked
        elif status == 403 and error_type == "agent_revoked":
            raise AgentRevokedError(error_data.get("did", "unknown"))
        
        # Network/server errors
        elif status >= 500:
            raise NetworkError(f"Server error ({status}): {message}")
        
        # Client errors
        elif status >= 400:
            raise NetworkError(f"Client error ({status}): {message}")
        
        # Unknown
        else:
            raise NetworkError(f"Unexpected error ({status}): {message}")
    
    @retry_with_backoff(max_retries=3, base_delay=2.0)
    async def deploy_agent(
        self, 
        name: str, 
        did: str, 
        public_key: str, 
        policy_id: str = "refund_v1"
    ) -> Dict[str, Any]:
        """
        Register a new agent with the API.
        
        Args:
            name: Agent name
            did: Decentralized identifier
            public_key: Base64-encoded public key
            policy_id: Policy identifier
        
        Returns:
            API response with agent details
        
        Raises:
            AuthenticationError: Invalid API key
            NetworkError: Connection or server error
        """
        try:
            response = await self.client.post(
                "/deploy",
                json={
                    "name": name,
                    "did": did,
                    "publicKey": public_key,
                    "policyId": policy_id
                },
                headers=self._get_headers()
            )
            
            if response.status_code >= 400:
                self._handle_error_response(response)
            
            return response.json()
            
        except httpx.RequestError as e:
            raise NetworkError(f"Connection error: {str(e)}")
    
    @retry_with_backoff(max_retries=3, base_delay=2.0)
    async def submit_action(
        self, 
        action_body: Dict[str, Any],
        idempotency_key: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Submit a signed action to the API.
        
        Args:
            action_body: Complete action object with proof
            idempotency_key: Optional idempotency key (auto-generated if not provided)
        
        Returns:
            API response with action ID and verification status
        
        Raises:
            AuthenticationError: Invalid API key
            PolicyDeniedError: Action denied by policy
            IdempotencyError: Idempotency key conflict
            NetworkError: Connection or server error
        """
        # Auto-generate idempotency key if not provided
        if not idempotency_key:
            idempotency_key = str(uuid.uuid4())
        
        try:
            response = await self.client.post(
                "/actions",
                json=action_body,
                headers=self._get_headers(idempotency_key=idempotency_key)
            )
            
            if response.status_code >= 400:
                self._handle_error_response(response)
            
            return response.json()
            
        except httpx.RequestError as e:
            raise NetworkError(f"Connection error: {str(e)}")
    
    @retry_with_backoff(max_retries=3, base_delay=2.0)
    async def verify_action(self, action_id: str) -> Dict[str, Any]:
        """
        Verify an action by ID.
        
        Args:
            action_id: Action identifier
        
        Returns:
            Verification result with valid/invalid status
        
        Raises:
            AuthenticationError: Invalid API key
            NetworkError: Connection or server error
        """
        try:
            response = await self.client.get(
                f"/verify/{action_id}",
                headers=self._get_headers()
            )
            
            if response.status_code >= 400:
                self._handle_error_response(response)
            
            return response.json()
            
        except httpx.RequestError as e:
            raise NetworkError(f"Connection error: {str(e)}")
