"""
Peer-to-peer synchronization manager for FHEnom AI.

Provides functionality for synchronizing encrypted models between peer instances
through certificate-based mutual authentication.
"""

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

from .exceptions import SyncError, ConnectionError, APIError

logger = logging.getLogger(__name__)


class SyncManager:
    """
    Manager for peer-to-peer synchronization operations.
    
    This class provides methods for synchronizing encrypted model keys, artifacts, and metadata
    between FHEnom AI instances. Synchronization uses certificate-based mutual authentication
    to securely exchange encrypted data required for inference.
    
    Prerequisites:
        - Both peers must have certificates from the same Certificate Authority
        - Both peers must use the same client_id
        - Bidirectional network connectivity on admin port (default 9099)
        - Both peers must be properly provisioned and running
    
    Example:
        ```python
        # Initialize client
        client = FHEnomClient(admin_url="http://localhost:9099")
        
        # Start synchronization with peer
        result = client.admin.sync.start(
            peer_url="http://192.168.1.100",
            peer_port=9099
        )
        
        print(f"Synchronization job: {result['job_id']}")
        print(f"Status: {result['status']}")
        ```
    """
    
    def __init__(
        self,
        base_url: str,
        timeout: int = 30,
        verify_ssl: bool = True,
        auth_token: str = "default-auth-token-2026"
    ):
        """
        Initialize SyncManager.
        
        Args:
            base_url: Base URL of the admin interface (e.g., "http://localhost:9099")
            timeout: Request timeout in seconds
            verify_ssl: Whether to verify SSL certificates
            auth_token: Authentication token for X-Auth-Token header
        """
        self.base_url = base_url.rstrip('/')
        self.timeout = timeout
        self.verify_ssl = verify_ssl
        self.auth_token = auth_token
        
        logger.info(f"Initialized SyncManager for {self.base_url}")
    
    def _request(
        self,
        method: str,
        endpoint: str,
        **kwargs
    ) -> requests.Response:
        """
        Make HTTP request with error handling.
        
        Args:
            method: HTTP method (GET, POST, etc.)
            endpoint: API endpoint path
            **kwargs: Additional arguments for requests
            
        Returns:
            Response object
            
        Raises:
            SyncError: If the request fails
            ConnectionError: If connection fails
        """
        url = urljoin(self.base_url + '/', endpoint.lstrip('/'))
        
        # Add authentication header
        headers = kwargs.pop('headers', {})
        headers['X-Auth-Token'] = self.auth_token
        
        # Set timeout if not specified
        if 'timeout' not in kwargs:
            kwargs['timeout'] = self.timeout
        
        # Set SSL verification
        kwargs['verify'] = self.verify_ssl
        
        try:
            logger.debug(f"{method} {url}")
            response = requests.request(method, url, headers=headers, **kwargs)
            response.raise_for_status()
            return response
            
        except requests.exceptions.ConnectionError as e:
            raise ConnectionError(f"Failed to connect to {url}: {str(e)}")
        except requests.exceptions.Timeout as e:
            raise SyncError(f"Request timeout: {str(e)}")
        except requests.exceptions.HTTPError as e:
            error_msg = f"Sync request failed: {e.response.status_code}"
            try:
                error_data = e.response.json()
                error_msg += f" - {error_data.get('error', str(error_data))}"
            except:
                error_msg += f" - {e.response.text}"
            raise SyncError(error_msg)
        except Exception as e:
            raise SyncError(f"Unexpected error during sync operation: {str(e)}")
    
    def start(
        self,
        peer_url: str,
        peer_port: int = 9099
    ) -> Dict[str, Any]:
        """
        Initiate peer-to-peer synchronization with another FHEnom AI instance.
        
        This method starts the synchronization process with a peer instance. The process
        includes:
        1. Mutual authentication using certificates and challenge-response
        2. Exchange of encrypted model inventories
        3. Automatic exchange of missing encrypted keys, artifacts, and metadata
        
        After successful synchronization, both peers are registered in the cluster and will
        automatically exchange information about new encrypted models.
        
        Prerequisites:
            - Peer must be reachable at the specified URL and port
            - Both instances must have certificates from the same Certificate Authority
            - Both instances must use the same client_id
            - Bidirectional network connectivity (both peers must reach each other)
        
        Args:
            peer_url: Base URL of the peer instance (e.g., "http://192.168.1.100" or "https://peer.example.com")
            peer_port: Admin port of the peer instance (default: 9099)
            
        Returns:
            Dictionary containing synchronization result:
            - job_id: Synchronization job identifier
            - status: Job status ('completed', 'running', 'failed')
            - peer_identity: Identity information of the peer
            - models_synced: Number of models synchronized (if completed)
            - message: Status message
            
        Raises:
            SyncError: If synchronization fails
            ConnectionError: If peer is unreachable
            ValidationError: If parameters are invalid
            
        Example:
            ```python
            # Synchronize with a peer
            result = client.admin.sync.start(
                peer_url="http://192.168.1.100",
                peer_port=9099
            )
            
            if result['status'] == 'completed':
                print(f"Successfully synced {result['models_synced']} models")
                print(f"Peer identity: {result['peer_identity']}")
            ```
            
        Note:
            - Synchronization is bidirectional: both peers will exchange models
            - If either peer restarts, call this method again to re-establish the connection
            - Network firewalls must allow bidirectional traffic on the admin port
        """
        # Validate inputs
        if not peer_url:
            raise SyncError("peer_url cannot be empty")
        
        peer_url = peer_url.rstrip('/')
        
        # Construct full peer address
        if ':' in peer_url and not peer_url.startswith(('http://', 'https://')):
            # URL contains port, add http:// if missing
            peer_url = f"http://{peer_url}"
        
        peer_address = f"{peer_url}:{peer_port}"
        
        logger.info(f"Starting synchronization with peer: {peer_address}")
        
        try:
            # Call the admin sync start endpoint
            response = self._request(
                method='POST',
                endpoint='/admin/sync/start',
                json={
                    'peer_url': peer_address
                }
            )
            
            result = response.json()
            logger.info(f"Synchronization initiated successfully: {result.get('job_id')}")
            
            return result
            
        except (SyncError, ConnectionError):
            raise
        except Exception as e:
            raise SyncError(
                f"Failed to start synchronization with {peer_address}: {str(e)}",
                peer_url=peer_address
            )
