//! Volume management operations.

use crate::DockerClient;
use crate::error::{DockerError, Result};
use bollard::models::*;
use bollard::volume::*;
use tracing::{debug, info};

/// Volume operations manager.
pub struct Volumes<'a> {
    client: &'a DockerClient,
}

impl<'a> Volumes<'a> {
    pub(crate) fn new(client: &'a DockerClient) -> Self {
        Self { client }
    }

    /// Create a new volume.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use lmrc_docker::DockerClient;
    ///
    /// #[tokio::main]
    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
    ///     let client = DockerClient::new()?;
    ///     let volume = client.volumes()
    ///         .create("my-volume")
    ///         .await?;
    ///     println!("Created volume: {}", volume.name);
    ///     Ok(())
    /// }
    /// ```
    pub async fn create(&self, name: impl Into<String>) -> Result<Volume> {
        let name = name.into();
        info!("Creating volume: {}", name);

        let config = CreateVolumeOptions {
            name: name.clone(),
            ..Default::default()
        };

        self.client
            .docker
            .create_volume(config)
            .await
            .map_err(|e| DockerError::Other(format!("Failed to create volume: {}", e)))
    }

    /// List all volumes.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use lmrc_docker::DockerClient;
    ///
    /// #[tokio::main]
    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
    ///     let client = DockerClient::new()?;
    ///     let volumes = client.volumes().list().await?;
    ///     for volume in volumes {
    ///         println!("{}", volume.name);
    ///     }
    ///     Ok(())
    /// }
    /// ```
    pub async fn list(&self) -> Result<Vec<Volume>> {
        let response = self
            .client
            .docker
            .list_volumes(Some(
                bollard::query_parameters::ListVolumesOptions::default(),
            ))
            .await
            .map_err(|e| DockerError::Other(format!("Failed to list volumes: {}", e)))?;

        Ok(response.volumes.unwrap_or_default())
    }

    /// Get a reference to a specific volume.
    pub fn get(&self, name: impl Into<String>) -> VolumeRef<'a> {
        VolumeRef::new(self.client, name.into())
    }

    /// Prune unused volumes.
    pub async fn prune(&self) -> Result<VolumePruneResponse> {
        info!("Pruning unused volumes...");
        self.client
            .docker
            .prune_volumes(Some(
                bollard::query_parameters::PruneVolumesOptions::default(),
            ))
            .await
            .map_err(|e| DockerError::Other(format!("Failed to prune volumes: {}", e)))
    }
}

/// Reference to a specific volume.
pub struct VolumeRef<'a> {
    client: &'a DockerClient,
    name: String,
}

impl<'a> VolumeRef<'a> {
    pub(crate) fn new(client: &'a DockerClient, name: String) -> Self {
        Self { client, name }
    }

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

    /// Inspect the volume to get detailed information.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use lmrc_docker::DockerClient;
    ///
    /// #[tokio::main]
    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
    ///     let client = DockerClient::new()?;
    ///     let info = client.volumes().get("my-volume").inspect().await?;
    ///     println!("Mountpoint: {}", info.mountpoint);
    ///     Ok(())
    /// }
    /// ```
    pub async fn inspect(&self) -> Result<Volume> {
        debug!("Inspecting volume: {}", self.name);
        self.client
            .docker
            .inspect_volume(&self.name)
            .await
            .map_err(|e| DockerError::VolumeNotFound(format!("{}: {}", self.name, e)))
    }

    /// Remove the volume.
    ///
    /// # Arguments
    ///
    /// * `force` - Force removal even if volume is in use
    pub async fn remove(&self, force: bool) -> Result<()> {
        info!("Removing volume: {}", self.name);

        let options = if force {
            Some(RemoveVolumeOptions { force })
        } else {
            None
        };

        self.client
            .docker
            .remove_volume(&self.name, options)
            .await
            .map_err(|e| DockerError::Other(format!("Failed to remove volume: {}", e)))
    }
}

impl DockerClient {
    /// Access volume operations.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use lmrc_docker::DockerClient;
    ///
    /// #[tokio::main]
    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
    ///     let client = DockerClient::new()?;
    ///     let volumes = client.volumes().list().await?;
    ///     Ok(())
    /// }
    /// ```
    pub fn volumes(&self) -> Volumes<'_> {
        Volumes::new(self)
    }
}
