"""
Admin API client for FHEnom AI operations.

Provides access to all Admin API endpoints (port 9099):
- Health monitoring (liveness, readiness, performance status)
- TEE attestation and verification
- Model discovery and management
- Model and dataset encryption
- Serving control
- Job monitoring
- Account management (placeholder)
- SFTP management (admin-only access)
- Peer-to-peer synchronization (advanced features)
"""

import requests
import time
from typing import Dict, Any, Optional, List, Callable, TYPE_CHECKING
from urllib.parse import quote
import logging

from .exceptions import (
    APIError,
    ConnectionError,
    JobError,
    ModelNotFoundError,
    TimeoutError,
)

if TYPE_CHECKING:
    from .sftp_manager import SFTPManager
    from .sync import SyncManager

logger = logging.getLogger(__name__)


class AdminAPI:
    """Client for FHEnom AI Admin API (port 9099)."""
    
    def __init__(
        self,
        base_url: str,
        timeout: int = 30,
        verify_ssl: bool = True,
        max_retries: int = 3,
        sftp_manager: Optional['SFTPManager'] = None,
        auth_token: str = "default-auth-token-2026"
    ):
        """
        Initialize Admin API client.
        
        Args:
            base_url: Base URL with port, e.g., "http://192.168.1.100:9099"
            timeout: Request timeout in seconds
            verify_ssl: Whether to verify SSL certificates
            max_retries: Maximum number of retry attempts
            sftp_manager: Optional SFTP manager instance (set by FHEnomClient)
            auth_token: Authentication token for X-Auth-Token header
        """
        self.base_url = base_url.rstrip('/')
        self.timeout = timeout
        self.verify_ssl = verify_ssl
        self.max_retries = max_retries
        self._sftp_manager = sftp_manager
        self._sync_manager = None  # Lazy initialization
        self.auth_token = auth_token
        
        logger.info(f"Initialized Admin API client: {self.base_url}")
    
    @property
    def sftp(self) -> 'SFTPManager':
        """
        Access SFTP manager for TEE directory operations.
        
        Returns:
            SFTPManager instance
        
        Raises:
            RuntimeError: If SFTP manager not configured
        
        Example:
            ```python
            # List files in upload directory
            files = client.admin.sftp.list_upload_directory()
            
            # Clear download directory
            client.admin.sftp.clear_download_directory()
            ```
        """
        if self._sftp_manager is None:
            raise RuntimeError(
                "SFTP manager not configured. Initialize FHEnomClient with SFTP enabled."
            )
        return self._sftp_manager
    
    @property
    def sync(self) -> 'SyncManager':
        """
        Access peer-to-peer synchronization manager.
        
        Provides methods for synchronizing encrypted models between peer instances
        through certificate-based mutual authentication.
        
        Returns:
            SyncManager instance
        
        Example:
            ```python
            # Synchronize with a peer instance
            result = client.admin.sync.start(
                peer_url="http://192.168.1.100",
                peer_port=9099
            )
            
            print(f"Synchronized {result['models_synced']} models")
            ```
        
        See Also:
            :class:`~fhenomai.sync.SyncManager`: Complete synchronization API reference
        """
        if self._sync_manager is None:
            # Lazy initialization
            from .sync import SyncManager
            self._sync_manager = SyncManager(
                base_url=self.base_url,
                timeout=self.timeout,
                verify_ssl=self.verify_ssl,
                auth_token=self.auth_token
            )
        return self._sync_manager
    
    def _request(
        self,
        method: str,
        endpoint: str,
        raise_for_status: bool = True,
        **kwargs
    ) -> requests.Response:
        """
        Make HTTP request with error handling and retries.
        
        Args:
            method: HTTP method (GET, POST, etc.)
            endpoint: API endpoint
            raise_for_status: Whether to raise exception on HTTP errors
            **kwargs: Additional arguments for requests.request()
        
        Returns:
            Response object
        
        Raises:
            ConnectionError: If connection fails
            APIError: If API returns error
        """
        url = f"{self.base_url}{endpoint}"
        kwargs.setdefault('timeout', self.timeout)
        kwargs.setdefault('verify', self.verify_ssl)
        
        # Add authentication header
        if 'headers' not in kwargs:
            kwargs['headers'] = {}
        kwargs['headers']['X-Auth-Token'] = self.auth_token
        
        last_exception = None
        
        for attempt in range(self.max_retries):
            try:
                logger.debug(f"{method} {url} (attempt {attempt + 1}/{self.max_retries})")
                response = requests.request(method, url, **kwargs)
                
                if raise_for_status:
                    response.raise_for_status()
                
                return response
                
            except requests.exceptions.ConnectionError as e:
                last_exception = e
                if attempt < self.max_retries - 1:
                    time.sleep(2 ** attempt)  # Exponential backoff
                    continue
                raise ConnectionError(f"Failed to connect to {url}: {e}")
                
            except requests.exceptions.HTTPError as e:
                # Don't retry on 4xx errors (client errors)
                if e.response.status_code < 500:
                    raise APIError(
                        f"API error: {e.response.status_code} - {e.response.text}",
                        status_code=e.response.status_code,
                        response=e.response.json() if e.response.text else None
                    )
                # Retry on 5xx errors (server errors)
                last_exception = e
                if attempt < self.max_retries - 1:
                    time.sleep(2 ** attempt)
                    continue
                raise APIError(f"Server error: {e}", status_code=e.response.status_code)
                
            except requests.exceptions.Timeout as e:
                last_exception = e
                if attempt < self.max_retries - 1:
                    continue
                raise TimeoutError(f"Request timeout: {e}")
                
            except requests.exceptions.RequestException as e:
                raise APIError(f"Request failed: {e}")
        
        # Should never reach here, but just in case
        raise APIError(f"Request failed after {self.max_retries} attempts: {last_exception}")
    
    # ========================================================================
    # Health Monitoring
    # ========================================================================
    
    def health_live(self) -> Dict[str, str]:
        """
        Check if the service is alive and responding.
        
        This is a lightweight liveness check that requires no authentication.
        Use this for basic availability monitoring.
        
        Returns:
            Dict with status (always {"status": "alive"} if responding)
        
        Example:
            >>> status = api.health_live()
            >>> print(status)  # {"status": "alive"}
        """
        response = self._request("GET", "/admin/health/live")
        return response.json()
    
    def health_ready(self) -> Dict[str, Any]:
        """
        Check readiness of system subsystems.
        
        Returns detailed status of:
        - tpm: TPM device availability and provisioning status
        - tee: TEE device availability and attestation capability
        - model_server: FHEnom AI server status
        
        Returns:
            Dict with subsystem statuses
        
        Example:
            >>> readiness = api.health_ready()
            >>> for subsystem, status in readiness.items():
            ...     print(f"{subsystem}: {status}")
            # Output:
            # tpm: available
            # tee: available
            # model_server: available
        """
        response = self._request("GET", "/admin/health/ready")
        return response.json()
    
    def health_status(self) -> Dict[str, Any]:
        """
        Get performance metrics and system status.
        
        Returns performance metrics using Exponential Weighted Moving Average (EWMA):
        - aggregate: Overall metrics across all models
        - per_model: Metrics for each individual model
        - timestamp: Snapshot timestamp
        
        Metrics include:
        - latency: Average request latency (EWMA)
        - tokens_per_second: Token generation rate (EWMA)
        - total_requests: Total request count
        - last_seen: Last request timestamp
        
        Note:
            - Metrics are non-persistent and reset on service restart
            - Idle models (>1 day) are automatically evicted from metrics
        
        Returns:
            Dict with performance metrics
        
        Example:
            >>> status = api.health_status()
            >>> print(f"Aggregate latency: {status['aggregate']['latency']:.2f}ms")
            >>> print(f"Aggregate tokens/sec: {status['aggregate']['tokens_per_second']:.2f}")
            >>> 
            >>> # Per-model metrics
            >>> for model_id, metrics in status['per_model'].items():
            ...     print(f"{model_id}: {metrics['total_requests']} requests")
        """
        response = self._request("GET", "/admin/health/status")
        return response.json()
    
    def attestation(self, nonce_hex: str) -> bytes:
        """
        Request TEE attestation report with provided nonce.
        
        This endpoint generates a TEE attestation report that can be verified
        to confirm the service is running in a genuine Trusted Execution Environment.
        
        Args:
            nonce_hex: 128-character hexadecimal string (64 bytes)
                      Use utils.generate_nonce() and utils.format_nonce_hex()
        
        Returns:
            Raw attestation report bytes
        
        Raises:
            APIError: If TEE is not available or attestation fails
            ValueError: If nonce format is invalid
        
        Example:
            >>> from fhenomai.utils import generate_nonce, format_nonce_hex
            >>> 
            >>> # Generate cryptographically secure nonce
            >>> nonce = generate_nonce(64)
            >>> nonce_hex = format_nonce_hex(nonce)
            >>> 
            >>> # Request attestation
            >>> report = api.attestation(nonce_hex)
            >>> 
            >>> # Save report for verification
            >>> with open('attestation_report.bin', 'wb') as f:
            ...     f.write(report)
        
        Note:
            The nonce must be exactly 64 bytes (128 hex characters).
            The attestation report can be verified using TEE verification libraries.
        """
        # Validate nonce format
        if len(nonce_hex) != 128:
            raise ValueError(f"Nonce must be 128 hex characters (64 bytes), got {len(nonce_hex)}")
        
        try:
            bytes.fromhex(nonce_hex)
        except ValueError as e:
            raise ValueError(f"Invalid hexadecimal nonce: {e}")
        
        response = self._request(
            "POST",
            "/admin/attestation",
            json={"nonce_hex": nonce_hex}
        )
        
        # Parse JSON response and decode hex string to bytes
        response_data = response.json()
        report_hex = response_data.get("report_bytes_hex")
        
        if not report_hex:
            raise APIError("Attestation response missing 'report_bytes_hex' field")
        
        try:
            return bytes.fromhex(report_hex)
        except ValueError as e:
            raise APIError(f"Invalid hex data in attestation report: {e}")
    
    def verify_attestation(self, report_bytes: bytes, expected_nonce_hex: str, 
                          engine_type: str = "amd_sev_snp") -> Dict[str, Any]:
        """
        Verify a TEE attestation report against an expected nonce.
        
        This method uses the fhenom-tee-attestation library to cryptographically
        verify that:
        - The report is authentic and signed by the TEE platform
        - The report is bound to the expected nonce
        - The certificate chain is valid (for AMD SEV-SNP)
        
        Args:
            report_bytes: Raw attestation report bytes to verify
            expected_nonce_hex: Expected nonce as 128-character hex string (64 bytes)
            engine_type: TEE platform type ("amd_sev_snp" or "intel_tdx")
        
        Returns:
            Dictionary with verification results:
                - verified: bool indicating if verification passed
                - platform: TEE platform type
                - error: Error message if verification failed (optional)
        
        Raises:
            ValueError: If nonce format is invalid or engine_type is unsupported
            ImportError: If fhenom-tee-attestation library is not installed
        
        Example:
            >>> from fhenomai.utils import generate_nonce, format_nonce_hex
            >>> 
            >>> # Generate nonce and get attestation
            >>> nonce = generate_nonce(64)
            >>> nonce_hex = format_nonce_hex(nonce)
            >>> report = client.admin.attestation(nonce_hex)
            >>> 
            >>> # Verify the attestation
            >>> result = client.admin.verify_attestation(report, nonce_hex)
            >>> if result['verified']:
            ...     print("Attestation verified successfully!")
            ... else:
            ...     print(f"Verification failed: {result['error']}")
        
        Note:
            The dk-tee-attestation package is included with fhenomai.
            To install standalone: pip install dk-tee-attestation
            
            For AMD SEV-SNP, this will fetch and verify certificates from AMD KDS.
        """
        # Validate nonce format
        if len(expected_nonce_hex) != 128:
            raise ValueError(f"Nonce must be 128 hex characters (64 bytes), got {len(expected_nonce_hex)}")
        
        try:
            expected_nonce_bytes = bytes.fromhex(expected_nonce_hex)
        except ValueError as e:
            raise ValueError(f"Invalid hexadecimal nonce: {e}")
        
        # Import the attestation library
        try:
            from dk_tee_attestation import AttestationEngineFactory, AttestationEngineType
        except ImportError:
            raise ImportError(
                "The dk-tee-attestation library is required for attestation verification. "
                "Install it with: pip install fhenomai"
            )
        
        # Map engine type string to enum
        engine_type_map = {
            "amd_sev_snp": AttestationEngineType.AMD_SEV_SNP,
            "intel_tdx": AttestationEngineType.INTEL_TDX,
        }
        
        if engine_type not in engine_type_map:
            raise ValueError(
                f"Unsupported engine_type: {engine_type}. "
                f"Supported types: {list(engine_type_map.keys())}"
            )
        
        # Get the attestation engine
        engine = AttestationEngineFactory.get(engine_type_map[engine_type])
        
        # Verify the report
        try:
            engine.verify_report(report_bytes, expected_nonce_bytes)
            return {
                "verified": True,
                "platform": engine_type,
                "message": "Attestation report verified successfully"
            }
        except Exception as e:
            return {
                "verified": False,
                "platform": engine_type,
                "error": str(e),
                "error_type": type(e).__name__
            }
    
    # ========================================================================
    # Model Discovery
    # ========================================================================
    
    def list_models(self) -> List[str]:
        """
        Get list of all configured encrypted models.
        
        Returns:
            List of model IDs
        
        Example:
            >>> models = api.list_models()
            >>> print(models)
            ['/mnt/models/llama-3-8b', '/mnt/models/mistral-7b']
        """
        response = self._request("GET", "/admin/list")
        return response.json()["response"]
    
    def list_online_models(self) -> List[Dict[str, str]]:
        """
        Get list of currently served models.
        
        Returns:
            List of dicts with 'name' and 'status' keys
        
        Example:
            >>> online = api.list_online_models()
            >>> for model in online:
            ...     print(f"{model['name']}: {model['status']}")
        """
        response = self._request("GET", "/admin/list_online")
        return response.json()["response"]
    
    def get_model_info(self, model_id_or_symbolic_name: str) -> Dict[str, Any]:
        """
        Get detailed information about a specific encrypted model.
        
        Args:
            model_id_or_symbolic_name: Model identifier or symbolic name
        
        Returns:
            Dict with model information including symbolic_name if available
        
        Raises:
            ModelNotFoundError: If model not found
        
        Example:
            >>> # Query by model ID
            >>> info = api.get_model_info('/mnt/models/llama-3-8b')
            >>> print(f"Encryption: {info['encryption_impl']}")
            >>> 
            >>> # Query by symbolic name
            >>> info = api.get_model_info('production-llama')
            >>> print(f"Symbolic name: {info.get('symbolic_name')}")
        """
        encoded_id = quote(model_id_or_symbolic_name, safe='')
        
        try:
            response = self._request("GET", f"/admin/model_info/{encoded_id}")
            return response.json()
        except APIError as e:
            if e.status_code == 404:
                raise ModelNotFoundError(model_id_or_symbolic_name)
            raise
    
    def delete_encrypted_model(self, model_id: str) -> Dict[str, Any]:
        """
        Delete an encrypted model from local storage.
        
        Note: This operation is local only and does not propagate to peer instances
        after synchronization. This allows selective deletion on individual nodes.
        
        Args:
            model_id: Model identifier to delete
        
        Returns:
            Dict with deletion status and any peer response information
        
        Raises:
            ModelNotFoundError: If model not found
            APIError: If deletion fails
        
        Example:
            >>> result = api.delete_encrypted_model('/mnt/models/llama-3-8b')
            >>> print(f"Deleted: {result['success']}")
            >>> if 'embedded_server_response_code' in result:
            ...     print(f"Peer response: {result['embedded_server_response_code']}")
        """
        encoded_id = quote(model_id, safe='')
        
        try:
            response = self._request("DELETE", f"/admin/models/{encoded_id}")
            return response.json()
        except APIError as e:
            if e.status_code == 404:
                raise ModelNotFoundError(model_id)
            raise
    
    # ========================================================================
    # Serving Control
    # ========================================================================
    
    def start_serving(
        self,
        encrypted_model_id: str,
        server_url: str,
        api_key: Optional[str] = None,
        display_model_name: Optional[str] = None
    ) -> Dict[str, Any]:
        """
        Start serving an encrypted model via external serving framework (e.g., VLLM).
        
        Args:
            encrypted_model_id: The ID of the encrypted model (from list_models())
            server_url: URL of external serving framework (without /v1 suffix)
                       Example: "http://192.168.1.100:8000"
            api_key: Optional API key if external framework requires authentication
            display_model_name: Optional display name for vLLM --served-model-name
                              If not provided, uses encrypted_model_id
        
        Returns:
            Response from the API with status
        
        Example:
            >>> result = api.start_serving(
            ...     encrypted_model_id='/mnt/models/llama-3-8b',
            ...     server_url='http://vllm-server:8000',
            ...     display_model_name='llama-3-8b-instruct'
            ... )
            >>> print(result['response'])  # "Successfully started"
        
        Note:
            - server_url should NOT include /v1 suffix
            - display_model_name should match vLLM's --served-model-name
            - Startup takes 1-2 minutes for TEE initialization
        """
        payload = {
            "encrypted_model_id": encrypted_model_id,
            "serving_mode": "external",
            "server_url": server_url
        }
        
        if api_key:
            payload["api_key"] = api_key
        if display_model_name:
            payload["display_model_name"] = display_model_name
        
        response = self._request("POST", "/admin/start", json=payload)
        return response.json()
    
    def stop_serving(self, encrypted_model_id: str) -> Dict[str, Any]:
        """
        Stop serving an encrypted model.
        
        Args:
            encrypted_model_id: Model identifier to stop
        
        Returns:
            Response from the API
        
        Example:
            >>> result = api.stop_serving('/mnt/models/llama-3-8b')
            >>> print(result['response'])  # "Model stopped successfully"
        
        Note:
            This only stops the TEE layer. You must manually stop the
            serving framework (VLLM, TGI, etc.) on your server.
        """
        payload = {"encrypted_model_id": encrypted_model_id}
        response = self._request("POST", "/admin/stop", json=payload)
        return response.json()
    
    # ========================================================================
    # Model Encryption
    # ========================================================================
    
    def encrypt_model(
        self,
        model_name_or_path: str,
        out_encrypted_model_path: str,
        symbolic_name: Optional[str] = None,
        server_ip: str = "fhenom_ai_server",
        server_port: int = 9100,
        encryption_impl: str = "decoder-only-llm",
        preprocessing_impl: str = "default",
        inference_mode: str = "double_tokenizer",
        driver_mode: str = "safetensor",
        dtype: str = "bfloat16",
        model_key_password: Optional[str] = None,
        encrypted_model_id: Optional[str] = None,
        preprocessing_args: Optional[List[str]] = None
    ) -> str:
        """
        Submit model encryption job.
        
        Args:
            model_name_or_path: Path to uploaded model in upload folder
            out_encrypted_model_path: Output path in download folder
            symbolic_name: Symbolic name for the encrypted model (required by API)
            server_ip: FHEnom AI Server hostname/IP
            server_port: Server admin port
            encryption_impl: Encryption method ('decoder-only-llm', 'moe-llm')
            preprocessing_impl: Preprocessing strategy ('default', 'lora')
            inference_mode: Inference mode (currently 'double_tokenizer')
            driver_mode: Model driver mode (currently 'safetensor')
            dtype: Data type ('bfloat16', 'float16')
            model_key_password: Optional password for model encryption keys
            encrypted_model_id: Optional custom model ID
            preprocessing_args: Optional preprocessing arguments
        
        Returns:
            Job ID for status tracking
        
        Example:
            >>> job_id = api.encrypt_model(
            ...     model_name_or_path='/models/upload/llama-3-8b',
            ...     out_encrypted_model_path='/models/download/llama-3-8b-encrypted'
            ... )
            >>> print(f"Job ID: {job_id}")
        """
        # Use symbolic_name if provided, otherwise use encrypted_model_id, or extract from output path
        if symbolic_name is None:
            if encrypted_model_id:
                symbolic_name = encrypted_model_id
            else:
                # Extract from output path as fallback
                symbolic_name = out_encrypted_model_path.split('/')[-1]
        
        payload = {
            "server_ip": server_ip,
            "server_port": server_port,
            "model_name_or_path": model_name_or_path,
            "out_encrypted_model_path": out_encrypted_model_path,
            "symbolic_name": symbolic_name,
            "encryption_impl": encryption_impl,
            "preprocessing_impl": preprocessing_impl,
            "inference_mode": inference_mode,
            "driver_mode": driver_mode,
            "dtype": dtype
        }
        
        if model_key_password:
            payload["model_key_password"] = model_key_password
        if encrypted_model_id:
            payload["encrypted_model_id"] = encrypted_model_id
        if preprocessing_args:
            payload["preprocessing_args"] = preprocessing_args
        
        response = self._request("POST", "/admin/encrypt_model", json=payload)
        result = response.json()
        return result["job_id"]
    
    # ========================================================================
    # Dataset Encryption
    # ========================================================================
    
    def encrypt_dataset(
        self,
        encrypted_model_id: str,
        dataset_name_or_path: str,
        out_encrypted_dataset_path: str,
        server_ip: str = "fhenom_ai_server",
        server_port: int = 9100,
        dataset_encryption_impl: str = "numeric",
        text_fields: Optional[List[str]] = None
    ) -> str:
        """
        Submit dataset encryption job.
        
        Args:
            encrypted_model_id: Path to encrypted model (dataset will be bound to this model)
            dataset_name_or_path: Path to plaintext dataset in upload folder
            out_encrypted_dataset_path: Output path in download folder
            server_ip: FHEnom AI Server hostname/IP
            server_port: Server admin port
            dataset_encryption_impl: Encryption method ('numeric' recommended)
            text_fields: Column names containing text to encrypt (e.g., ['text'])
        
        Returns:
            Job ID for status tracking
        
        Example:
            >>> job_id = api.encrypt_dataset(
            ...     encrypted_model_id='/mnt/models/llama-3-8b',
            ...     dataset_name_or_path='/models/upload/my-dataset',
            ...     out_encrypted_dataset_path='/models/download/my-dataset-encrypted',
            ...     text_fields=['text']
            ... )
        
        Note:
            The encrypted dataset is cryptographically bound to the specified
            encrypted_model_id and can only be used to train that model.
        """
        # Normalize paths: add upload/ or download/ if not present
        if not dataset_name_or_path.startswith('upload/') and not dataset_name_or_path.startswith('/models/upload/'):
            dataset_name_or_path = f"/models/upload/{dataset_name_or_path}"
        elif dataset_name_or_path.startswith('upload/'):
            dataset_name_or_path = f"/models/{dataset_name_or_path}"
        
        if not out_encrypted_dataset_path.startswith('download/') and not out_encrypted_dataset_path.startswith('/models/download/'):
            out_encrypted_dataset_path = f"/models/download/{out_encrypted_dataset_path}"
        elif out_encrypted_dataset_path.startswith('download/'):
            out_encrypted_dataset_path = f"/models/{out_encrypted_dataset_path}"
        
        payload = {
            "encrypted_model_id": encrypted_model_id,
            "server_ip": server_ip,
            "server_port": server_port,
            "dataset_name_or_path": dataset_name_or_path,
            "out_encrypted_dataset_path": out_encrypted_dataset_path,
            "dataset_encryption_impl": dataset_encryption_impl
        }
        
        if text_fields:
            payload["text_fields"] = text_fields
        
        response = self._request("POST", "/admin/encrypt_dataset", json=payload)
        result = response.json()
        return result["job_id"]
    
    # ========================================================================
    # Job Management
    # ========================================================================
    
    def get_job_status(self, job_id: str) -> Dict[str, Any]:
        """
        Get status of an asynchronous job.
        
        Args:
            job_id: Job identifier
        
        Returns:
            Dict with job_id, status, progress, message, result, error
        
        Example:
            >>> status = api.get_job_status('enc_model_abc123')
            >>> print(f"Status: {status['status']}")
            >>> print(f"Progress: {status['progress']:.1%}")
        """
        response = self._request("GET", f"/admin/jobs/{job_id}")
        return response.json()
    
    def wait_for_job(
        self,
        job_id: str,
        poll_interval: int = 10,
        timeout: Optional[int] = 3600,
        callback: Optional[Callable[[Dict[str, Any]], None]] = None,
        show_progress: bool = True
    ) -> Dict[str, Any]:
        """
        Wait for job completion with progress monitoring.
        
        Args:
            job_id: Job identifier
            poll_interval: Seconds between status checks
            timeout: Maximum seconds to wait (None for no timeout)
            callback: Optional callback function called on each status update
            show_progress: Whether to print progress updates
        
        Returns:
            Final job status dict
        
        Raises:
            JobError: If job fails
            TimeoutError: If job exceeds timeout
        
        Example:
            >>> def on_progress(status):
            ...     print(f"Progress: {status['progress']:.1%}")
            >>> 
            >>> result = api.wait_for_job(
            ...     'enc_model_abc123',
            ...     callback=on_progress
            ... )
            >>> print(f"Final status: {result['status']}")
        """
        start_time = time.time()
        
        while True:
            status = self.get_job_status(job_id)
            
            # Call callback if provided
            if callback:
                callback(status)
            
            # Print progress if requested
            if show_progress:
                progress_pct = status.get('progress', 0) * 100
                message = status.get('message', '')
                print(f"[{status['status']}] {message} ({progress_pct:.1f}%)", end='\r')
            
            # Check if job is done
            if status["status"] == "done":
                if show_progress:
                    print()  # New line after progress updates
                logger.info(f"Job {job_id} completed successfully")
                return status
            
            if status["status"] == "error":
                error_msg = status.get("error", "Unknown error")
                logger.error(f"Job {job_id} failed: {error_msg}")
                raise JobError(
                    f"Job failed: {error_msg}",
                    job_id=job_id,
                    error_details=error_msg
                )
            
            # Check timeout
            elapsed = time.time() - start_time
            if timeout is not None and elapsed > timeout:
                raise TimeoutError(
                    f"Job timeout after {timeout}s",
                    job_id=job_id
                )
            
            # Sleep before next check
            time.sleep(poll_interval)
    
    # ========================================================================
    # Health & Monitoring
    # ========================================================================
    
    def get_health(self) -> Dict[str, Any]:
        """
        Get system health status.
        
        Returns:
            Dict with health information
        
        Example:
            >>> health = api.get_health()
            >>> print(f"Status: {health['status']}")
        
        Note:
            This is a placeholder. Implementation depends on TEE system APIs.
        """
        # TODO: Implement when TEE provides health endpoint
        logger.warning("get_health() is a placeholder - not yet implemented by TEE")
        return {
            "status": "unknown",
            "message": "Health endpoint not yet available",
            "implementation": "placeholder"
        }
    
    def get_metrics(self) -> Dict[str, Any]:
        """
        Get system performance metrics.
        
        Returns:
            Dict with performance metrics (CPU, memory, etc.)
        
        Example:
            >>> metrics = api.get_metrics()
            >>> print(f"CPU: {metrics.get('cpu_usage', 'N/A')}%")
        
        Note:
            This is a placeholder. Implementation depends on TEE system APIs.
        """
        # TODO: Implement when TEE provides metrics endpoint
        logger.warning("get_metrics() is a placeholder - not yet implemented by TEE")
        return {
            "cpu_usage": None,
            "memory_usage": None,
            "gpu_usage": None,
            "active_models": len(self.list_online_models()),
            "message": "Metrics endpoint not yet available",
            "implementation": "placeholder"
        }
    
    def get_tee_info(self) -> Dict[str, Any]:
        """
        Get TEE system information.
        
        Returns:
            Dict with TEE version, capabilities, configuration
        
        Example:
            >>> info = api.get_tee_info()
            >>> print(f"Version: {info.get('version', 'unknown')}")
        
        Note:
            This is a placeholder. Implementation depends on TEE system APIs.
        """
        # TODO: Implement when TEE provides system info endpoint
        logger.warning("get_tee_info() is a placeholder - not yet implemented by TEE")
        return {
            "version": "unknown",
            "capabilities": [],
            "configuration": {},
            "message": "System info endpoint not yet available",
            "implementation": "placeholder"
        }
    
    # ========================================================================
    # Account Management (Placeholder)
    # ========================================================================
    
    def create_user(self, username: str, password: str, role: str = "user") -> Dict[str, Any]:
        """
        Create a new user account.
        
        Args:
            username: Username
            password: Password
            role: User role ('user', 'admin')
        
        Returns:
            Dict with user creation result
        
        Note:
            This is a placeholder. Account management not yet implemented by TEE.
        """
        # TODO: Implement when TEE provides account management
        logger.warning("create_user() is a placeholder - not yet implemented by TEE")
        return {
            "success": False,
            "message": "Account management not yet available",
            "implementation": "placeholder"
        }
    
    def delete_user(self, username: str) -> Dict[str, Any]:
        """
        Delete a user account.
        
        Args:
            username: Username to delete
        
        Returns:
            Dict with deletion result
        
        Note:
            This is a placeholder. Account management not yet implemented by TEE.
        """
        # TODO: Implement when TEE provides account management
        logger.warning("delete_user() is a placeholder - not yet implemented by TEE")
        return {
            "success": False,
            "message": "Account management not yet available",
            "implementation": "placeholder"
        }
    
    def list_users(self) -> List[Dict[str, Any]]:
        """
        List all user accounts.
        
        Returns:
            List of user dicts
        
        Note:
            This is a placeholder. Account management not yet implemented by TEE.
        """
        # TODO: Implement when TEE provides account management
        logger.warning("list_users() is a placeholder - not yet implemented by TEE")
        return []
    
    def update_user_permissions(
        self,
        username: str,
        permissions: Dict[str, bool]
    ) -> Dict[str, Any]:
        """
        Update user permissions.
        
        Args:
            username: Username
            permissions: Dict of permission flags
        
        Returns:
            Dict with update result
        
        Note:
            This is a placeholder. Account management not yet implemented by TEE.
        """
        # TODO: Implement when TEE provides account management
        logger.warning("update_user_permissions() is a placeholder - not yet implemented by TEE")
        return {
            "success": False,
            "message": "Account management not yet available",
            "implementation": "placeholder"
        }
    
    # ========================================================================
    # Advanced Operations (Placeholders)
    # ========================================================================
    
    def batch_encrypt_models(
        self,
        models: List[Dict[str, Any]],
        concurrent: int = 1
    ) -> List[str]:
        """
        Encrypt multiple models in batch.
        
        Args:
            models: List of model configs (each with encryption parameters)
            concurrent: Number of concurrent encryption jobs (default: 1)
        
        Returns:
            List of job IDs
        
        Note:
            This is a convenience method. TEE processes jobs sequentially.
        
        Example:
            >>> models = [
            ...     {'model_name_or_path': '/models/upload/llama-3-8b', ...},
            ...     {'model_name_or_path': '/models/upload/mistral-7b', ...}
            ... ]
            >>> job_ids = api.batch_encrypt_models(models)
        """
        logger.info(f"Submitting {len(models)} model encryption jobs")
        job_ids = []
        
        for model_config in models:
            try:
                job_id = self.encrypt_model(**model_config)
                job_ids.append(job_id)
                logger.info(f"Submitted: {model_config['model_name_or_path']} -> {job_id}")
            except Exception as e:
                logger.error(f"Failed to submit {model_config['model_name_or_path']}: {e}")
        
        return job_ids
    
    def get_queue_status(self) -> Dict[str, Any]:
        """
        Get job queue status.
        
        Returns:
            Dict with queue information
        
        Note:
            This is a placeholder. Queue management not yet exposed by TEE API.
        """
        # TODO: Implement when TEE provides queue status endpoint
        logger.warning("get_queue_status() is a placeholder - not yet implemented by TEE")
        return {
            "pending_jobs": 0,
            "running_jobs": 0,
            "message": "Queue status endpoint not yet available",
            "implementation": "placeholder"
        }
    
    def cancel_job(self, job_id: str) -> Dict[str, Any]:
        """
        Cancel a running job.
        
        Args:
            job_id: Job identifier to cancel
        
        Returns:
            Dict with cancellation result
        
        Note:
            This is a placeholder. Job cancellation not yet implemented by TEE.
        """
        # TODO: Implement when TEE provides job cancellation
        logger.warning("cancel_job() is a placeholder - not yet implemented by TEE")
        return {
            "success": False,
            "message": "Job cancellation not yet available",
            "implementation": "placeholder"
        }
    
    def get_model_usage_stats(self, model_id: str) -> Dict[str, Any]:
        """
        Get usage statistics for a model.
        
        Args:
            model_id: Model identifier
        
        Returns:
            Dict with usage statistics
        
        Note:
            This is a placeholder. Usage statistics not yet available from TEE.
        """
        # TODO: Implement when TEE provides usage statistics
        logger.warning("get_model_usage_stats() is a placeholder - not yet implemented by TEE")
        return {
            "model_id": model_id,
            "total_requests": 0,
            "message": "Usage statistics not yet available",
            "implementation": "placeholder"
        }
