use crate::{error::Error, secret::SftpSecret};
use ssh_transfer::{AuthenticationType, Configuration, Connection, KnownHost};
use std::{
  convert::TryFrom,
  sync::{Arc, Mutex},
};

#[derive(Clone)]
pub struct SftpEndpoint {
  connection: Arc<Mutex<Connection>>,
  root_directory: String,
}

unsafe impl Sync for SftpEndpoint {}

impl TryFrom<&SftpSecret> for SftpEndpoint {
  type Error = Error;

  fn try_from(secret: &SftpSecret) -> Result<Self, Self::Error> {
    let connection = Self::connect(secret)?;
    let connection = Arc::new(Mutex::new(connection));
    Ok(Self {
      connection,
      root_directory: secret
        .root_directory
        .clone()
        .unwrap_or_else(|| "/".to_string()),
    })
  }
}

impl SftpEndpoint {
  fn connect(secret: &SftpSecret) -> Result<Connection, Error> {
    log::debug!(
      "Attempting to connect to {}:{}.",
      secret.hostname(),
      secret.port()
    );

    let authentication_type = if let Some(password) = secret.password() {
      AuthenticationType::Password(password)
    } else if let Some(private_key) = secret.private_key() {
      AuthenticationType::KeyMemory(private_key)
    } else if let Some(private_key_file_path) = &secret.private_key_file_path() {
      AuthenticationType::KeyFile(private_key_file_path.into())
    } else {
      AuthenticationType::Agent
    };

    let configuration = Configuration::new(secret.hostname())
      .with_port(secret.port())
      .with_username(secret.username())
      .with_authentication(authentication_type);

    let mut connection = Connection::new(&configuration)?;

    if let Some(known_host) = &secret.known_host() {
      let known_host = KnownHost::try_from(known_host.as_str())?;
      connection.add_known_host(&known_host)?;
    }

    connection.start()?;

    Ok(connection)
  }

  pub fn connection(&self) -> Arc<Mutex<Connection>> {
    self.connection.clone()
  }

  pub fn absolute_path(&self, path: &str) -> String {
    format!(
      "{}/{}",
      self.root_directory.trim_end_matches('/'),
      path.trim_start_matches('/')
    )
  }
}
