use crate::{
    builtins::{Builtin, BuiltinFuture, BuiltinRegistry},
    shell::{CommandOutput, ExitCode, SharedShellState, ShellAction},
};

pub struct TypeBuiltin;

impl Builtin for TypeBuiltin {
    fn name(&self) -> &'static str {
        "type"
    }

    fn execute<'a>(&'a self, state: SharedShellState, args: &'a [String]) -> BuiltinFuture<'a> {
        Box::pin(async move {
            if args.len() != 1 {
                return Ok(ShellAction::continue_with(CommandOutput {
                    exit_code: ExitCode::FAILURE,
                    stdout: String::new(),
                    stderr: "type: expected exactly one argument\n".to_string(),
                }));
            }

            let needle = &args[0];
            let registry = BuiltinRegistry::defaults();

            if let Some(value) = state.read().await.aliases().get(needle).map(str::to_owned) {
                return Ok(ShellAction::continue_with(CommandOutput {
                    exit_code: ExitCode::SUCCESS,
                    stdout: format!("{needle} is aliased to `{value}`\n"),
                    stderr: String::new(),
                }));
            }

            if state.read().await.functions().get(needle).is_some() {
                return Ok(ShellAction::continue_with(CommandOutput {
                    exit_code: ExitCode::SUCCESS,
                    stdout: format!("{needle} is a shell function\n"),
                    stderr: String::new(),
                }));
            }

            if registry.contains(needle) {
                return Ok(ShellAction::continue_with(CommandOutput {
                    exit_code: ExitCode::SUCCESS,
                    stdout: format!("{needle} is a shell builtin\n"),
                    stderr: String::new(),
                }));
            }

            Ok(ShellAction::continue_with(CommandOutput {
                exit_code: ExitCode::FAILURE,
                stdout: String::new(),
                stderr: format!("type: {needle} not found\n"),
            }))
        })
    }
}
