use std::borrow::Cow;
use std::collections::HashMap;

use super::{ArgValue, Command, CommandArg, CommandId, CommandMeta};
use crate::app_state::AppState;
use crate::commands::meta;
use crate::operation::Operation;

pub struct CommandBuilder {
    id: CommandId,
    title: Cow<'static, str>,
    description: Cow<'static, str>,
    args: Vec<CommandArg>,
    contexts: Vec<Cow<'static, str>>,
    visibility: meta::Visibility,
}

pub fn command(id: impl Into<CommandId>) -> CommandBuilder {
    CommandBuilder {
        id: id.into(),
        title: Cow::Borrowed(""),
        description: Cow::Borrowed(""),
        args: vec![],
        contexts: vec![],
        visibility: meta::Visibility::UserVisible,
    }
}

impl CommandBuilder {
    pub fn title(mut self, t: impl Into<Cow<'static, str>>) -> Self {
        self.title = t.into();
        self
    }
    pub fn description(mut self, d: impl Into<Cow<'static, str>>) -> Self {
        self.description = d.into();
        self
    }
    pub fn context(mut self, c: impl Into<Cow<'static, str>>) -> Self {
        self.contexts.push(c.into());
        self
    }
    #[allow(dead_code)]
    pub fn arg(mut self, a: CommandArg) -> Self {
        self.args.push(a);
        self
    }
    pub fn visibility(mut self, visibility: meta::Visibility) -> Self {
        self.visibility = visibility;
        self
    }
    pub fn hidden(self) -> Self {
        self.visibility(meta::Visibility::Hidden)
    }

    /// Handler receives read-only app state, returns operations.
    pub fn handler(
        self,
        f: impl Fn(&AppState, &HashMap<String, ArgValue>) -> Vec<Operation> + 'static,
    ) -> Command {
        Command {
            meta: CommandMeta {
                id: self.id,
                title: self.title,
                description: self.description,
                args: self.args,
                contexts: self.contexts,
                visibility: self.visibility,
            },
            handler: Box::new(f),
        }
    }
}
