use serde::{Serialize, Deserialize};
use sqlx::types::Uuid;
use tauri::{AppHandle, State};

use crate::config::AppConfig;
use crate::credentials::{
    AppSession,
    CredentialRecord,
    SshKey,
};
use crate::errors::*;
use crate::clientinfo::Client;
use crate::state::AppState;
use crate::terminal;


#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AwsRequestNotification {
    pub client: Client,
    pub name: Option<String>,
    pub base: bool,
}


#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SshRequestNotification {
    pub client: Client,
    pub key_name: String,
}


#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DockerRequestNotification {
    pub client: Client,
    pub server_url: String,
}


#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum RequestNotificationDetail {
    Aws(AwsRequestNotification),
    Ssh(SshRequestNotification),
    Docker(DockerRequestNotification),
}

impl RequestNotificationDetail {
    pub fn new_aws(client: Client, name: Option<String>, base: bool) -> Self {
        Self::Aws(AwsRequestNotification {client, name, base})
    }

    pub fn new_ssh(client: Client, key_name: String) -> Self {
        Self::Ssh(SshRequestNotification {client, key_name})
    }

    pub fn new_docker(client: Client, server_url: String) -> Self {
        Self::Docker(DockerRequestNotification {client, server_url})
    }
}


#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RequestNotification {
    pub id: u64,
    #[serde(flatten)]
    pub detail: RequestNotificationDetail,
}


#[derive(Debug, Serialize, Deserialize)]
pub struct RequestResponse {
    pub id: u64,
    pub approval: Approval,
    pub base: bool,
}


#[derive(Debug, Serialize, Deserialize)]
pub enum Approval {
    Approved,
    Denied,
}


#[tauri::command]
pub async fn respond(response: RequestResponse, app_state: State<'_, AppState>) -> Result<(), SendResponseError> {
    app_state.send_response(response).await
}


#[tauri::command]
pub async fn unlock(passphrase: String, app_state: State<'_, AppState>) -> Result<(), UnlockError> {
    app_state.unlock(&passphrase).await
}


#[tauri::command]
pub async fn lock(app_state: State<'_, AppState>) -> Result<(), LockError> {
    app_state.lock().await
}


#[tauri::command]
pub async fn reset_session(app_state: State<'_, AppState>) -> Result<(), SaveCredentialsError> {
    app_state.reset_session().await
}


#[tauri::command]
pub async fn set_passphrase(passphrase: &str, app_state: State<'_, AppState>) -> Result<(), SaveCredentialsError> {
    app_state.set_passphrase(passphrase).await
}


#[tauri::command]
pub async fn get_session_status(app_state: State<'_, AppState>) -> Result<String, ()> {
    let session = app_state.app_session.read().await;
    let status = match *session {
        AppSession::Locked{..} => "locked".into(),
        AppSession::Unlocked{..} => "unlocked".into(),
        AppSession::Empty => "empty".into(),
    };
    Ok(status)
}


#[tauri::command]
pub async fn signal_activity(app_state: State<'_, AppState>) -> Result<(), ()> {
    app_state.signal_activity().await;
    Ok(())
}


#[tauri::command]
pub async fn save_credential(
    record: CredentialRecord,
    app_state: State<'_, AppState>
) -> Result<(), SaveCredentialsError> {
    app_state.save_credential(record).await
}


#[tauri::command]
pub async fn delete_credential(id: &str, app_state: State<'_, AppState>) -> Result<(), SaveCredentialsError> {
    let id = Uuid::try_parse(id)
        .map_err(|_| LoadCredentialsError::NoCredentials)?;
    app_state.delete_credential(&id).await
}


#[tauri::command]
pub async fn list_credentials(app_state: State<'_, AppState>) -> Result<Vec<CredentialRecord>, GetCredentialsError> {
    app_state.list_credentials().await
}


#[tauri::command]
pub async fn sshkey_from_file(path: &str, passphrase: &str) -> Result<SshKey, LoadSshKeyError> {
    SshKey::from_file(path, passphrase)
}


#[tauri::command]
pub async fn sshkey_from_private_key(private_key: &str, passphrase: &str) -> Result<SshKey, LoadSshKeyError> {
    SshKey::from_private_key(private_key, passphrase)
}


#[tauri::command]
pub async fn get_config(app_state: State<'_, AppState>) -> Result<AppConfig, ()> {
    let config = app_state.config.read().await;
    Ok(config.clone())
}


#[tauri::command]
pub async fn save_config(config: AppConfig, app_state: State<'_, AppState>) -> Result<(), String>  {
    app_state.update_config(config)
        .await
        .map_err(|e| format!("Error saving config: {e}"))?;
        Ok(())
}


#[tauri::command]
pub async fn launch_terminal(base: bool) -> Result<(), LaunchTerminalError> {
    let res = terminal::launch(base).await;
    res
}


#[tauri::command]
pub async fn get_setup_errors(app_state: State<'_, AppState>) -> Result<Vec<String>, ()> {
    Ok(app_state.setup_errors.clone())
}


#[tauri::command]
pub fn exit(app_handle: AppHandle) {
    app_handle.exit(0)
}
