"""
HTTP client for communicating with AgentNEX Backend

Authentication: User API Token (X-API-Key Header)
- Uses X-API-Key: <token> header (custom header to avoid Cloud Run/LB stripping)
- Token is user-scoped: devices are filtered by user's role and assignments
- Token is obtained from AGENTNEX_API_KEY environment variable

Security:
- Every request is tied to a specific user
- Users only see devices assigned to them (unless SuperAdmin/OrgAdmin)
- Complete audit trail on backend
"""

import asyncio
import logging
from typing import Any, Dict, List, Optional
from urllib.parse import urljoin

import httpx
from pydantic import BaseModel

from app.core.config import settings
from app.core.exceptions import (
    BackendConnectionError,
    AuthenticationError,
    DeviceNotFoundError,
    MCPServerError
)


logger = logging.getLogger(__name__)


class BackendClient:
    """HTTP client for AgentNEX Backend internal APIs
    
    Uses X-API-Key header authentication for user-scoped access.
    The token (from AGENTNEX_API_KEY) must be a valid user API token
    created via the backend's /api/auth/api-tokens/ endpoint.
    """
    
    def __init__(self):
        self.base_url = settings.agentnex_backend_url.rstrip('/')
        self.api_token = settings.agentnex_api_key  # User API token
        self.timeout = settings.agentnex_backend_timeout
        self.retry_attempts = settings.agentnex_backend_retry_attempts
        self.retry_delay = settings.agentnex_backend_retry_delay
        
        # HTTP client configuration - X-API-Key header authentication
        self.headers = {
            "X-API-Key": self.api_token,
            "Content-Type": "application/json",
            "User-Agent": f"{settings.agentnex_mcp_server_name}/{settings.agentnex_mcp_server_version}"
        }
        
        # Log token info (masked) for debugging
        if self.api_token and self.api_token != "test-api-key":
            masked = self.api_token[:16] + "..." if len(self.api_token) > 16 else "***"
            logger.info(f"BackendClient initialized with token: {masked}")
        else:
            logger.warning(f"BackendClient initialized with default/missing token! Set AGENTNEX_API_KEY environment variable.")
        
        # Log the X-API-Key header that will be sent
        logger.info(f"X-API-Key header configured: {self.api_token[:10]}..." if self.api_token else "NO TOKEN")
        
    async def _make_request(
        self, 
        method: str, 
        endpoint: str, 
        **kwargs
    ) -> Dict[str, Any]:
        """Make HTTP request with retry logic.

        Endpoints are relative to base_url (e.g. /api/health, /api/internal/devices).
        base_url must be the backend origin only (no path), e.g. https://host.run.app
        """
        url = urljoin(f"{self.base_url}/", endpoint.lstrip('/'))
        
        # Debug: Log headers being sent (mask token)
        debug_headers = {k: (v[:20] + "..." if k == "X-API-Key" and len(v) > 20 else v) for k, v in self.headers.items()}
        logger.debug(f"Making request to {url} with headers: {debug_headers}")
        
        for attempt in range(self.retry_attempts + 1):
            try:
                async with httpx.AsyncClient(timeout=self.timeout) as client:
                    response = await client.request(
                        method=method,
                        url=url,
                        headers=self.headers,
                        **kwargs
                    )
                    
                    # Handle HTTP errors
                    if response.status_code == 401:
                        raise AuthenticationError(
                            "Invalid or expired API token. "
                            "Ensure AGENTNEX_API_KEY contains a valid user API token "
                            "created via /api/auth/api-tokens/"
                        )
                    elif response.status_code == 404:
                        raise DeviceNotFoundError("Device not found or offline")
                    elif response.status_code >= 400:
                        error_msg = f"HTTP {response.status_code}: {response.text}"
                        raise BackendConnectionError(error_msg)
                    
                    return response.json()
                    
            except httpx.TimeoutException:
                if attempt < self.retry_attempts:
                    logger.warning(f"Request timeout, retrying in {self.retry_delay}s (attempt {attempt + 1})")
                    await asyncio.sleep(self.retry_delay)
                    continue
                raise BackendConnectionError("Request timeout after retries")
                
            except httpx.ConnectError:
                if attempt < self.retry_attempts:
                    logger.warning(f"Connection failed, retrying in {self.retry_delay}s (attempt {attempt + 1})")
                    await asyncio.sleep(self.retry_delay)
                    continue
                raise BackendConnectionError("Failed to connect to backend")
                
            except Exception as e:
                if attempt < self.retry_attempts:
                    logger.warning(f"Request failed: {e}, retrying in {self.retry_delay}s")
                    await asyncio.sleep(self.retry_delay)
                    continue
                raise MCPServerError(f"Backend request failed: {e}")
    
    async def get_devices(self, organization_id: Optional[str] = None) -> List[Dict[str, Any]]:
        """Get list of devices
        
        Uses /api/devices which respects user's role:
        - SuperAdmin: sees all devices
        - OrgAdmin: sees all devices in their organization
        - User: sees only devices assigned to them
        """
        params = {"fast": "true"}  # Use fast mode for basic listing
        if organization_id:
            params["organization_id"] = organization_id
            
        # Use user-authenticated endpoint instead of internal API
        response = await self._make_request("GET", "/api/devices", params=params)
        # /api/devices returns a list directly, not wrapped in "devices" key
        if isinstance(response, list):
            return response
        return response.get("devices", response)
    
    async def get_device(self, device_id: str) -> Dict[str, Any]:
        """Get device information"""
        response = await self._make_request("GET", f"/api/devices/{device_id}")
        return response
    
    async def get_device_telemetry(self, device_id: str) -> Dict[str, Any]:
        """Get device telemetry data"""
        response = await self._make_request("GET", f"/api/internal/devices/{device_id}/telemetry")
        return response
    
    async def get_device_processes(self, device_id: str) -> List[Dict[str, Any]]:
        """Get device process list"""
        response = await self._make_request("GET", f"/api/internal/devices/{device_id}/processes")
        return response.get("processes", [])
    
    async def get_device_events(self, device_id: str, limit: int = 100) -> List[Dict[str, Any]]:
        """Get device event logs"""
        params = {"limit": limit}
        response = await self._make_request("GET", f"/api/internal/devices/{device_id}/events", params=params)
        return response.get("events", [])
    
    async def execute_action(self, device_id: str, action_data: Dict[str, Any]) -> Dict[str, Any]:
        """Execute action on device"""
        response = await self._make_request(
            "POST", 
            f"/api/internal/devices/{device_id}/actions",
            json=action_data
        )
        return response
    
    async def get_device_status(self, device_id: str) -> Dict[str, Any]:
        """Get device status"""
        response = await self._make_request("GET", f"/api/internal/devices/{device_id}/status")
        return response
    
    async def health_check(self) -> bool:
        """Check if backend is healthy"""
        try:
            await self._make_request("GET", "/api/health")
            return True
        except Exception:
            return False
    
    async def get_current_user(self) -> Dict[str, Any]:
        """Get information about the authenticated user
        
        Useful for verifying token authentication and debugging.
        Returns user ID, email, role, organization, etc.
        """
        response = await self._make_request("GET", "/api/internal/me")
        return response