"""BlackAnt SDK unified client interface.

Main entry point for BlackAnt SDK providing all 5 core methods
through a single unified interface for simplified usage.
"""

from typing import Dict, Any, Optional, List
import os

from .auth.blackant_auth import BlackAntAuth
from .docker.client import BlackAntDockerClient
from .services.registry import ServiceRegistry
from .http.client import HTTPClient, HTTPConnectionError
from .exceptions import BlackAntDockerError, BlackAntConnectionError
from .utils.logging import get_logger


class BlackAntClient:
    """Unified BlackAnt SDK client providing all core functionality.

    This is the main entry point for the BlackAnt SDK, combining all
    core functionality into a single, easy-to-use interface. It provides
    the 5 core methods that make up the complete BlackAnt SDK experience.

    Args:
        user: BlackAnt username or email
        password: BlackAnt password
        login_url: Optional login URL override
        base_url: Optional base URL override

    Examples:
        >>> # Initialize client
        >>> client = BlackAntClient(user="developer@company.com", password="xxx")

        >>> # Test connection
        >>> if client.test_connection():
        ...     print("Connected to BlackAnt!")

        >>> # Build and deploy service
        >>> result = client.build_service("my-calculation")
        >>> print(f"Service deployed: {result['service_id']}")

        >>> # List all services
        >>> services = client.list_services()
        >>> print(f"Found {len(services)} services")

        >>> # Register additional service
        >>> service_id = client.register_service({
        ...     "name": "external-service",
        ...     "type": "api"
        ... })
    """

    def __init__(self,
                 user: str = None,
                 password: str = None,
                 token: str = None,
                 client_id: str = None,
                 client_secret: str = None,
                 login_url: str = None,
                 base_url: str = None):
        """Initialize unified BlackAnt client.

        Supports three authentication modes:
        1. Username/Password: Provide user + password
        2. Client Credentials: Provide client_id + client_secret (for automation/CI/CD)
        3. Existing Token: Provide user + token

        Args:
            user: BlackAnt username or email (required for password/token auth)
            password: BlackAnt password (password flow)
            token: Existing JWT token (token flow)
            client_id: Service account client ID (client credentials flow)
            client_secret: Service account client secret (client credentials flow)
            login_url: Optional Keycloak login URL override
            base_url: Optional BlackAnt API base URL override

        Examples:
            >>> # Password authentication
            >>> client = BlackAntClient(user="dev@company.com", password="xxx")

            >>> # Client credentials authentication (for automation)
            >>> client = BlackAntClient(
            ...     client_id="my-service-account",
            ...     client_secret="xyz123..."
            ... )

            >>> # Existing token
            >>> client = BlackAntClient(user="dev@company.com", token="eyJhbG...")
        """
        self.logger = get_logger("blackant.client")

        # Initialize authentication based on provided credentials
        if client_id and client_secret:
            # Client Credentials Flow (service account)
            self.auth = BlackAntAuth(
                client_id=client_id,
                client_secret=client_secret,
                login_url=login_url
            )
        elif user and password:
            # Password Flow (username/password)
            self.auth = BlackAntAuth(user=user, password=password, login_url=login_url)
        elif user and token:
            # Existing token
            self.auth = BlackAntAuth(user=user, password="dummy", login_url=login_url)
            self.auth.token_store.user_token = token
            self.auth._authenticated = True
        else:
            raise ValueError(
                "Provide either: (user+password), (client_id+client_secret), or (user+token)"
            )

        # Initialize specialized clients
        self.docker_client = BlackAntDockerClient(auth=self.auth, base_url=base_url)
        self.service_registry = ServiceRegistry(auth=self.auth, base_url=base_url)

        # Initialize HTTP client for connection testing
        self.base_url = base_url or os.getenv("BLACKANT_BASE_URL", "https://dev.blackant.app")
        self.http_client = HTTPClient(base_url=self.base_url, auth_token_store=self.auth.token_store)

        self.logger.info("BlackAnt unified client initialized")

    # ==========================================
    # CORE METHOD 1: AUTHENTICATION
    # ==========================================
    def authenticate(self) -> bool:
        """Authenticate with BlackAnt platform.

        Performs authentication with BlackAnt Keycloak and stores
        the JWT token for subsequent API calls.

        Returns:
            bool: True if authentication successful, False otherwise

        Raises:
            BlackAntAuthenticationError: If authentication fails

        Example:
            >>> client = BlackAntClient(user="dev@company.com", password="xxx")
            >>> if client.authenticate():
            ...     print("Successfully authenticated!")
        """
        try:
            token = self.auth.authenticate()
            self.logger.info("Authentication successful")
            return bool(token)
        except Exception as e:
            self.logger.error(f"Authentication failed: {e}")
            return False

    # ==========================================
    # CORE METHOD 2: CONNECTION TEST
    # ==========================================
    def test_connection(self) -> bool:
        """Test connection to BlackAnt platform.

        Verifies that the client can connect to BlackAnt services
        and that authentication is working properly.

        Returns:
            bool: True if connection successful, False otherwise

        Example:
            >>> client = BlackAntClient(user="dev@company.com", password="xxx")
            >>> if client.test_connection():
            ...     print("Connection to BlackAnt successful!")
            ... else:
            ...     print("Connection failed")
        """
        try:
            # Ensure we have a valid token
            if not self.auth.is_authenticated():
                self.authenticate()

            # Test Docker API connection
            version = self.docker_client.version
            self.logger.info(f"Docker API connection successful, version: {version}")

            # Test Service Registry connection
            services = self.service_registry.list()
            self.logger.info(f"Service Registry connection successful, found {len(services)} services")

            return True

        except Exception as e:
            self.logger.error(f"Connection test failed: {e}")
            return False

    # ==========================================
    # CORE METHOD 3: BUILD SERVICE
    # ==========================================
    def build_service(self,
                     service_name: str,
                     impl_path: str = "src/calculation/impl",
                     dockerfile_path: str = "Dockerfile",
                     tag: Optional[str] = None,
                     push: bool = True,
                     register_service: bool = True) -> Dict[str, Any]:
        """Build and deploy service Docker image.

        Builds a Docker image from user implementation, pushes to registry,
        and registers the service with ServiceManager. This is the primary
        method for deploying calculations to BlackAnt platform.

        Args:
            service_name: Service name (alphanumeric + hyphens/underscores)
            impl_path: User implementation folder path
            dockerfile_path: Dockerfile path (repository root)
            tag: Version tag (auto-generated if None: v1.0.0-{timestamp})
            push: Upload to env.blackant.app registry
            register_service: Automatic service registration with ServiceManager

        Returns:
            dict: Build and deployment information
                {
                    "image_name": "env.blackant.app/systemdevelopers/service_name",
                    "tag": "v1.0.0-20250105-123456",
                    "full_image": "env.blackant.app/systemdevelopers/service_name:v1.0.0-20250105-123456",
                    "image_id": "sha256:...",
                    "size": 125000000,
                    "pushed": True,
                    "registry_url": "env.blackant.app",
                    "service_id": "uuid-from-servicemanager",
                    "service_registered": True,
                    "build_success": True
                }

        Raises:
            BlackAntDockerError: Build, push or registration error

        Example:
            >>> client = BlackAntClient(user="developer", password="xxx")
            >>> result = client.build_service("my-calculation")
            >>> if result["build_success"]:
            ...     print(f"Service deployed with ID: {result['service_id']}")
        """
        return self.docker_client.build_service(
            service_name=service_name,
            impl_path=impl_path,
            dockerfile_path=dockerfile_path,
            tag=tag,
            push=push,
            register_service=register_service
        )

    # ==========================================
    # CORE METHOD 4: REGISTER SERVICE
    # ==========================================
    def register_service(self, service_data: Dict[str, Any]) -> str:
        """Register a service with BlackAnt platform.

        Registers a service with the ServiceManager, making it available
        for discovery and management through the BlackAnt platform.

        Args:
            service_data: Service registration data containing:
                - name: Service name
                - type: Service type (e.g., "calculation", "api")
                - metadata: Additional service metadata
                - image: Optional Docker image information

        Returns:
            str: Service ID of the registered service

        Raises:
            BlackAntDockerError: If service registration fails

        Example:
            >>> client = BlackAntClient(user="developer", password="xxx")
            >>> service_id = client.register_service({
            ...     "name": "external-api",
            ...     "type": "api",
            ...     "metadata": {"version": "2.0.0"}
            ... })
            >>> print(f"Service registered with ID: {service_id}")
        """
        return self.service_registry.register(service_data)

    # ==========================================
    # CORE METHOD 5: LIST SERVICES
    # ==========================================
    def list_services(self, namespace: Optional[str] = None) -> List[Dict[str, Any]]:
        """List all services, optionally filtered by namespace.

        Retrieves a list of all services registered with BlackAnt platform,
        optionally filtered by namespace for organization.

        Args:
            namespace: Optional namespace filter

        Returns:
            List of service dictionaries with metadata

        Example:
            >>> client = BlackAntClient(user="developer", password="xxx")
            >>> services = client.list_services()
            >>> print(f"Found {len(services)} services")
            >>> for service in services:
            ...     print(f"- {service['name']}: {service['status']}")
        """
        return self.service_registry.list(namespace=namespace)

    # ==========================================
    # ADDITIONAL UTILITY METHODS
    # ==========================================
    def get_service(self, service_id: str = None, service_name: str = None) -> Dict[str, Any]:
        """Get detailed information about a specific service.

        Args:
            service_id: Service UUID
            service_name: Service name

        Returns:
            Service details dictionary

        Example:
            >>> service = client.get_service(service_name="my-calculation")
            >>> print(f"Service status: {service['status']}")
        """
        return self.service_registry.get(service_id=service_id, service_name=service_name)

    def update_service(self, service_id: str = None, service_name: str = None,
                      update_data: Dict[str, Any] = None) -> bool:
        """Update service metadata.

        Args:
            service_id: Service UUID
            service_name: Service name
            update_data: Updated service data

        Returns:
            bool: True if update successful

        Example:
            >>> success = client.update_service(
            ...     service_name="my-calculation",
            ...     update_data={"metadata": {"version": "2.0.0"}}
            ... )
        """
        return self.service_registry.update(
            service_id=service_id,
            service_name=service_name,
            update_data=update_data
        )

    def delete_service(self, service_id: str = None, service_name: str = None) -> bool:
        """Delete a service.

        Args:
            service_id: Service UUID
            service_name: Service name

        Returns:
            bool: True if deletion successful

        Example:
            >>> success = client.delete_service(service_name="old-calculation")
        """
        return self.service_registry.delete(service_id=service_id, service_name=service_name)

    def get_service_status(self, service_id: str = None, service_name: str = None) -> Dict[str, Any]:
        """Get runtime status of a service.

        Args:
            service_id: Service UUID
            service_name: Service name

        Returns:
            Service status information

        Example:
            >>> status = client.get_service_status("my-calculation")
            >>> print(f"Service is {status['state']}")
        """
        return self.service_registry.get_status(service_id=service_id, service_name=service_name)

    def is_authenticated(self) -> bool:
        """Check if client is currently authenticated.

        Returns:
            bool: True if authenticated with valid token
        """
        return self.auth.is_authenticated()

    def __repr__(self) -> str:
        """String representation of client."""
        if hasattr(self.auth, '_auth_mode') and self.auth._auth_mode == "client_credentials":
            return f"BlackAntClient(client_id={self.auth.client_id}, authenticated={self.is_authenticated()})"
        else:
            return f"BlackAntClient(user={self.auth.user}, authenticated={self.is_authenticated()})"

    def __enter__(self):
        """Context manager entry."""
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        """Context manager exit."""
        # Cleanup if needed
        pass