use anyhow::Result;
use std::path::Path;

use super::ssh::SshConnection;

/// High-level file transfer operations over SSH.
///
/// Provides convenient methods for uploading and downloading files/directories
/// using the underlying rsync-based file transfer.
pub struct FileTransferOps {
    connection: SshConnection,
}

impl FileTransferOps {
    /// Creates a new file transfer operations handler.
    pub fn new(connection: SshConnection) -> Self {
        Self { connection }
    }

    /// Uploads a file or directory to the remote system.
    ///
    /// For directories, uses rsync to efficiently transfer all contents.
    /// For files, also uses rsync for consistency.
    ///
    /// # Arguments
    ///
    /// * `local_path` - Path to the local file or directory
    /// * `remote_path` - Path on the remote system where content will be uploaded
    ///
    /// # Returns
    ///
    /// Returns the exit code and output from the rsync operation.
    ///
    /// # Errors
    ///
    /// Returns an error if the local path doesn't exist or the SSH operation fails.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use herolib_os::ssh::ssh::SshConnection;
    /// use herolib_os::ssh::file_transfer::FileTransferOps;
    ///
    /// let conn = SshConnection::builder()
    ///     .host("example.com")
    ///     .user("ubuntu")
    ///     .build();
    ///
    /// let ops = FileTransferOps::new(conn);
    ///
    /// // Upload a single file
    /// let (code, output) = ops.upload("/local/file.txt", "/remote/destination/")?;
    ///
    /// // Upload a directory
    /// let (code, output) = ops.upload("/local/mydir", "/remote/destination/")?;
    /// # Ok::<(), anyhow::Error>(())
    /// ```
    pub fn upload<S: Into<String>>(&self, local_path: S, remote_path: S) -> Result<(i32, String)> {
        let local_path = local_path.into();
        let remote_path = remote_path.into();

        // Verify local path exists
        if !Path::new(&local_path).exists() {
            anyhow::bail!("Local path does not exist: {}", local_path);
        }

        // Use the file transfer builder from the connection
        let transfer = self.connection.file_transfer();
        transfer.push(&local_path, &remote_path)
    }

    /// Downloads a file or directory from the remote system.
    ///
    /// For directories, uses rsync to efficiently transfer all contents.
    /// For files, also uses rsync for consistency.
    ///
    /// # Arguments
    ///
    /// * `remote_path` - Path on the remote system to download
    /// * `local_path` - Local destination path
    ///
    /// # Returns
    ///
    /// Returns the exit code and output from the rsync operation.
    ///
    /// # Errors
    ///
    /// Returns an error if the SSH operation fails.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use herolib_os::ssh::ssh::SshConnection;
    /// use herolib_os::ssh::file_transfer::FileTransferOps;
    ///
    /// let conn = SshConnection::builder()
    ///     .host("example.com")
    ///     .user("ubuntu")
    ///     .build();
    ///
    /// let ops = FileTransferOps::new(conn);
    ///
    /// // Download a single file
    /// let (code, output) = ops.download("/remote/file.txt", "/local/destination/")?;
    ///
    /// // Download a directory
    /// let (code, output) = ops.download("/remote/mydir", "/local/destination/")?;
    /// # Ok::<(), anyhow::Error>(())
    /// ```
    pub fn download<S: Into<String>>(&self, remote_path: S, local_path: S) -> Result<(i32, String)> {
        let remote_path = remote_path.into();
        let local_path = local_path.into();

        // Use the file transfer builder from the connection
        let transfer = self.connection.file_transfer();
        transfer.pull(&remote_path, &local_path)
    }

    /// Uploads a file with custom transfer options.
    ///
    /// This provides direct access to the underlying FileTransferBuilder
    /// for advanced configuration (compression, exclusions, etc.).
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use herolib_os::ssh::ssh::SshConnection;
    /// use herolib_os::ssh::file_transfer::FileTransferOps;
    ///
    /// let conn = SshConnection::builder()
    ///     .host("example.com")
    ///     .user("ubuntu")
    ///     .build();
    ///
    /// let ops = FileTransferOps::new(conn);
    ///
    /// // Upload with exclusions
    /// let (code, output) = conn.file_transfer()
    ///     .exclude("*.log")
    ///     .exclude(".git")
    ///     .compress(true)
    ///     .push("/local/project", "/remote/destination")?;
    /// # Ok::<(), anyhow::Error>(())
    /// ```
    pub fn connection(&self) -> &SshConnection {
        &self.connection
    }

    /// Uploads a file or directory with custom compression settings.
    ///
    /// # Arguments
    ///
    /// * `local_path` - Path to the local file or directory
    /// * `remote_path` - Path on the remote system
    /// * `compress` - Enable/disable compression
    ///
    /// # Returns
    ///
    /// Returns the exit code and output from the rsync operation.
    pub fn upload_with_compression<S: Into<String>>(
        &self,
        local_path: S,
        remote_path: S,
        compress: bool,
    ) -> Result<(i32, String)> {
        let local_path = local_path.into();
        let remote_path = remote_path.into();

        // Verify local path exists
        if !Path::new(&local_path).exists() {
            anyhow::bail!("Local path does not exist: {}", local_path);
        }

        let transfer = self.connection.file_transfer().compress(compress);
        transfer.push(&local_path, &remote_path)
    }

    /// Downloads a file or directory with custom compression settings.
    ///
    /// # Arguments
    ///
    /// * `remote_path` - Path on the remote system to download
    /// * `local_path` - Local destination path
    /// * `compress` - Enable/disable compression
    ///
    /// # Returns
    ///
    /// Returns the exit code and output from the rsync operation.
    pub fn download_with_compression<S: Into<String>>(
        &self,
        remote_path: S,
        local_path: S,
        compress: bool,
    ) -> Result<(i32, String)> {
        let remote_path = remote_path.into();
        let local_path = local_path.into();

        let transfer = self.connection.file_transfer().compress(compress);
        transfer.pull(&remote_path, &local_path)
    }
}

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

    #[test]
    fn test_ops_creation() {
        let conn = SshConnection::builder()
            .host("localhost")
            .user("root")
            .build();
        let ops = FileTransferOps::new(conn);
        assert_eq!(ops.connection().host(), "localhost");
    }
}
