use super::error::BtrfsError;
use super::cmd::{filesystem_df_execute_and_parse, find_mountpoint, mount_execute, subvolume_list_execute_and_parse, umount_execute};
use super::path_utils::{ensure_block_device, path_to_str};
use super::types::{FilesystemUsageStructured, BtrfsSubvolumeEntry};
use std::fs;
use std::path::{Path, PathBuf};

#[derive(Clone)]
pub struct FilesystemManager {
    device_path: PathBuf,
    device_path_str: String,
    mount_path: Option<PathBuf>,
    is_mounted: bool,
}

impl FilesystemManager {
    /// Create a new FilesystemManager for a block device
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// use herolib_os::virt::btrfs::FilesystemManager;
    ///
    /// let manager = FilesystemManager::new("/dev/sda1")?;
    /// ```
    pub fn new(device_path: &str) -> Result<Self, BtrfsError> {
        let path = Path::new(device_path);
        ensure_block_device(path)?;
        
        let device_path_str = path_to_str(path)?.to_string();
        let device_path_buf = path.to_path_buf();
        
        let (mount_path, is_mounted) = match find_mountpoint(path) {
            Ok(mp) => (Some(mp), true),
            Err(_) => (None, false),
        };
        
        Ok(Self {
            device_path: device_path_buf,
            device_path_str,
            mount_path,
            is_mounted,
        })
    }

    /// Mount the device to the specified mountpoint
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// let mut manager = FilesystemManager::new("/dev/sda1")?;
    /// manager.mount("/mnt/btrfs")?;
    /// ```
    pub fn mount(&mut self, mountpoint: &str) -> Result<(), BtrfsError> {
        if self.is_mounted {
            return Err(BtrfsError::AlreadyMounted(format!(
                "Device {} is already mounted at {:?}",
                self.device_path_str,
                self.mount_path
            )));
        }

        let mount_path = Path::new(mountpoint);
        create_path(mount_path)?;

        let mount_path_str = path_to_str(mount_path)?;
        mount_execute(&self.device_path_str, mount_path_str)
            .map_err(|e| BtrfsError::MountFailed(format!("Failed to mount {} to {}: {}", self.device_path_str, mount_path_str, e)))?;

        self.mount_path = Some(mount_path.to_path_buf());
        self.is_mounted = true;

        Ok(())
    }

    /// Unmount the device
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// manager.unmount()?;
    /// ```
    pub fn unmount(&mut self) -> Result<(), BtrfsError> {
        if !self.is_mounted {
            return Err(BtrfsError::NotMounted(format!(
                "Device {} is not mounted",
                self.device_path_str
            )));
        }

        let mount_path = self.mount_path.as_ref()
            .ok_or_else(|| BtrfsError::NotMounted(format!(
                "Device {} has no mount path tracked",
                self.device_path_str
            )))?;
        
        let mount_path_str = path_to_str(mount_path)?;
        umount_execute(mount_path_str)
            .map_err(|e| BtrfsError::UnmountFailed(format!("Failed to unmount {}: {}", mount_path_str, e)))?;
        
        self.mount_path = None;
        self.is_mounted = false;

        Ok(())
    }

    /// Get structured filesystem usage information
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// let usage = manager.usage()?;
    /// println!("Data usage: {}/{}", usage.data.used, usage.data.total);
    /// ```
    pub fn usage(&self) -> Result<FilesystemUsageStructured, BtrfsError> {
        let filesystem_path = if let Some(ref mp) = self.mount_path {
            mp
        } else {
            return Err(BtrfsError::NotMounted(format!(
                "Cannot get filesystem usage: device {} is not mounted. Mount it first or provide a mount point path.",
                self.device_path_str
            )));
        };
        
        let filesystem_path_str = path_to_str(filesystem_path)?;
        filesystem_df_execute_and_parse(filesystem_path_str)
            .map_err(|e| BtrfsError::CommandFailed(format!("Failed to get filesystem usage for {}: {}", filesystem_path_str, e)))
    }

    /// Get the device path
    pub fn device_path(&self) -> &Path {
        &self.device_path
    }

    /// Get the device path as string
    pub fn device_path_str(&self) -> &str {
        &self.device_path_str
    }

    /// Get the mount path if mounted
    pub fn mount_path(&self) -> Option<&Path> {
        self.mount_path.as_deref()
    }

    /// Check if the device is mounted
    pub fn is_mounted(&self) -> bool {
        self.is_mounted
    }

    /// List all subvolumes in the filesystem
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// let subvolumes = manager.list_subvolumes()?;
    /// for sv in subvolumes {
    ///     println!("Subvolume: {}", sv.path);
    /// }
    /// ```
    pub fn list_subvolumes(&self) -> Result<Vec<BtrfsSubvolumeEntry>, BtrfsError> {
        let filesystem_path = if let Some(ref mp) = self.mount_path {
            mp
        } else {
            return Err(BtrfsError::NotMounted(format!(
                "Cannot list subvolumes: device {} is not mounted. Mount it first or provide a mount point path.",
                self.device_path_str
            )));
        };
        
        let filesystem_path_str = path_to_str(filesystem_path)?;
        subvolume_list_execute_and_parse(filesystem_path_str)
            .map_err(|e| BtrfsError::CommandFailed(format!("Failed to list subvolumes in {}: {}", filesystem_path_str, e)))
    }
}

/// Create a directory path if it doesn't exist
fn create_path(path: &Path) -> Result<(), BtrfsError> {
    if !path.exists() {
        fs::create_dir_all(path)?;
    }
    Ok(())
}
