use crate::config::{manager::ConfigManager, OPNsenseShell};
use crate::error::Error;
use async_trait::async_trait;
use log::info;
use russh_keys::key::KeyPair;
use std::sync::Arc;

#[derive(Debug)]
pub enum SshCredentials {
    SshKey { username: String, key: Arc<KeyPair> },
    Password { username: String, password: String },
}

#[derive(Debug)]
pub struct SshConfigManager {
    opnsense_shell: Arc<dyn OPNsenseShell>,
}

impl SshConfigManager {
    pub fn new(opnsense_shell: Arc<dyn OPNsenseShell>) -> Self {
        Self { opnsense_shell }
    }
}

impl SshConfigManager {
    async fn backup_config_remote(&self) -> Result<String, Error> {
        let ts = chrono::Utc::now();
        let backup_filename = format!("config-{}-harmony.xml", ts.format("%s%.3f"));

        self.opnsense_shell
            .exec(&format!(
                "cp /conf/config.xml /conf/backup/{}",
                backup_filename
            ))
            .await
    }

    async fn move_to_live_config(&self, new_config_path: &str) -> Result<String, Error> {
        info!("Overwriting OPNSense /conf/config.xml with {new_config_path}");
        self.opnsense_shell
            .exec(&format!("mv {new_config_path} /conf/config.xml"))
            .await
    }

    async fn reload_all_services(&self) -> Result<String, Error> {
        info!("Reloading all opnsense services");
        self.opnsense_shell
            .exec(&format!("configctl service reload all"))
            .await
    }
}

#[async_trait]
impl ConfigManager for SshConfigManager {
    async fn load_as_str(&self) -> Result<String, Error> {
        self.opnsense_shell.exec("cat /conf/config.xml").await
    }

    async fn save_config(&self, content: &str) -> Result<(), Error> {
        let temp_filename = self
            .opnsense_shell
            .write_content_to_temp_file(content)
            .await?;
        self.backup_config_remote().await?;
        self.move_to_live_config(&temp_filename).await?;
        Ok(())
    }

    async fn apply_new_config(&self, content: &str) -> Result<(), Error> {
        self.save_config(content).await?;
        self.reload_all_services().await?;
        Ok(())
    }
}
