use async_trait::async_trait;
use std::sync::Arc;

use russh::{client, keys::key};

use crate::{
    domain::executors::{ExecutorError, SshClient},
    topology::IpAddress,
};

pub struct RusshClient;

#[async_trait]
impl SshClient for RusshClient {
    async fn test_connection(
        &self,
        address: IpAddress,
        _username: &str,
        _password: &str,
    ) -> Result<(), crate::domain::executors::ExecutorError> {
        let config = client::Config::default();
        let c = Client {};
        let mut client = client::connect(Arc::new(config), (address, 22), c).await?;
        match client
            .authenticate_password("nationtech", "opnsense")
            .await?
        {
            true => Ok(()),
            false => Err(ExecutorError::AuthenticationError(
                "ssh authentication failed".to_string(),
            )),
        }
    }
}

struct Client {}

// More SSH event handlers
// can be defined in this trait
// In this example, we're only using Channel, so these aren't needed.
#[async_trait]
impl client::Handler for Client {
    type Error = ExecutorError;

    async fn check_server_key(
        &mut self,
        _server_public_key: &key::PublicKey,
    ) -> Result<bool, Self::Error> {
        Ok(true)
    }
}

impl From<russh::Error> for ExecutorError {
    fn from(_value: russh::Error) -> Self {
        // TODO handle various russh errors properly
        ExecutorError::NetworkError("Russh client error".to_string())
    }
}
