// File: /root/code/git.threefold.info/herocode/sal/src/virt/nerdctl/container_operations.rs

use super::container_types::{Container, ContainerStatus, ResourceUsage};
use super::{NerdctlError, execute_nerdctl_command};
use crate::process::CommandResult;
use herolib_core::text::path_fix;
use serde_json;

impl Container {
    /// Start the container and verify it's running
    /// If the container hasn't been created yet, it will be created automatically.
    ///
    /// # Returns
    ///
    /// * `Result<CommandResult, NerdctlError>` - Command result or error with detailed information
    pub fn start(&self) -> Result<CommandResult, NerdctlError> {
        // If container_id is None, we need to create the container first
        let container = if self.container_id.is_none() {
            // Check if we have an image specified
            if self.image.is_none() {
                return Err(NerdctlError::Other(
                    "No image specified for container creation".to_string(),
                ));
            }

            // Clone self and create the container
            println!("Container not created yet. Creating container from image...");

            // First, try to pull the image if it doesn't exist locally
            let image = self.image.as_ref().unwrap();
            match execute_nerdctl_command(&["image", "inspect", image]) {
                Err(_) => {
                    println!("Image '{}' not found locally. Pulling image...", image);
                    if let Err(e) = execute_nerdctl_command(&["pull", image]) {
                        return Err(NerdctlError::CommandFailed(format!(
                            "Failed to pull image '{}': {}",
                            image, e
                        )));
                    }
                    println!("Image '{}' pulled successfully.", image);
                }
                Ok(_) => {
                    println!("Image '{}' found locally.", image);
                }
            }

            // Now create the container
            match self.clone().build() {
                Ok(built) => built,
                Err(e) => {
                    return Err(NerdctlError::CommandFailed(format!(
                        "Failed to create container from image '{}': {}",
                        image, e
                    )));
                }
            }
        } else {
            // Container already has an ID, use it as is
            self.clone()
        };

        if let Some(container_id) = &container.container_id {
            // First, try to start the container
            let start_result = execute_nerdctl_command(&["start", container_id]);

            // If the start command failed, return the error with details
            if let Err(err) = &start_result {
                return Err(NerdctlError::CommandFailed(format!(
                    "Failed to start container {}: {}",
                    container_id, err
                )));
            }

            // Verify the container is actually running
            match container.verify_running() {
                Ok(true) => start_result,
                Ok(false) => {
                    // Container started but isn't running - get detailed information
                    let mut error_message =
                        format!("Container {} started but is not running.", container_id);

                    // Get container status
                    if let Ok(status) = container.status() {
                        error_message.push_str(&format!(
                            "\nStatus: {}, State: {}, Health: {}",
                            status.status,
                            status.state,
                            status.health_status.unwrap_or_else(|| "N/A".to_string())
                        ));
                    }

                    // Get container logs
                    if let Ok(logs) = execute_nerdctl_command(&["logs", container_id]) {
                        if !logs.stdout.trim().is_empty() {
                            error_message.push_str(&format!(
                                "\nContainer logs (stdout):\n{}",
                                logs.stdout.trim()
                            ));
                        }
                        if !logs.stderr.trim().is_empty() {
                            error_message.push_str(&format!(
                                "\nContainer logs (stderr):\n{}",
                                logs.stderr.trim()
                            ));
                        }
                    }

                    // Get container exit code if available
                    if let Ok(inspect_result) = execute_nerdctl_command(&[
                        "inspect",
                        "--format",
                        "{{.State.ExitCode}}",
                        container_id,
                    ]) {
                        let exit_code = inspect_result.stdout.trim();
                        if !exit_code.is_empty() && exit_code != "0" {
                            error_message
                                .push_str(&format!("\nContainer exit code: {}", exit_code));
                        }
                    }

                    Err(NerdctlError::CommandFailed(error_message))
                }
                Err(err) => {
                    // Failed to verify if container is running
                    Err(NerdctlError::CommandFailed(format!(
                        "Container {} may have started, but verification failed: {}",
                        container_id, err
                    )))
                }
            }
        } else {
            Err(NerdctlError::Other(
                "Failed to create container. No container ID available.".to_string(),
            ))
        }
    }

    /// Verify if the container is running
    ///
    /// # Returns
    ///
    /// * `Result<bool, NerdctlError>` - True if running, false if not running, error if verification failed
    fn verify_running(&self) -> Result<bool, NerdctlError> {
        if let Some(container_id) = &self.container_id {
            // Use inspect to check if the container is running
            let inspect_result = execute_nerdctl_command(&[
                "inspect",
                "--format",
                "{{.State.Running}}",
                container_id,
            ]);

            match inspect_result {
                Ok(result) => {
                    let running = result.stdout.trim().to_lowercase() == "true";
                    Ok(running)
                }
                Err(err) => Err(err),
            }
        } else {
            Err(NerdctlError::Other("No container ID available".to_string()))
        }
    }

    /// Stop the container
    ///
    /// # Returns
    ///
    /// * `Result<CommandResult, NerdctlError>` - Command result or error
    pub fn stop(&self) -> Result<CommandResult, NerdctlError> {
        if let Some(container_id) = &self.container_id {
            execute_nerdctl_command(&["stop", container_id])
        } else {
            Err(NerdctlError::Other("No container ID available".to_string()))
        }
    }

    /// Remove the container
    ///
    /// # Returns
    ///
    /// * `Result<CommandResult, NerdctlError>` - Command result or error
    pub fn remove(&self) -> Result<CommandResult, NerdctlError> {
        if let Some(container_id) = &self.container_id {
            execute_nerdctl_command(&["rm", container_id])
        } else {
            Err(NerdctlError::Other("No container ID available".to_string()))
        }
    }

    /// Execute a command in the container
    ///
    /// # Arguments
    ///
    /// * `command` - The command to run
    ///
    /// # Returns
    ///
    /// * `Result<CommandResult, NerdctlError>` - Command result or error
    pub fn exec(&self, command: &str) -> Result<CommandResult, NerdctlError> {
        if let Some(container_id) = &self.container_id {
            execute_nerdctl_command(&["exec", container_id, "sh", "-c", command])
        } else {
            Err(NerdctlError::Other("No container ID available".to_string()))
        }
    }

    /// Copy files between container and local filesystem
    ///
    /// # Arguments
    ///
    /// * `source` - Source path (can be container:path or local path)
    /// * `dest` - Destination path (can be container:path or local path)
    ///
    /// # Returns
    ///
    /// * `Result<CommandResult, NerdctlError>` - Command result or error
    pub fn copy(&self, source: &str, dest: &str) -> Result<CommandResult, NerdctlError> {
        if self.container_id.is_some() {
            execute_nerdctl_command(&["cp", source, dest])
        } else {
            Err(NerdctlError::Other("No container ID available".to_string()))
        }
    }

    /// Export the container to a tarball
    ///
    /// # Arguments
    ///
    /// * `path` - Path to save the tarball
    ///
    /// # Returns
    ///
    /// * `Result<CommandResult, NerdctlError>` - Command result or error
    pub fn export(&self, path: &str) -> Result<CommandResult, NerdctlError> {
        if let Some(container_id) = &self.container_id {
            let path = path_fix(path);
            execute_nerdctl_command(&["export", "-o", &path, container_id])
        } else {
            Err(NerdctlError::Other("No container ID available".to_string()))
        }
    }

    /// Commit the container to an image
    ///
    /// # Arguments
    ///
    /// * `image_name` - Name for the new image
    ///
    /// # Returns
    ///
    /// * `Result<CommandResult, NerdctlError>` - Command result or error
    pub fn commit(&self, image_name: &str) -> Result<CommandResult, NerdctlError> {
        if let Some(container_id) = &self.container_id {
            execute_nerdctl_command(&["commit", container_id, image_name])
        } else {
            Err(NerdctlError::Other("No container ID available".to_string()))
        }
    }

    /// Get container status
    ///
    /// # Returns
    ///
    /// * `Result<ContainerStatus, NerdctlError>` - Container status or error
    pub fn status(&self) -> Result<ContainerStatus, NerdctlError> {
        if let Some(container_id) = &self.container_id {
            let result = execute_nerdctl_command(&["inspect", container_id])?;

            // Parse the JSON output
            match serde_json::from_str::<serde_json::Value>(&result.stdout) {
                Ok(json) => {
                    if let Some(container_json) = json.as_array().and_then(|arr| arr.first()) {
                        let state = container_json
                            .get("State")
                            .and_then(|state| state.get("Status"))
                            .and_then(|status| status.as_str())
                            .unwrap_or("unknown")
                            .to_string();

                        let status = container_json
                            .get("State")
                            .and_then(|state| state.get("Running"))
                            .and_then(|running| {
                                if running.as_bool().unwrap_or(false) {
                                    Some("running")
                                } else {
                                    Some("stopped")
                                }
                            })
                            .unwrap_or("unknown")
                            .to_string();

                        let created = container_json
                            .get("Created")
                            .and_then(|created| created.as_str())
                            .unwrap_or("unknown")
                            .to_string();

                        let started = container_json
                            .get("State")
                            .and_then(|state| state.get("StartedAt"))
                            .and_then(|started| started.as_str())
                            .unwrap_or("unknown")
                            .to_string();

                        // Get health status if available
                        let health_status = container_json
                            .get("State")
                            .and_then(|state| state.get("Health"))
                            .and_then(|health| health.get("Status"))
                            .and_then(|status| status.as_str())
                            .map(|s| s.to_string());

                        // Get health check output if available
                        let health_output = container_json
                            .get("State")
                            .and_then(|state| state.get("Health"))
                            .and_then(|health| health.get("Log"))
                            .and_then(|log| log.as_array())
                            .and_then(|log_array| log_array.last())
                            .and_then(|last_log| last_log.get("Output"))
                            .and_then(|output| output.as_str())
                            .map(|s| s.to_string());

                        Ok(ContainerStatus {
                            state,
                            status,
                            created,
                            started,
                            health_status,
                            health_output,
                        })
                    } else {
                        Err(NerdctlError::JsonParseError(
                            "Invalid container inspect JSON".to_string(),
                        ))
                    }
                }
                Err(e) => Err(NerdctlError::JsonParseError(format!(
                    "Failed to parse container inspect JSON: {}",
                    e
                ))),
            }
        } else {
            Err(NerdctlError::Other("No container ID available".to_string()))
        }
    }

    /// Get the health status of the container
    ///
    /// # Returns
    ///
    /// * `Result<String, NerdctlError>` - Health status or error
    pub fn health_status(&self) -> Result<String, NerdctlError> {
        if let Some(container_id) = &self.container_id {
            let result = execute_nerdctl_command(&[
                "inspect",
                "--format",
                "{{.State.Health.Status}}",
                container_id,
            ])?;
            Ok(result.stdout.trim().to_string())
        } else {
            Err(NerdctlError::Other("No container ID available".to_string()))
        }
    }

    /// Get container logs
    ///
    /// # Returns
    ///
    /// * `Result<CommandResult, NerdctlError>` - Command result or error
    pub fn logs(&self) -> Result<CommandResult, NerdctlError> {
        if let Some(container_id) = &self.container_id {
            execute_nerdctl_command(&["logs", container_id])
        } else {
            Err(NerdctlError::Other("No container ID available".to_string()))
        }
    }

    /// Get container resource usage
    ///
    /// # Returns
    ///
    /// * `Result<ResourceUsage, NerdctlError>` - Resource usage or error
    pub fn resources(&self) -> Result<ResourceUsage, NerdctlError> {
        if let Some(container_id) = &self.container_id {
            let result = execute_nerdctl_command(&["stats", "--no-stream", container_id])?;

            // Parse the output
            let lines: Vec<&str> = result.stdout.lines().collect();
            if lines.len() >= 2 {
                let headers = lines[0];
                let values = lines[1];

                let headers_vec: Vec<&str> = headers.split_whitespace().collect();
                let values_vec: Vec<&str> = values.split_whitespace().collect();

                // Find indices for each metric
                let cpu_index = headers_vec
                    .iter()
                    .position(|&h| h.contains("CPU"))
                    .unwrap_or(0);
                let mem_index = headers_vec
                    .iter()
                    .position(|&h| h.contains("MEM"))
                    .unwrap_or(0);
                let mem_perc_index = headers_vec
                    .iter()
                    .position(|&h| h.contains("MEM%"))
                    .unwrap_or(0);
                let net_in_index = headers_vec
                    .iter()
                    .position(|&h| h.contains("NET"))
                    .unwrap_or(0);
                let net_out_index = if net_in_index > 0 {
                    net_in_index + 1
                } else {
                    0
                };
                let block_in_index = headers_vec
                    .iter()
                    .position(|&h| h.contains("BLOCK"))
                    .unwrap_or(0);
                let block_out_index = if block_in_index > 0 {
                    block_in_index + 1
                } else {
                    0
                };
                let pids_index = headers_vec
                    .iter()
                    .position(|&h| h.contains("PIDS"))
                    .unwrap_or(0);

                let cpu_usage = if cpu_index < values_vec.len() {
                    values_vec[cpu_index].to_string()
                } else {
                    "unknown".to_string()
                };

                let memory_usage = if mem_index < values_vec.len() {
                    values_vec[mem_index].to_string()
                } else {
                    "unknown".to_string()
                };

                let memory_limit = if mem_index + 1 < values_vec.len() {
                    values_vec[mem_index + 1].to_string()
                } else {
                    "unknown".to_string()
                };

                let memory_percentage = if mem_perc_index < values_vec.len() {
                    values_vec[mem_perc_index].to_string()
                } else {
                    "unknown".to_string()
                };

                let network_input = if net_in_index < values_vec.len() {
                    values_vec[net_in_index].to_string()
                } else {
                    "unknown".to_string()
                };

                let network_output = if net_out_index < values_vec.len() {
                    values_vec[net_out_index].to_string()
                } else {
                    "unknown".to_string()
                };

                let block_input = if block_in_index < values_vec.len() {
                    values_vec[block_in_index].to_string()
                } else {
                    "unknown".to_string()
                };

                let block_output = if block_out_index < values_vec.len() {
                    values_vec[block_out_index].to_string()
                } else {
                    "unknown".to_string()
                };

                let pids = if pids_index < values_vec.len() {
                    values_vec[pids_index].to_string()
                } else {
                    "unknown".to_string()
                };

                Ok(ResourceUsage {
                    cpu_usage,
                    memory_usage,
                    memory_limit,
                    memory_percentage,
                    network_input,
                    network_output,
                    block_input,
                    block_output,
                    pids,
                })
            } else {
                Err(NerdctlError::ConversionError(
                    "Failed to parse stats output".to_string(),
                ))
            }
        } else {
            Err(NerdctlError::Other("No container ID available".to_string()))
        }
    }
}
