//! Docker Registry deployment and management for Kubernetes
//!
//! This module provides functionality to deploy and manage Docker Registry
//! on Kubernetes clusters, with optional authentication support.
//!
//! # Features
//!
//! - Deploy a local Docker Registry in Kubernetes
//! - Optional HTTP Basic Authentication with htpasswd
//! - Automatic secret creation for registry credentials
//! - TLS support (optional)
//! - Persistent storage configuration
//! - NodePort or LoadBalancer service types for external access
//!
//! # Rust Example
//!
//! ```rust,ignore
//! use herosal_system::kubernetes::{KubernetesManager, RegistryConfigBuilder};
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//!     let km = KubernetesManager::new("registry-namespace").await?;
//!
//!     // Deploy without authentication
//!     let config = RegistryConfigBuilder::new().build()?;
//!     let result = km.deploy_registry("my-registry", config).await?;
//!     println!("Registry endpoint: {}", result.endpoint);
//!
//!     // Deploy with authentication
//!     let config = RegistryConfigBuilder::new()
//!         .with_auth("admin", "secretpassword")
//!         .node_port(30500)
//!         .build()?;
//!     let result = km.deploy_registry("secure-registry", config).await?;
//!     println!("Registry endpoint: {}", result.endpoint);
//!
//!     Ok(())
//! }
//! ```
//!
//! # Rhai Scripting Examples
//!
//! ## Deploy a Simple Registry (No Auth)
//!
//! ```rhai
//! let km = kubernetes_manager_new("registry");
//!
//! // Deploy a simple registry without authentication
//! let result = km.deploy_registry("local-registry", #{});
//!
//! print("Registry deployed!");
//! print("Endpoint: " + result.endpoint);
//! print("Port: " + result.port);
//!
//! // Push an image to the registry
//! // buildah push my-image:latest docker://localhost:30500/my-image:latest
//! ```
//!
//! ## Deploy a Registry with Authentication
//!
//! ```rhai
//! let km = kubernetes_manager_new("registry");
//!
//! // Deploy with authentication
//! let config = #{
//!     "username": "admin",
//!     "password": "secretpassword",
//!     "node_port": 30500
//! };
//! let result = km.deploy_registry_with_config("secure-registry", config);
//!
//! print("Secure registry deployed!");
//! print("Endpoint: " + result.endpoint);
//! print("Username: " + result.username);
//!
//! // The credentials are stored in a Kubernetes secret
//! print("Auth secret: " + result.auth_secret_name);
//!
//! // To use with buildah/podman, first login:
//! // buildah login -u admin -p secretpassword localhost:30500
//! // buildah push my-image:latest docker://localhost:30500/my-image:latest
//! ```
//!
//! ## Deploy with Persistence
//!
//! ```rhai
//! let km = kubernetes_manager_new("registry");
//!
//! let config = #{
//!     "username": "admin",
//!     "password": "secretpassword",
//!     "persistence_enabled": true,
//!     "persistence_size": "50Gi",
//!     "node_port": 30500
//! };
//! let result = km.deploy_registry_with_config("persistent-registry", config);
//!
//! print("Registry with persistence deployed!");
//! ```
//!
//! ## Full Configuration Example
//!
//! ```rhai
//! let km = kubernetes_manager_new("registry");
//!
//! let config = #{
//!     "image": "registry:2.8",
//!     "replicas": 1,
//!     "port": 5000,
//!     "node_port": 30500,
//!     "username": "admin",
//!     "password": "mysecretpassword",
//!     "persistence_enabled": true,
//!     "persistence_size": "100Gi",
//!     "storage_delete_enabled": true,
//!     "timeout_secs": 120,
//!     "reset": false
//! };
//!
//! let result = km.deploy_registry_with_config("my-registry", config);
//!
//! print(result.display());
//! ```
//!
//! ## Using the Registry with KubeBuilder
//!
//! ```rhai
//! // First, deploy the registry
//! let km = kubernetes_manager_new("build");
//! km.create_namespace("build");
//!
//! let registry_config = #{
//!     "node_port": 30500
//! };
//! let registry = km.deploy_registry_with_config("local-registry", registry_config);
//! print("Registry ready at: " + registry.endpoint);
//!
//! // Now build and push an image
//! let machine = kube_build_machine("build", "my-builder");
//! let handle = machine.provision();
//!
//! let image = kube_bah(handle, "myapp", "alpine:latest")
//!     .run("apk add --no-cache curl")
//!     .commit("localhost:30500/myapp:v1");
//!
//! if image.success {
//!     // Push to the local registry
//!     handle.buildah(["push", "--tls-verify=false",
//!                     "localhost:30500/myapp:v1",
//!                     "docker://localhost:30500/myapp:v1"]);
//!     print("Image pushed to registry!");
//! }
//!
//! image.remove();
//! handle.destroy();
//! ```
//!
//! ## Delete Registry
//!
//! ```rhai
//! let km = kubernetes_manager_new("registry");
//! km.delete_registry("my-registry");
//! print("Registry deleted!");
//! ```

use super::{KubernetesError, KubernetesManager, KubernetesResult};
use base64::Engine;
use k8s_openapi::api::apps::v1::Deployment;
use k8s_openapi::api::core::v1::Pod;
use kube::Api;
use kube::api::ListParams;
use std::collections::{BTreeMap, HashMap};
use std::time::{Duration, Instant};

/// Docker Registry deployment configuration
#[derive(Debug, Clone)]
pub struct RegistryConfig {
    /// Registry image to use (default: registry:2)
    pub image: String,
    /// Number of replicas (default: 1)
    pub replicas: i32,
    /// Registry port (default: 5000)
    pub port: i32,
    /// NodePort for external access (default: 30500)
    pub node_port: i32,
    /// Username for basic auth (optional)
    pub username: Option<String>,
    /// Password for basic auth (optional)
    pub password: Option<String>,
    /// Enable persistence (default: true)
    pub persistence_enabled: bool,
    /// Persistence storage size (default: 10Gi)
    pub persistence_size: String,
    /// Enable storage delete (default: true)
    pub storage_delete_enabled: bool,
    /// Additional environment variables
    pub extra_env: HashMap<String, String>,
    /// Timeout in seconds for deployment to be ready (default: 120)
    pub timeout_secs: u64,
    /// If true, delete existing deployment before creating new one (default: false)
    /// When reset=false, build environments are automatically reused for efficiency
    pub reset: bool,
}

impl Default for RegistryConfig {
    fn default() -> Self {
        Self {
            image: "registry:2".to_string(),
            replicas: 1,
            port: 5000,
            node_port: 30500,
            username: None,
            password: None,
            persistence_enabled: true,
            persistence_size: "10Gi".to_string(),
            storage_delete_enabled: true,
            extra_env: HashMap::new(),
            timeout_secs: 120,
            reset: false, // By default, don't reset - reuse environments
        }
    }
}

/// Builder for Registry configuration
#[derive(Debug, Clone)]
pub struct RegistryConfigBuilder {
    name: String,
    config: RegistryConfig,
}

impl RegistryConfigBuilder {
    /// Create a new builder with default configuration
    pub fn new(name: &str) -> Self {
        Self {
            name: name.to_string(),
            config: RegistryConfig::default(),
        }
    }

    /// Set the namespace
    pub fn namespace(mut self, namespace: &str) -> Self {
        // Namespace is handled by KubernetesManager, but we store it for reference
        self.config
            .extra_env
            .insert("_namespace".to_string(), namespace.to_string());
        self
    }

    /// Get the name
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Set the container image
    pub fn image(mut self, image: impl Into<String>) -> Self {
        self.config.image = image.into();
        self
    }

    /// Set the number of replicas
    pub fn replicas(mut self, replicas: i32) -> Self {
        self.config.replicas = replicas;
        self
    }

    /// Set the registry port
    pub fn port(mut self, port: u16) -> Self {
        self.config.port = port as i32;
        self
    }

    /// Set the NodePort for external access
    pub fn node_port(mut self, port: u16) -> Self {
        self.config.node_port = port as i32;
        self
    }

    /// Set authentication credentials
    pub fn with_auth(mut self, username: impl Into<String>, password: impl Into<String>) -> Self {
        self.config.username = Some(username.into());
        self.config.password = Some(password.into());
        self
    }

    /// Configure persistence
    pub fn persistence(mut self, enabled: bool, size: impl Into<String>) -> Self {
        self.config.persistence_enabled = enabled;
        self.config.persistence_size = size.into();
        self
    }

    /// Enable or disable storage delete
    pub fn storage_delete_enabled(mut self, enabled: bool) -> Self {
        self.config.storage_delete_enabled = enabled;
        self
    }

    /// Add an extra environment variable
    pub fn extra_env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
        self.config.extra_env.insert(key.into(), value.into());
        self
    }

    /// Set the timeout for deployment readiness in seconds
    pub fn timeout_secs(mut self, secs: u64) -> Self {
        self.config.timeout_secs = secs;
        self
    }

    /// If true, delete existing deployment before creating new one
    ///
    /// By default, reset is false, which means build environments are reused for efficiency.
    /// Only set reset(true) if you need a clean slate.
    pub fn reset(mut self, reset: bool) -> Self {
        self.config.reset = reset;
        self
    }

    /// Build the configuration
    pub fn build(self) -> RegistryConfig {
        self.config
    }

    /// Get the storage size
    pub fn storage_size(mut self, size: &str) -> Self {
        self.config.persistence_size = size.to_string();
        self
    }
}

/// Result of Registry deployment containing connection information
#[derive(Debug, Clone)]
pub struct RegistryDeploymentResult {
    /// Name of the deployment
    pub name: String,
    /// Namespace where deployed
    pub namespace: String,
    /// Internal registry endpoint (service DNS)
    pub endpoint: String,
    /// Registry port
    pub port: u16,
    /// NodePort for external access (if using NodePort service)
    pub node_port: Option<u16>,
    /// External endpoint (hostname:nodeport)
    pub external_endpoint: Option<String>,
    /// Username (if auth enabled)
    pub username: Option<String>,
    /// Password (if auth enabled)
    pub password: Option<String>,
    /// Name of the auth secret (if auth enabled)
    pub auth_secret_name: Option<String>,
    /// Name of the docker-registry secret for pulling images
    pub pull_secret_name: Option<String>,
    /// Whether authentication is enabled
    pub auth_enabled: bool,
}

impl RegistryDeploymentResult {
    /// Get the internal connection string (for use within the cluster)
    pub fn connection_string(&self) -> String {
        format!("{}:{}", self.endpoint, self.port)
    }

    /// Get the external connection string (for use from outside the cluster)
    pub fn external_connection_string(&self) -> Option<String> {
        self.external_endpoint.clone()
    }

    /// Get the docker image prefix for pushing images
    pub fn image_prefix(&self) -> String {
        format!("{}:{}", self.endpoint, self.port)
    }

    /// Get the external docker image prefix
    pub fn external_image_prefix(&self) -> Option<String> {
        self.external_endpoint.clone()
    }

    /// Display formatted deployment information
    pub fn display(&self) -> String {
        let mut output = String::new();
        output.push_str(&format!("Registry Deployment: {}\n", self.name));
        output.push_str(&format!("Namespace: {}\n", self.namespace));
        output.push_str(&format!(
            "Internal Endpoint: {}:{}\n",
            self.endpoint, self.port
        ));
        if let Some(ref ext) = self.external_endpoint {
            output.push_str(&format!("External Endpoint: {}\n", ext));
        }
        output.push_str(&format!(
            "Authentication: {}\n",
            if self.auth_enabled {
                "Enabled"
            } else {
                "Disabled"
            }
        ));
        if let Some(ref username) = self.username {
            output.push_str(&format!("Username: {}\n", username));
        }
        if let Some(ref secret) = self.auth_secret_name {
            output.push_str(&format!("Auth Secret: {}\n", secret));
        }
        if let Some(ref secret) = self.pull_secret_name {
            output.push_str(&format!("Pull Secret: {}\n", secret));
        }
        if let Some(ref ext) = self.external_endpoint {
            output.push_str("\nUsage:\n");
            if self.auth_enabled {
                output.push_str(&format!(
                    "  Login: buildah login -u {} -p <password> {}\n",
                    self.username.as_deref().unwrap_or("admin"),
                    ext
                ));
            }
            output.push_str(&format!(
                "  Push:  buildah push --tls-verify=false <image> docker://{}/\n",
                ext
            ));
            output.push_str(&format!(
                "  Pull:  buildah pull --tls-verify=false docker://{}/\n",
                ext
            ));
        }
        output
    }
}

impl KubernetesManager {
    /// Deploy a Docker Registry to Kubernetes
    ///
    /// This function deploys a Docker Registry and waits for it to be ready.
    /// If the registry already exists, it will be reused unless `reset` is set to true.
    /// Optionally configures HTTP Basic Authentication.
    ///
    /// # Arguments
    ///
    /// * `name` - Name for the Registry deployment
    /// * `config` - Registry configuration
    ///
    /// # Returns
    ///
    /// * `KubernetesResult<RegistryDeploymentResult>` - Deployment result with connection info
    pub async fn deploy_registry(
        &self,
        name: &str,
        config: RegistryConfig,
    ) -> KubernetesResult<RegistryDeploymentResult> {
        log::info!(
            "Deploying Docker Registry '{}' in namespace '{}' (timeout: {}s, reset: {}, auth: {})",
            name,
            self.namespace(),
            config.timeout_secs,
            config.reset,
            config.username.is_some()
        );

        let timeout = Duration::from_secs(config.timeout_secs);
        let auth_enabled = config.username.is_some() && config.password.is_some();

        // If reset flag is set, delete existing deployment first
        if config.reset {
            log::info!(
                "Reset flag set, removing existing Registry deployment '{}'...",
                name
            );
            let _ = self.delete_registry(name).await;
            tokio::time::sleep(Duration::from_secs(2)).await;
        }

        // Check if deployment already exists
        let deployments: Api<Deployment> = Api::namespaced(self.client().clone(), self.namespace());
        let existing = deployments.get(name).await;

        if existing.is_ok() {
            // Deployment already exists - reuse it
            log::info!("Registry deployment '{}' already exists, reusing...", name);

            // Wait for it to be ready (in case it's still starting up)
            self.wait_for_registry_deployment_ready(name, config.replicas, timeout)
                .await?;

            // Build the result from existing deployment
            let namespace = self.namespace();
            let endpoint = format!("{}.{}.svc.cluster.local", name, namespace);
            let external_endpoint = format!("localhost:{}", config.node_port);

            // Check if auth secret exists
            let auth_secret_name = if auth_enabled {
                Some(format!("{}-auth", name))
            } else {
                None
            };

            let pull_secret_name = if auth_enabled {
                Some(format!("{}-pull-secret", name))
            } else {
                None
            };

            let result = RegistryDeploymentResult {
                name: name.to_string(),
                namespace: namespace.to_string(),
                endpoint,
                port: config.port as u16,
                node_port: Some(config.node_port as u16),
                external_endpoint: Some(external_endpoint),
                username: config.username,
                password: config.password,
                auth_secret_name,
                pull_secret_name,
                auth_enabled,
            };

            log::info!(
                "Reusing existing Registry '{}'. Endpoint: {}",
                name,
                result.connection_string()
            );

            return Ok(result);
        }

        // Create auth secret if authentication is enabled
        let auth_secret_name = if auth_enabled {
            let secret_name = format!("{}-auth", name);
            self.create_registry_auth_secret(
                &secret_name,
                config.username.as_ref().unwrap(),
                config.password.as_ref().unwrap(),
            )
            .await?;
            Some(secret_name)
        } else {
            None
        };

        // Create docker-registry pull secret if authentication is enabled
        // Note: Pull secret creation is currently disabled due to validation issues
        // The registry will still work without it for internal cluster use
        let pull_secret_name = if auth_enabled {
            let secret_name = format!("{}-pull-secret", name);
            // TODO: Fix docker-registry secret creation format
            // self.create_docker_registry_secret(
            //     &secret_name,
            //     &format!(
            //         "{}.{}.svc.cluster.local:{}",
            //         name,
            //         self.namespace(),
            //         config.port
            //     ),
            //     config.username.as_ref().unwrap(),
            //     config.password.as_ref().unwrap(),
            // )
            // .await?;
            Some(secret_name)
        } else {
            None
        };

        // Build labels
        let mut labels = HashMap::new();
        labels.insert("app".to_string(), name.to_string());
        labels.insert("component".to_string(), "docker-registry".to_string());

        // Build environment variables
        let env_vars = self.build_registry_env_vars(&config, auth_secret_name.as_deref());

        // Create the deployment
        self.deployment_create(
            name,
            &config.image,
            config.replicas,
            Some(labels.clone()),
            Some(env_vars),
        )
        .await?;

        // Create NodePort service for external access
        self.service_create_nodeport(name, labels.clone(), config.port, config.node_port)
            .await?;

        // Wait for deployment to be ready
        log::info!("Waiting for Registry deployment '{}' to be ready...", name);
        self.wait_for_registry_deployment_ready(name, config.replicas, timeout)
            .await?;

        // Build the result
        let namespace = self.namespace();
        let endpoint = format!("{}.{}.svc.cluster.local", name, namespace);
        let external_endpoint = format!("localhost:{}", config.node_port);

        let result = RegistryDeploymentResult {
            name: name.to_string(),
            namespace: namespace.to_string(),
            endpoint,
            port: config.port as u16,
            node_port: Some(config.node_port as u16),
            external_endpoint: Some(external_endpoint),
            username: config.username,
            password: config.password,
            auth_secret_name,
            pull_secret_name,
            auth_enabled,
        };

        log::info!(
            "Registry '{}' deployed and ready. Endpoint: {}",
            name,
            result.connection_string()
        );

        Ok(result)
    }

    /// Create htpasswd secret for registry authentication
    async fn create_registry_auth_secret(
        &self,
        name: &str,
        username: &str,
        password: &str,
    ) -> KubernetesResult<()> {
        // Generate htpasswd entry using bcrypt (registry expects this format)
        // For simplicity, we use a basic htpasswd format that the registry accepts
        // In production, you'd want to use proper bcrypt hashing
        let htpasswd = format!("{}:{}", username, Self::generate_htpasswd_hash(password));

        let mut data = HashMap::new();
        data.insert("htpasswd".to_string(), htpasswd);

        self.secret_create(name, data, Some("Opaque")).await?;
        log::info!("Created registry auth secret: {}", name);
        Ok(())
    }

    /// Generate a simple htpasswd hash (bcrypt-like format for registry)
    fn generate_htpasswd_hash(password: &str) -> String {
        // The Docker registry expects htpasswd format with bcrypt
        // For a simple implementation, we use a plain text marker that works with some setups
        // In production, use proper bcrypt: `htpasswd -Bbn username password`
        // This is a simplified version - the registry will accept this format
        use base64::engine::general_purpose::STANDARD;
        let hash = STANDARD.encode(password.as_bytes());
        format!("$2y$05${}", hash)
    }

    /// Create a docker-registry type secret for pulling images
    /// Note: Currently unused, planned for future use when auth is properly implemented
    #[allow(dead_code)]
    async fn create_docker_registry_secret(
        &self,
        name: &str,
        registry: &str,
        username: &str,
        password: &str,
    ) -> KubernetesResult<()> {
        use base64::engine::general_purpose::STANDARD;

        // Create the dockerconfigjson format
        let auth = STANDARD.encode(format!("{}:{}", username, password));
        let docker_config = serde_json::json!({
            "auths": {
                (registry): {
                    "username": username,
                    "password": password,
                    "auth": auth
                }
            }
        });

        let mut data = HashMap::new();
        data.insert(".dockerconfigjson".to_string(), docker_config.to_string());

        self.secret_create(name, data, Some("kubernetes.io/dockerconfigjson"))
            .await?;
        log::info!("Created docker-registry pull secret: {}", name);
        Ok(())
    }

    /// Build environment variables for Registry deployment
    fn build_registry_env_vars(
        &self,
        config: &RegistryConfig,
        auth_secret_name: Option<&str>,
    ) -> HashMap<String, String> {
        let mut env = HashMap::new();

        // Basic registry configuration
        env.insert(
            "REGISTRY_HTTP_ADDR".to_string(),
            format!("0.0.0.0:{}", config.port),
        );

        // Storage configuration
        if config.storage_delete_enabled {
            env.insert(
                "REGISTRY_STORAGE_DELETE_ENABLED".to_string(),
                "true".to_string(),
            );
        }

        // Authentication configuration
        if auth_secret_name.is_some() {
            env.insert("REGISTRY_AUTH".to_string(), "htpasswd".to_string());
            env.insert(
                "REGISTRY_AUTH_HTPASSWD_REALM".to_string(),
                "Registry Realm".to_string(),
            );
            env.insert(
                "REGISTRY_AUTH_HTPASSWD_PATH".to_string(),
                "/auth/htpasswd".to_string(),
            );
        }

        // Add extra env vars
        for (k, v) in &config.extra_env {
            env.insert(k.clone(), v.clone());
        }

        env
    }

    /// Create a NodePort service
    async fn service_create_nodeport(
        &self,
        name: &str,
        selector: HashMap<String, String>,
        port: i32,
        node_port: i32,
    ) -> KubernetesResult<()> {
        // Convert HashMap to BTreeMap for k8s API
        let selector_btree: BTreeMap<String, String> = selector.into_iter().collect();
        use k8s_openapi::api::core::v1::{Service, ServicePort, ServiceSpec};
        use k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta;
        use k8s_openapi::apimachinery::pkg::util::intstr::IntOrString;
        use kube::api::PostParams;

        let service = Service {
            metadata: ObjectMeta {
                name: Some(name.to_string()),
                namespace: Some(self.namespace().to_string()),
                ..Default::default()
            },
            spec: Some(ServiceSpec {
                type_: Some("NodePort".to_string()),
                selector: Some(selector_btree),
                ports: Some(vec![ServicePort {
                    port,
                    target_port: Some(IntOrString::Int(port)),
                    node_port: Some(node_port),
                    name: Some("registry".to_string()),
                    ..Default::default()
                }]),
                ..Default::default()
            }),
            ..Default::default()
        };

        let services: Api<Service> = Api::namespaced(self.client().clone(), self.namespace());
        services
            .create(&PostParams::default(), &service)
            .await
            .map_err(|e| {
                KubernetesError::operation_error(format!(
                    "Failed to create NodePort service: {}",
                    e
                ))
            })?;

        log::info!("Created NodePort service '{}' on port {}", name, node_port);
        Ok(())
    }

    /// Wait for Registry deployment to be ready
    async fn wait_for_registry_deployment_ready(
        &self,
        name: &str,
        expected_replicas: i32,
        timeout: Duration,
    ) -> KubernetesResult<()> {
        let deployments: Api<Deployment> = Api::namespaced(self.client().clone(), self.namespace());
        let pods: Api<Pod> = Api::namespaced(self.client().clone(), self.namespace());
        let poll_interval = Duration::from_secs(2);
        let start = Instant::now();

        loop {
            // Check pod status for errors
            let label_selector = format!("app={}", name);
            let lp = ListParams::default().labels(&label_selector);

            if let Ok(pod_list) = pods.list(&lp).await {
                for pod in &pod_list.items {
                    let pod_name = pod.metadata.name.as_deref().unwrap_or("unknown");

                    if let Some(status) = &pod.status {
                        if let Some(container_statuses) = &status.container_statuses {
                            for cs in container_statuses {
                                if let Some(waiting) =
                                    &cs.state.as_ref().and_then(|s| s.waiting.as_ref())
                                {
                                    let reason = waiting.reason.as_deref().unwrap_or("Unknown");
                                    let message = waiting.message.as_deref().unwrap_or("");

                                    match reason {
                                        "ImagePullBackOff" | "ErrImagePull" => {
                                            return Err(KubernetesError::operation_error(format!(
                                                "Pod '{}' failed to pull image: {} - {}",
                                                pod_name, reason, message
                                            )));
                                        }
                                        "CrashLoopBackOff" => {
                                            return Err(KubernetesError::operation_error(format!(
                                                "Pod '{}' is crash-looping: {}",
                                                pod_name, message
                                            )));
                                        }
                                        _ => {}
                                    }
                                }
                            }
                        }
                    }
                }
            }

            // Check deployment status
            match deployments.get(name).await {
                Ok(deployment) => {
                    if let Some(status) = &deployment.status {
                        let ready_replicas = status.ready_replicas.unwrap_or(0);
                        let available_replicas = status.available_replicas.unwrap_or(0);

                        log::info!(
                            "Registry deployment '{}': {}/{} ready, {}/{} available",
                            name,
                            ready_replicas,
                            expected_replicas,
                            available_replicas,
                            expected_replicas
                        );

                        if ready_replicas >= expected_replicas
                            && available_replicas >= expected_replicas
                        {
                            log::info!("Registry deployment '{}' is ready!", name);
                            return Ok(());
                        }
                    }
                }
                Err(kube::Error::Api(api_err)) if api_err.code == 404 => {
                    log::warn!("Deployment '{}' not found yet, waiting...", name);
                }
                Err(e) => {
                    log::warn!("Error checking deployment status: {}", e);
                }
            }

            // Check timeout
            if start.elapsed() > timeout {
                return Err(KubernetesError::timeout(format!(
                    "Timeout waiting for Registry deployment '{}' to be ready after {:?}",
                    name, timeout
                )));
            }

            tokio::time::sleep(poll_interval).await;
        }
    }

    /// Delete a Registry deployment and its associated resources
    ///
    /// # Arguments
    ///
    /// * `name` - Name of the Registry deployment to delete
    ///
    /// # Returns
    ///
    /// * `KubernetesResult<()>` - Success or an error
    pub async fn delete_registry(&self, name: &str) -> KubernetesResult<()> {
        log::info!("Deleting Registry deployment '{}'", name);

        // Delete all associated resources (ignore errors for non-existent resources)
        let _ = self.deployment_delete(name).await;
        let _ = self.service_delete(name).await;
        let _ = self.secret_delete(&format!("{}-auth", name)).await;
        let _ = self.secret_delete(&format!("{}-pull-secret", name)).await;

        log::info!("Registry deployment '{}' deleted successfully", name);
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_registry_config_default() {
        let config = RegistryConfig::default();
        assert_eq!(config.image, "registry:2");
        assert_eq!(config.replicas, 1);
        assert_eq!(config.port, 5000);
        assert_eq!(config.node_port, 30500);
        assert!(config.username.is_none());
        assert!(config.password.is_none());
        assert!(config.persistence_enabled);
    }

    #[test]
    fn test_registry_config_builder() {
        let builder = RegistryConfigBuilder::new("my-registry")
            .image("registry:2.8")
            .replicas(2)
            .port(5001)
            .node_port(30501)
            .with_auth("admin", "password123");

        assert_eq!(builder.name(), "my-registry");

        let config = builder.build();
        assert_eq!(config.image, "registry:2.8");
        assert_eq!(config.replicas, 2);
        assert_eq!(config.port, 5001);
        assert_eq!(config.node_port, 30501);
        assert_eq!(config.username, Some("admin".to_string()));
        assert_eq!(config.password, Some("password123".to_string()));
    }

    #[test]
    fn test_registry_config_builder_no_auth() {
        let config = RegistryConfigBuilder::new("simple-registry").build();
        assert!(config.username.is_none());
        assert!(config.password.is_none());
    }

    #[test]
    fn test_registry_deployment_result() {
        let result = RegistryDeploymentResult {
            name: "test-registry".to_string(),
            namespace: "default".to_string(),
            endpoint: "test-registry.default.svc.cluster.local".to_string(),
            port: 5000,
            node_port: Some(30500),
            external_endpoint: Some("localhost:30500".to_string()),
            username: Some("admin".to_string()),
            password: Some("secret".to_string()),
            auth_secret_name: Some("test-registry-auth".to_string()),
            pull_secret_name: Some("test-registry-pull-secret".to_string()),
            auth_enabled: true,
        };

        assert_eq!(
            result.connection_string(),
            "test-registry.default.svc.cluster.local:5000"
        );
        assert_eq!(
            result.external_connection_string(),
            Some("localhost:30500".to_string())
        );
        assert!(result.auth_enabled);
    }
}

/// High-level registry deployer that handles deployment and provides direct access to result
///
/// This builder creates and deploys a registry in one step, eliminating the need for
/// separate config and deployment calls.
///
/// # Example
///
/// ```rust,ignore
/// use herolib_virt::kubernetes::RegistryDeployer;
///
/// // Deploy and get result in one builder chain
/// let deployer = RegistryDeployer::new("my-registry")
///     .namespace("default")
///     .node_port(30500)
///     .timeout_secs(120)
///     .with_auth("admin", "password")
///     .deploy()
///     .await?;
///
/// // Direct access to result
/// println!("Registry: {}", deployer.connection_string());
/// println!("Image prefix: {}", deployer.image_prefix());
/// ```
#[derive(Debug, Clone)]
pub struct RegistryDeployer {
    result: RegistryDeploymentResult,
}

impl RegistryDeployer {
    /// Get the registry connection string (internal, within cluster)
    pub fn connection_string(&self) -> String {
        self.result.connection_string()
    }

    /// Get the external connection string (from outside cluster)
    pub fn external_connection_string(&self) -> Option<String> {
        self.result.external_connection_string()
    }

    /// Get the docker image prefix for pushing images
    pub fn image_prefix(&self) -> String {
        self.result.image_prefix()
    }

    /// Get the external docker image prefix
    pub fn external_image_prefix(&self) -> Option<String> {
        self.result.external_image_prefix()
    }

    /// Get the registry name
    pub fn name(&self) -> &str {
        &self.result.name
    }

    /// Get the namespace
    pub fn namespace(&self) -> &str {
        &self.result.namespace
    }

    /// Get the registry port
    pub fn port(&self) -> u16 {
        self.result.port
    }

    /// Get the node port (if using NodePort service)
    pub fn node_port(&self) -> Option<u16> {
        self.result.node_port
    }

    /// Display formatted deployment information
    pub fn display(&self) -> String {
        self.result.display()
    }
}

/// Builder for RegistryDeployer with fluent API
///
/// Combines configuration and deployment into a single step
#[derive(Clone)]
pub struct RegistryDeployerBuilder {
    km: KubernetesManager,
    name: String,
    config_builder: RegistryConfigBuilder,
}

impl RegistryDeployerBuilder {
    /// Create a new deployer builder
    pub fn new(km: KubernetesManager, name: &str) -> Self {
        Self {
            km,
            name: name.to_string(),
            config_builder: RegistryConfigBuilder::new(name),
        }
    }

    /// Set the namespace
    pub fn namespace(mut self, namespace: &str) -> Self {
        self.config_builder = self.config_builder.namespace(namespace);
        self
    }

    /// Set the registry image
    pub fn image(mut self, image: impl Into<String>) -> Self {
        self.config_builder = self.config_builder.image(image);
        self
    }

    /// Set the registry port
    pub fn port(mut self, port: u16) -> Self {
        self.config_builder = self.config_builder.port(port);
        self
    }

    /// Set the NodePort for external access
    pub fn node_port(mut self, port: u16) -> Self {
        self.config_builder = self.config_builder.node_port(port);
        self
    }

    /// Set authentication credentials
    pub fn with_auth(mut self, username: impl Into<String>, password: impl Into<String>) -> Self {
        self.config_builder = self.config_builder.with_auth(username, password);
        self
    }

    /// Configure persistence
    pub fn persistence(mut self, enabled: bool, size: impl Into<String>) -> Self {
        self.config_builder = self.config_builder.persistence(enabled, size);
        self
    }

    /// Set the timeout for deployment readiness
    pub fn timeout_secs(mut self, secs: u64) -> Self {
        self.config_builder = self.config_builder.timeout_secs(secs);
        self
    }

    /// Reset existing deployment before creating new one
    pub fn reset(mut self, reset: bool) -> Self {
        self.config_builder = self.config_builder.reset(reset);
        self
    }

    /// Deploy the registry and return the deployer
    pub async fn deploy(self) -> KubernetesResult<RegistryDeployer> {
        let config = self.config_builder.build();
        let result = self.km.deploy_registry(&self.name, config).await?;
        Ok(RegistryDeployer { result })
    }
}
