"""Docker configuration management for BlackAnt SDK."""

import os
from typing import Optional, Dict, Any
from dataclasses import dataclass, field

from blackant.utils.logging import get_logger

try:
    import docker
    import docker.tls
except ImportError:
    docker = None


@dataclass
class DockerDaemonConfig:
    """Remote Docker daemon configuration.

    Supports both nginx proxy (recommended) and direct TLS connection
    to remote Docker daemon as per BlackAnt architecture specification.
    """

    # Remote Docker daemon host
    remote_host: str = field(default_factory=lambda: os.getenv(
        "DOCKER_DAEMON_HOST",
        "localhost"
    ))

    remote_port: int = field(default_factory=lambda: int(os.getenv(
        "DOCKER_DAEMON_PORT",
        "80"
    )))

    # Use nginx proxy (recommended for JWT token authentication)
    use_nginx_proxy: bool = field(default_factory=lambda: os.getenv(
        "DOCKER_USE_NGINX_PROXY",
        "true"
    ).lower() == "true")

    # Nginx proxy URL (when use_nginx_proxy=True)
    nginx_proxy_url: str = field(default_factory=lambda: os.getenv(
        "DOCKER_NGINX_PROXY_URL",
        "http://localhost:80/api/docker"
    ))

    # TLS configuration (when use_nginx_proxy=False)
    use_tls: bool = field(default_factory=lambda: os.getenv(
        "DOCKER_USE_TLS",
        "false"
    ).lower() == "true")

    ca_cert: Optional[str] = field(default_factory=lambda: os.getenv("DOCKER_CA_CERT"))
    client_cert: Optional[str] = field(default_factory=lambda: os.getenv("DOCKER_CLIENT_CERT"))
    client_key: Optional[str] = field(default_factory=lambda: os.getenv("DOCKER_CLIENT_KEY"))

    # Connection timeout
    timeout: int = field(default_factory=lambda: int(os.getenv(
        "DOCKER_DAEMON_TIMEOUT",
        "600"
    )))

    @property
    def base_url(self) -> str:
        """Get Docker daemon base URL.

        Returns:
            str: Docker daemon URL (nginx proxy or direct connection)
        """
        if self.use_nginx_proxy:
            return self.nginx_proxy_url

        protocol = "https" if self.use_tls else "http"
        return f"{protocol}://{self.remote_host}:{self.remote_port}"

    def get_tls_config(self) -> Optional['docker.tls.TLSConfig']:
        """Get TLS configuration for Docker client.

        Returns:
            TLSConfig or None if TLS not used or nginx proxy is enabled
        """
        if docker is None:
            return None

        if not self.use_tls or self.use_nginx_proxy:
            return None

        if not all([self.ca_cert, self.client_cert, self.client_key]):
            raise ValueError("TLS enabled but certificate paths not configured")

        return docker.tls.TLSConfig(
            client_cert=(self.client_cert, self.client_key),
            ca_cert=self.ca_cert,
            verify=True
        )


@dataclass
class DockerRegistryConfig:
    """Docker registry configuration data class."""

    url: str = "env.blackant.app"
    namespace: str = "science_module"
    username: Optional[str] = None
    password: Optional[str] = None

    def __post_init__(self):
        """Post-initialization to load from environment if not provided."""
        if not self.username:
            self.username = os.getenv("DOCKER_REGISTRY_USER")
        if not self.password:
            self.password = os.getenv("DOCKER_REGISTRY_PASS")


@dataclass
class SdkServicesConfig:
    """SDK Services configuration for HTTP-based operations.

    When enabled, registry push and role assignment operations are
    delegated to sdk-services backend, keeping credentials server-side.
    """

    # SDK Services URL
    url: str = field(default_factory=lambda: os.getenv(
        "SDK_SERVICES_URL",
        ""  # Empty = disabled, use direct Docker/Keycloak access
    ))

    # Use SDK Services for registry push (instead of direct Docker push)
    use_for_registry: bool = field(default_factory=lambda: os.getenv(
        "SDK_SERVICES_REGISTRY",
        "false"
    ).lower() == "true")

    # Use SDK Services for role assignment (instead of direct Keycloak access)
    use_for_roles: bool = field(default_factory=lambda: os.getenv(
        "SDK_SERVICES_ROLES",
        "false"
    ).lower() == "true")

    # Request timeout in seconds
    timeout: int = field(default_factory=lambda: int(os.getenv(
        "SDK_SERVICES_TIMEOUT",
        "600"
    )))

    @property
    def enabled(self) -> bool:
        """Check if SDK Services is configured and enabled."""
        return bool(self.url) and (self.use_for_registry or self.use_for_roles)

    @property
    def registry_push_url(self) -> str:
        """Get the registry push endpoint URL."""
        return f"{self.url}/api/sdk_services/registry/push" if self.url else ""

    @property
    def roles_assign_url(self) -> str:
        """Get the role assignment endpoint URL."""
        return f"{self.url}/api/sdk_services/roles/assign" if self.url else ""


@dataclass
class DockerBuildConfig:
    """Docker build configuration data class."""

    timeout: int = 600  # seconds
    build_args: Dict[str, str] = None
    labels: Dict[str, str] = None
    pull: bool = True  # Pull base image updates
    rm: bool = True  # Remove intermediate containers
    forcerm: bool = True  # Always remove intermediate containers
    push_retries: int = 3
    push_timeout: int = 300  # seconds

    def __post_init__(self):
        """Initialize default values for mutable fields."""
        if self.build_args is None:
            self.build_args = {
                "BLACKANT_VERSION": "1.0.0",
                "BUILD_PLATFORM": "linux/amd64"
            }

        if self.labels is None:
            self.labels = {
                "blackant.sdk.version": "1.0.0",
                "blackant.build.automated": "true"
            }


class DockerConfig:
    """Central Docker configuration manager for BlackAnt SDK."""

    def __init__(self):
        """Initialize Docker configuration."""
        self.logger = get_logger("docker-config")
        self._daemon_config = None
        self._registry_config = None
        self._build_config = None
        self._sdk_services_config = None

        # Load configuration on init
        self._load_config()

    def _load_config(self) -> None:
        """Load configuration from environment and defaults."""
        self.logger.debug("Loading Docker configuration")

        # Remote Docker daemon configuration
        self._daemon_config = DockerDaemonConfig()

        # Registry configuration
        self._registry_config = DockerRegistryConfig(
            url=os.getenv("DOCKER_REGISTRY_URL", "env.blackant.app"),
            namespace=os.getenv("DOCKER_REGISTRY_NAMESPACE", "science_module"),
            username=os.getenv("DOCKER_REGISTRY_USER"),
            password=os.getenv("DOCKER_REGISTRY_PASS")
        )

        # Build configuration
        self._build_config = DockerBuildConfig(
            timeout=int(os.getenv("DOCKER_BUILD_TIMEOUT", "600")),
            push_retries=int(os.getenv("DOCKER_PUSH_RETRIES", "3")),
            push_timeout=int(os.getenv("DOCKER_PUSH_TIMEOUT", "300"))
        )

        # SDK Services configuration
        self._sdk_services_config = SdkServicesConfig()

        self.logger.info(f"Docker config loaded: daemon={self._daemon_config.base_url}, registry={self._registry_config.url}")

        if self._sdk_services_config.enabled:
            self.logger.info(f"SDK Services enabled: {self._sdk_services_config.url}")

    @property
    def daemon(self) -> DockerDaemonConfig:
        """Get Docker daemon configuration."""
        return self._daemon_config

    @property
    def registry(self) -> DockerRegistryConfig:
        """Get registry configuration."""
        return self._registry_config

    @property
    def build(self) -> DockerBuildConfig:
        """Get build configuration."""
        return self._build_config

    @property
    def sdk_services(self) -> SdkServicesConfig:
        """Get SDK Services configuration."""
        return self._sdk_services_config

    def get_full_image_name(self, service_name: str, tag: str = "latest") -> str:
        """Generate full Docker image name with registry and namespace.

        Args:
            service_name: Service name
            tag: Image tag

        Returns:
            str: Full image name (e.g., "env.blackant.app/systemdevelopers/service:tag")
        """
        return f"{self.registry.url}/{self.registry.namespace}/{service_name}:{tag}"

    def get_registry_credentials(self) -> Dict[str, Optional[str]]:
        """Get registry credentials for Docker login.

        Returns:
            dict: Registry credentials or empty dict if not configured
        """
        if self.registry.username and self.registry.password:
            return {
                "username": self.registry.username,
                "password": self.registry.password,
                "registry": self.registry.url
            }
        return {}

    def get_build_args(self, service_name: str, additional_args: Optional[Dict[str, str]] = None) -> Dict[str, str]:
        """Get build arguments for Docker build.

        Args:
            service_name: Service name to add to build args
            additional_args: Additional build arguments to merge

        Returns:
            dict: Combined build arguments
        """
        args = {
            **self.build.build_args,
            "SERVICE_NAME": service_name
        }

        if additional_args:
            args.update(additional_args)

        return args

    def get_build_labels(self, service_name: str, additional_labels: Optional[Dict[str, str]] = None) -> Dict[str, str]:
        """Get build labels for Docker build.

        Args:
            service_name: Service name to add to labels
            additional_labels: Additional labels to merge

        Returns:
            dict: Combined build labels
        """
        from datetime import datetime

        labels = {
            **self.build.labels,
            "blackant.service.name": service_name,
            "blackant.build.date": datetime.now().isoformat(),
            "blackant.registry": self.registry.url
        }

        if additional_labels:
            labels.update(additional_labels)

        return labels

    def validate_config(self) -> bool:
        """Validate Docker configuration.

        Returns:
            bool: True if configuration is valid

        Raises:
            ValueError: If configuration is invalid
        """
        # Registry validation
        if not self.registry.url:
            raise ValueError("Docker registry URL cannot be empty")

        if not self.registry.namespace:
            raise ValueError("Docker registry namespace cannot be empty")

        # Build validation
        if self.build.timeout <= 0:
            raise ValueError("Docker build timeout must be positive")

        if self.build.push_retries < 0:
            raise ValueError("Docker push retries cannot be negative")

        if self.build.push_timeout <= 0:
            raise ValueError("Docker push timeout must be positive")

        self.logger.debug("Docker configuration validation passed")
        return True

    def to_dict(self) -> Dict[str, Any]:
        """Convert configuration to dictionary.

        Returns:
            dict: Configuration as dictionary (without sensitive data)
        """
        return {
            "daemon": {
                "base_url": self.daemon.base_url,
                "use_nginx_proxy": self.daemon.use_nginx_proxy,
                "use_tls": self.daemon.use_tls,
                "timeout": self.daemon.timeout
            },
            "registry": {
                "url": self.registry.url,
                "namespace": self.registry.namespace,
                "has_credentials": bool(self.registry.username and self.registry.password)
            },
            "build": {
                "timeout": self.build.timeout,
                "push_retries": self.build.push_retries,
                "push_timeout": self.build.push_timeout,
                "pull": self.build.pull,
                "rm": self.build.rm,
                "forcerm": self.build.forcerm
            }
        }

    def __str__(self) -> str:
        """String representation of configuration."""
        return f"DockerConfig(daemon={self.daemon.base_url}, registry={self.registry.url}/{self.registry.namespace})"


# Global configuration instance - singleton pattern használata
_docker_config_instance = None


def get_docker_config() -> DockerConfig:
    """Get global Docker configuration instance.

    Returns:
        DockerConfig: Global configuration instance
    """
    global _docker_config_instance

    if _docker_config_instance is None:
        _docker_config_instance = DockerConfig()
        _docker_config_instance.validate_config()

    return _docker_config_instance


def reload_docker_config() -> DockerConfig:
    """Reload Docker configuration from environment.

    Returns:
        DockerConfig: Newly loaded configuration instance
    """
    global _docker_config_instance
    _docker_config_instance = None
    return get_docker_config()


# Environment variable documentation
"""
Environment Variables for Docker Configuration:

=== Remote Docker Daemon Configuration ===
DOCKER_USE_NGINX_PROXY: Use nginx proxy for Docker daemon access (default: true)
DOCKER_NGINX_PROXY_URL: Nginx proxy URL (default: http://localhost)
DOCKER_DAEMON_HOST: Remote Docker daemon host (default: localhost)
DOCKER_DAEMON_PORT: Remote Docker daemon port (default: 80)
DOCKER_USE_TLS: Use TLS for direct Docker connection (default: false)
DOCKER_CA_CERT: Path to CA certificate for TLS
DOCKER_CLIENT_CERT: Path to client certificate for TLS
DOCKER_CLIENT_KEY: Path to client key for TLS
DOCKER_DAEMON_TIMEOUT: Docker daemon timeout in seconds (default: 600)

=== Docker Registry Configuration ===
DOCKER_REGISTRY_URL: Docker registry URL (default: env.blackant.app)
DOCKER_REGISTRY_NAMESPACE: Registry namespace (default: systemdevelopers)
DOCKER_REGISTRY_USER: Registry username for authentication
DOCKER_REGISTRY_PASS: Registry password for authentication

=== Build Configuration ===
DOCKER_BUILD_TIMEOUT: Build timeout in seconds (default: 600)
DOCKER_PUSH_RETRIES: Number of push retries (default: 3)
DOCKER_PUSH_TIMEOUT: Push timeout in seconds (default: 300)

Example - Nginx Proxy (Recommended):
    export DOCKER_USE_NGINX_PROXY=true
    export DOCKER_NGINX_PROXY_URL="http://localhost/api/docker"
    export DOCKER_REGISTRY_URL="env.blackant.app"
    export DOCKER_REGISTRY_NAMESPACE="systemdevelopers"

Example - Direct TLS Connection:
    export DOCKER_USE_NGINX_PROXY=false
    export DOCKER_DAEMON_HOST="docker.blackant.app"
    export DOCKER_DAEMON_PORT="2376"
    export DOCKER_USE_TLS=true
    export DOCKER_CA_CERT="/etc/blackant/certs/ca.pem"
    export DOCKER_CLIENT_CERT="/etc/blackant/certs/client-cert.pem"
    export DOCKER_CLIENT_KEY="/etc/blackant/certs/client-key.pem"
"""