use std::process::Command;

use tauri::Manager;

use crate::app::APP;
use crate::errors::*;
use crate::state::AppState;


pub async fn launch(use_base: bool) -> Result<(), LaunchTerminalError> {
    let app = APP.get().unwrap();
    let state = app.state::<AppState>();

    // register_terminal_request() returns Err if there is another request pending
    if state.register_terminal_request().await.is_err() {
        return Ok(());
    }

    let mut cmd = {
        let config = state.config.read().await;
        let mut cmd = Command::new(&config.terminal.exec);
        cmd.args(&config.terminal.args);
        cmd
    };

    // if session is unlocked or empty, wait for credentials from frontend
    if !state.is_unlocked().await {
        app.emit_all("launch-terminal-request", ())?;
        let lease = state.acquire_visibility_lease(0).await
            .map_err(|_e| LaunchTerminalError::NoMainWindow)?; // automate conversion eventually?

        let (tx, rx) = tokio::sync::oneshot::channel();
        app.once_global("credentials-event", move |e| {
            let success = match e.payload() {
                Some("\"unlocked\"") | Some("\"entered\"") => true,
                _ => false,
            };
            let _ = tx.send(success);
        });

        if !rx.await.unwrap_or(false) {
            state.unregister_terminal_request().await;
            return Ok(()); // request was canceled by user
        }
        lease.release();
    }

    // more lock-management
    {
        let app_session = state.session.read().await;
        // session should really be unlocked at this point, but if the frontend misbehaves
        // (i.e. lies about unlocking) we could end up here with a locked session
        // this will result in an error popup to the user (see main hotkey handler)
        let (base_creds, session_creds) = app_session.try_get()?;
        if use_base {
            cmd.env("AWS_ACCESS_KEY_ID", &base_creds.access_key_id);
            cmd.env("AWS_SECRET_ACCESS_KEY", &base_creds.secret_access_key);
        }
        else {
            cmd.env("AWS_ACCESS_KEY_ID", &session_creds.access_key_id);
            cmd.env("AWS_SECRET_ACCESS_KEY", &session_creds.secret_access_key);
            cmd.env("AWS_SESSION_TOKEN", &session_creds.session_token);
        }
    }

    let res = match cmd.spawn() {
        Ok(_) => Ok(()),
        Err(e) if std::io::ErrorKind::NotFound == e.kind() => {
            Err(ExecError::NotFound(cmd.get_program().to_owned()))
        },
        Err(e) => Err(ExecError::ExecutionFailed(e)),
    };

    state.unregister_terminal_request().await;

    res?; // ? auto-conversion is more liberal than .into()
    Ok(())
}
