use std::sync::Arc;

use anyhow::Result;
use vtcode_core::llm::provider as uni;
use vtcode_core::skills::loader::EnhancedSkillLoader;
use vtcode_core::skills::types::SkillVariety;
use vtcode_core::tools::handlers::ToolModelCapabilities;
use vtcode_core::utils::ansi::MessageStyle;
use vtcode_tui::app::{
    InlineListItem, InlineListSearchConfig, InlineListSelection, WizardModalMode, WizardStep,
};

use crate::agent::runloop::unified::tool_catalog::tool_catalog_change_notifier;
use crate::agent::runloop::unified::turn::utils::{
    enforce_history_limits, truncate_message_content,
};
use crate::agent::runloop::unified::ui_interaction::start_loading_status;
use crate::agent::runloop::unified::wizard_modal::{
    WizardModalOutcome, show_wizard_modal_and_wait,
};
use crate::agent::runloop::{SkillCommandAction, SkillCommandOutcome, handle_skill_command};

use super::{SlashCommandContext, SlashCommandControl};

const SKILL_ACTION_PREFIX: &str = "skills.action.";
const SKILL_ACTION_BACK: &str = "skills.action.back";
const SKILL_OPEN_PREFIX: &str = "skills.open.";
const SKILL_ENABLE_PREFIX: &str = "skills.enable.";
const SKILL_DISABLE_PREFIX: &str = "skills.disable.";
const SKILL_INFO_PREFIX: &str = "skills.info.";
const SKILL_USE_PREFIX: &str = "skills.use.";
const SKILL_VALIDATE_PREFIX: &str = "skills.validate.";
const SKILL_PACKAGE_PREFIX: &str = "skills.package.";
const SKILL_BACK_ACTION: &str = "skills.back";
const SKILL_PICK_PREFIX: &str = "skills.pick.";
const SKILL_PICK_BACK_ACTION: &str = "skills.pick.back";
const SKILL_PROMPT_QUESTION_ID: &str = "skills.input";

#[derive(Clone)]
struct InteractiveSkillEntry {
    name: String,
    description: String,
    loaded: bool,
    kind: InteractiveSkillKind,
}

#[derive(Clone, Copy, PartialEq, Eq)]
enum InteractiveSkillKind {
    Traditional,
    BuiltIn,
    Utility,
}

impl InteractiveSkillEntry {
    fn is_loadable(&self) -> bool {
        matches!(self.kind, InteractiveSkillKind::Traditional)
    }

    fn supports_validation(&self) -> bool {
        matches!(self.kind, InteractiveSkillKind::Traditional)
    }

    fn supports_packaging(&self) -> bool {
        matches!(self.kind, InteractiveSkillKind::Traditional)
    }

    fn badge(&self) -> String {
        if self.loaded {
            return "Enabled".to_string();
        }

        match self.kind {
            InteractiveSkillKind::Traditional => "Available".to_string(),
            InteractiveSkillKind::BuiltIn => "Built-in".to_string(),
            InteractiveSkillKind::Utility => "Utility".to_string(),
        }
    }
}

#[derive(Clone, Copy)]
enum SkillFilter {
    Any,
    Loaded,
    Unloaded,
}

impl SkillFilter {
    fn matches(self, entry: &InteractiveSkillEntry) -> bool {
        match self {
            SkillFilter::Any => true,
            SkillFilter::Loaded => entry.loaded,
            SkillFilter::Unloaded => !entry.loaded,
        }
    }
}

pub(crate) async fn handle_manage_skills(
    mut ctx: SlashCommandContext<'_>,
    action: SkillCommandAction,
) -> Result<SlashCommandControl> {
    super::activation::ensure_skills_context_activated(&ctx).await?;

    if matches!(action, SkillCommandAction::Interactive) {
        return run_interactive_skills_manager(&mut ctx).await;
    }

    let outcome = handle_skill_command(action, ctx.config.workspace.clone()).await?;
    apply_skill_command_outcome(&mut ctx, outcome).await
}

async fn apply_skill_command_outcome(
    ctx: &mut SlashCommandContext<'_>,
    outcome: SkillCommandOutcome,
) -> Result<SlashCommandControl> {
    match outcome {
        SkillCommandOutcome::Handled { message } => {
            ctx.renderer.line(MessageStyle::Info, &message)?;
            Ok(SlashCommandControl::Continue)
        }
        SkillCommandOutcome::LoadSkill { skill, message } => {
            if let Err(e) = skill_runtime(ctx)
                .activate_skill(ctx.loaded_skills, skill)
                .await
            {
                ctx.renderer.line(
                    MessageStyle::Error,
                    &format!("Failed to activate skill: {}", e),
                )?;
                return Ok(SlashCommandControl::Continue);
            }

            ctx.renderer.line(MessageStyle::Info, &message)?;
            Ok(SlashCommandControl::Continue)
        }
        SkillCommandOutcome::UnloadSkill { name } => {
            let removed = skill_runtime(ctx)
                .deactivate_skill(ctx.loaded_skills, &name)
                .await?;
            let message = if removed {
                format!("Unloaded skill: {}", name)
            } else {
                format!("Skill was not active: {}", name)
            };
            ctx.renderer.line(MessageStyle::Info, &message)?;
            Ok(SlashCommandControl::Continue)
        }
        SkillCommandOutcome::UseSkill { skill, input } => {
            use vtcode_core::skills::execute_skill_with_sub_llm;

            let skill_name = skill.name().to_string();
            let status_message = skill_invocation_status_message(&skill_name);
            let available_tools = ctx.tools.read().await.clone();
            let model = ctx.config.model.clone();
            let loading_spinner = ctx.renderer.supports_inline_ui().then(|| {
                start_loading_status(ctx.handle, ctx.input_status_state, status_message.clone())
            });

            ctx.renderer.line(MessageStyle::Info, &status_message)?;

            match execute_skill_with_sub_llm(
                &skill,
                input,
                ctx.provider_client.as_ref(),
                ctx.tool_registry,
                available_tools,
                model,
            )
            .await
            {
                Ok(result) => {
                    drop(loading_spinner);
                    ctx.renderer
                        .render_markdown_output(MessageStyle::Output, &result)?;
                    ctx.conversation_history.push(uni::Message::user(format!(
                        "/skills use {} [executed]",
                        skill_name
                    )));

                    let limited = truncate_message_content(&result);
                    ctx.conversation_history
                        .push(uni::Message::assistant(limited));
                    enforce_history_limits(ctx.conversation_history);
                    Ok(SlashCommandControl::Continue)
                }
                Err(e) => {
                    drop(loading_spinner);
                    ctx.renderer.line(
                        MessageStyle::Error,
                        &format!("Failed to execute skill: {}", e),
                    )?;
                    Ok(SlashCommandControl::Continue)
                }
            }
        }
        SkillCommandOutcome::UseBuiltInCommand {
            name,
            slash_name,
            input,
        } => {
            let status_message = skill_invocation_status_message(&name);
            let loading_spinner = ctx.renderer.supports_inline_ui().then(|| {
                start_loading_status(ctx.handle, ctx.input_status_state, status_message.clone())
            });

            ctx.renderer.line(MessageStyle::Info, &status_message)?;
            let outcome = crate::agent::runloop::slash_commands::execute_command_skill_by_name(
                &slash_name,
                &input,
                ctx.renderer,
                &ctx.config.workspace,
            )
            .await?;
            drop(loading_spinner);
            let control = Box::pin(
                crate::agent::runloop::unified::turn::session::slash_commands::handle_outcome(
                    outcome,
                    ctx.reborrow(),
                ),
            )
            .await?;
            match control {
                SlashCommandControl::Continue => Ok(SlashCommandControl::Continue),
                SlashCommandControl::SubmitPrompt(prompt) => {
                    Ok(SlashCommandControl::SubmitPrompt(prompt))
                }
                SlashCommandControl::ReplaceInput(content) => {
                    Ok(SlashCommandControl::ReplaceInput(content))
                }
                SlashCommandControl::BreakWithReason(reason) => {
                    ctx.renderer.line(
                        MessageStyle::Info,
                        &format!("Built-in command skill '{}' exited the session.", name),
                    )?;
                    Ok(SlashCommandControl::BreakWithReason(reason))
                }
            }
        }
        SkillCommandOutcome::Error { message } => {
            ctx.renderer.line(MessageStyle::Error, &message)?;
            Ok(SlashCommandControl::Continue)
        }
    }
}

fn skill_invocation_status_message(skill_name: &str) -> String {
    let invocation_target = skill_name
        .strip_prefix("cmd-")
        .map(|slash_name| format!("/{}", slash_name))
        .unwrap_or_else(|| skill_name.to_string());
    format!("Running {}...", invocation_target)
}

fn skill_runtime(
    ctx: &SlashCommandContext<'_>,
) -> vtcode_core::tools::skills::SkillToolSessionRuntime {
    let tool_documentation_mode = ctx
        .vt_cfg
        .as_ref()
        .as_ref()
        .map(|cfg| cfg.agent.tool_documentation_mode)
        .unwrap_or_default();

    vtcode_core::tools::skills::SkillToolSessionRuntime::new(
        Arc::new(ctx.tool_registry.clone()),
        Some(Arc::clone(ctx.tools)),
        tool_documentation_mode,
        ToolModelCapabilities::for_model_name(&ctx.config.model),
        Some(tool_catalog_change_notifier(ctx.tool_catalog)),
    )
    .with_fork_executor(Arc::new(
        vtcode_core::skills::executor::ChildAgentSkillExecutor::new(
            Arc::new(ctx.tool_registry.clone()),
            vtcode_core::skills::executor::ForkSkillRuntimeConfig {
                workspace: ctx.config.workspace.clone(),
                model: ctx.config.model.clone(),
                api_key: ctx.config.api_key.clone(),
                openai_chatgpt_auth: ctx.config.openai_chatgpt_auth.clone(),
                vt_cfg: ctx.vt_cfg.as_ref().cloned(),
            },
        ),
    ))
}

async fn execute_skill_action(
    ctx: &mut SlashCommandContext<'_>,
    action: SkillCommandAction,
) -> Result<()> {
    let outcome = handle_skill_command(action, ctx.config.workspace.clone()).await?;
    let _ = apply_skill_command_outcome(ctx, outcome).await?;
    Ok(())
}

async fn run_interactive_skills_manager(
    ctx: &mut SlashCommandContext<'_>,
) -> Result<SlashCommandControl> {
    if !ctx.renderer.supports_inline_ui() {
        execute_skill_action(ctx, SkillCommandAction::Help).await?;
        return Ok(SlashCommandControl::Continue);
    }

    loop {
        show_skills_manager_actions_modal(ctx);
        let Some(selection) = super::ui::wait_for_list_modal_selection(ctx).await else {
            return Ok(SlashCommandControl::Continue);
        };

        let InlineListSelection::ConfigAction(action) = selection else {
            continue;
        };

        if action == SKILL_ACTION_BACK {
            return Ok(SlashCommandControl::Continue);
        }

        let Some(action_key) = action.strip_prefix(SKILL_ACTION_PREFIX) else {
            continue;
        };

        match action_key {
            "browse" => run_skill_browser(ctx).await?,
            "list" => execute_skill_action(ctx, SkillCommandAction::List { query: None }).await?,
            "search" => {
                if let Some(query) = prompt_required_text(
                    ctx,
                    "Search Skills",
                    "Provide a query to filter by name or description.",
                    "Query:",
                    "Type search text",
                )
                .await?
                {
                    execute_skill_action(ctx, SkillCommandAction::List { query: Some(query) })
                        .await?;
                }
            }
            "create" => {
                if let Some(name) = prompt_required_text(
                    ctx,
                    "Create Skill",
                    "Provide a new skill name (kebab-case recommended).",
                    "Name:",
                    "Type skill name",
                )
                .await?
                {
                    execute_skill_action(ctx, SkillCommandAction::Create { name, path: None })
                        .await?;
                }
            }
            "load" => {
                if let Some(name) = pick_skill_name(
                    ctx,
                    SkillFilter::Unloaded,
                    "Enable Skill",
                    "Select a skill to enable for this session.",
                    PickerMode::TraditionalOnly,
                )
                .await?
                {
                    execute_skill_action(ctx, SkillCommandAction::Load { name }).await?;
                }
            }
            "unload" => {
                if let Some(name) = pick_skill_name(
                    ctx,
                    SkillFilter::Loaded,
                    "Disable Skill",
                    "Select an enabled skill to unload from this session.",
                    PickerMode::TraditionalOnly,
                )
                .await?
                {
                    execute_skill_action(ctx, SkillCommandAction::Unload { name }).await?;
                }
            }
            "info" => {
                if let Some(name) = pick_skill_name(
                    ctx,
                    SkillFilter::Any,
                    "Skill Details",
                    "Select a skill to inspect.",
                    PickerMode::Any,
                )
                .await?
                {
                    execute_skill_action(ctx, SkillCommandAction::Info { name }).await?;
                }
            }
            "use" => {
                if let Some(name) = pick_skill_name(
                    ctx,
                    SkillFilter::Any,
                    "Run Skill",
                    "Select a skill to execute with input.",
                    PickerMode::Any,
                )
                .await?
                    && let Some(input) = prompt_optional_text(
                        ctx,
                        "Run Skill",
                        "Provide input for this skill run (optional).",
                        "Input:",
                        "Type skill input (optional)",
                    )
                    .await?
                {
                    execute_skill_action(ctx, SkillCommandAction::Use { name, input }).await?;
                }
            }
            "validate" => {
                if let Some(name) = pick_skill_name(
                    ctx,
                    SkillFilter::Any,
                    "Validate Skill",
                    "Select a skill to validate.",
                    PickerMode::TraditionalOnly,
                )
                .await?
                {
                    execute_skill_action(ctx, SkillCommandAction::Validate { name }).await?;
                }
            }
            "package" => {
                if let Some(name) = pick_skill_name(
                    ctx,
                    SkillFilter::Any,
                    "Package Skill",
                    "Select a skill to package to .skill.",
                    PickerMode::TraditionalOnly,
                )
                .await?
                {
                    execute_skill_action(ctx, SkillCommandAction::Package { name }).await?;
                }
            }
            "regen" => execute_skill_action(ctx, SkillCommandAction::RegenerateIndex).await?,
            "help" => execute_skill_action(ctx, SkillCommandAction::Help).await?,
            _ => continue,
        }
    }
}

async fn run_skill_browser(ctx: &mut SlashCommandContext<'_>) -> Result<()> {
    loop {
        let entries = discover_interactive_skills(ctx).await?;
        if entries.is_empty() {
            ctx.renderer.line(
                MessageStyle::Info,
                "No skills found. Use /skills --create <name> to scaffold a new one.",
            )?;
            return Ok(());
        }

        show_skills_list_modal(ctx, &entries);
        let Some(selection) = super::ui::wait_for_list_modal_selection(ctx).await else {
            return Ok(());
        };

        let InlineListSelection::ConfigAction(action) = selection else {
            continue;
        };
        if action == SKILL_PICK_BACK_ACTION {
            return Ok(());
        }

        let Some(skill_name) = action.strip_prefix(SKILL_OPEN_PREFIX) else {
            continue;
        };
        let Some(entry) = entries
            .iter()
            .find(|candidate| candidate.name == skill_name)
            .cloned()
        else {
            continue;
        };

        show_skill_actions_modal(ctx, &entry);
        let Some(skill_action) = super::ui::wait_for_list_modal_selection(ctx).await else {
            continue;
        };
        let InlineListSelection::ConfigAction(skill_action) = skill_action else {
            continue;
        };
        if skill_action == SKILL_BACK_ACTION {
            continue;
        }

        if let Some(name) = skill_action.strip_prefix(SKILL_ENABLE_PREFIX) {
            execute_skill_action(
                ctx,
                SkillCommandAction::Load {
                    name: name.to_string(),
                },
            )
            .await?;
            continue;
        }

        if let Some(name) = skill_action.strip_prefix(SKILL_DISABLE_PREFIX) {
            execute_skill_action(
                ctx,
                SkillCommandAction::Unload {
                    name: name.to_string(),
                },
            )
            .await?;
            continue;
        }

        if let Some(name) = skill_action.strip_prefix(SKILL_INFO_PREFIX) {
            execute_skill_action(
                ctx,
                SkillCommandAction::Info {
                    name: name.to_string(),
                },
            )
            .await?;
            continue;
        }

        if let Some(name) = skill_action.strip_prefix(SKILL_USE_PREFIX) {
            if let Some(input) = prompt_optional_text(
                ctx,
                &format!("Run Skill: {}", name),
                "Provide input for this skill run (optional).",
                "Input:",
                "Type skill input (optional)",
            )
            .await?
            {
                execute_skill_action(
                    ctx,
                    SkillCommandAction::Use {
                        name: name.to_string(),
                        input,
                    },
                )
                .await?;
            }
            continue;
        }

        if let Some(name) = skill_action.strip_prefix(SKILL_VALIDATE_PREFIX) {
            execute_skill_action(
                ctx,
                SkillCommandAction::Validate {
                    name: name.to_string(),
                },
            )
            .await?;
            continue;
        }

        if let Some(name) = skill_action.strip_prefix(SKILL_PACKAGE_PREFIX) {
            execute_skill_action(
                ctx,
                SkillCommandAction::Package {
                    name: name.to_string(),
                },
            )
            .await?;
            continue;
        }
    }
}

async fn discover_interactive_skills(
    ctx: &SlashCommandContext<'_>,
) -> Result<Vec<InteractiveSkillEntry>> {
    let mut loader = EnhancedSkillLoader::new(ctx.config.workspace.clone());
    let discovered = loader.discover_all_skills().await?;
    let loaded = ctx.loaded_skills.read().await;

    let mut entries: Vec<InteractiveSkillEntry> = discovered
        .skills
        .into_iter()
        .map(|skill_ctx| {
            let manifest = skill_ctx.manifest();
            InteractiveSkillEntry {
                name: manifest.name.clone(),
                description: manifest.description.clone(),
                loaded: loaded.contains_key(&manifest.name),
                kind: if manifest.variety == SkillVariety::BuiltIn {
                    InteractiveSkillKind::BuiltIn
                } else {
                    InteractiveSkillKind::Traditional
                },
            }
        })
        .collect();
    entries.extend(
        discovered
            .tools
            .into_iter()
            .map(|tool| InteractiveSkillEntry {
                name: tool.name,
                description: tool.description,
                loaded: false,
                kind: InteractiveSkillKind::Utility,
            }),
    );

    entries.sort_by(|left, right| left.name.cmp(&right.name));
    Ok(entries)
}

async fn pick_skill_name(
    ctx: &mut SlashCommandContext<'_>,
    filter: SkillFilter,
    title: &str,
    description: &str,
    picker_mode: PickerMode,
) -> Result<Option<String>> {
    let entries = discover_interactive_skills(ctx).await?;
    let filtered: Vec<InteractiveSkillEntry> = entries
        .into_iter()
        .filter(|entry| filter.matches(entry) && picker_mode.allows(entry))
        .collect();

    if filtered.is_empty() {
        ctx.renderer.line(
            MessageStyle::Info,
            "No matching skills available for that action.",
        )?;
        return Ok(None);
    }

    show_skill_picker_modal(ctx, title, description, &filtered);
    let Some(selection) = super::ui::wait_for_list_modal_selection(ctx).await else {
        return Ok(None);
    };

    let InlineListSelection::ConfigAction(action) = selection else {
        return Ok(None);
    };
    if action == SKILL_PICK_BACK_ACTION {
        return Ok(None);
    }

    Ok(action
        .strip_prefix(SKILL_PICK_PREFIX)
        .map(ToString::to_string))
}

#[derive(Clone, Copy)]
enum PickerMode {
    Any,
    TraditionalOnly,
}

impl PickerMode {
    fn allows(self, entry: &InteractiveSkillEntry) -> bool {
        match self {
            PickerMode::Any => true,
            PickerMode::TraditionalOnly => entry.is_loadable(),
        }
    }
}

async fn prompt_required_text(
    ctx: &mut SlashCommandContext<'_>,
    title: &str,
    question: &str,
    freeform_label: &str,
    placeholder: &str,
) -> Result<Option<String>> {
    let Some(value) = prompt_text(ctx, title, question, freeform_label, placeholder, false).await?
    else {
        return Ok(None);
    };

    let trimmed = value.trim().to_string();
    if trimmed.is_empty() {
        ctx.renderer
            .line(MessageStyle::Info, "Input was empty. Nothing executed.")?;
        return Ok(None);
    }

    Ok(Some(trimmed))
}

async fn prompt_optional_text(
    ctx: &mut SlashCommandContext<'_>,
    title: &str,
    question: &str,
    freeform_label: &str,
    placeholder: &str,
) -> Result<Option<String>> {
    prompt_text(ctx, title, question, freeform_label, placeholder, true).await
}

async fn prompt_text(
    ctx: &mut SlashCommandContext<'_>,
    title: &str,
    question: &str,
    freeform_label: &str,
    placeholder: &str,
    allow_empty: bool,
) -> Result<Option<String>> {
    let step = WizardStep {
        title: "Input".to_string(),
        question: question.to_string(),
        items: vec![InlineListItem {
            title: "Submit".to_string(),
            subtitle: Some("Press Tab to type text, then Enter to submit.".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::RequestUserInputAnswer {
                question_id: SKILL_PROMPT_QUESTION_ID.to_string(),
                selected: vec![],
                other: Some(String::new()),
            }),
            search_value: Some("submit input".to_string()),
        }],
        completed: false,
        answer: None,
        allow_freeform: true,
        freeform_label: Some(freeform_label.to_string()),
        freeform_placeholder: Some(placeholder.to_string()),
        freeform_default: None,
    };

    let outcome = show_wizard_modal_and_wait(
        ctx.handle,
        ctx.session,
        title.to_string(),
        vec![step],
        0,
        None,
        WizardModalMode::MultiStep,
        ctx.ctrl_c_state,
        ctx.ctrl_c_notify,
    )
    .await?;
    let value = match outcome {
        WizardModalOutcome::Submitted(selections) => {
            selections
                .into_iter()
                .find_map(|selection| match selection {
                    InlineListSelection::RequestUserInputAnswer {
                        question_id,
                        selected,
                        other,
                    } if question_id == SKILL_PROMPT_QUESTION_ID => {
                        if let Some(other) = other {
                            Some(other)
                        } else {
                            selected.first().cloned()
                        }
                    }
                    _ => None,
                })
        }
        WizardModalOutcome::Cancelled { .. } => None,
    };

    let Some(value) = value else {
        return Ok(None);
    };

    if allow_empty {
        return Ok(Some(value));
    }

    let trimmed = value.trim().to_string();
    if trimmed.is_empty() {
        return Ok(None);
    }
    Ok(Some(trimmed))
}

fn show_skills_manager_actions_modal(ctx: &mut SlashCommandContext<'_>) {
    let items = vec![
        InlineListItem {
            title: "Browse skills".to_string(),
            subtitle: Some("Open the skills catalog and per-skill actions".to_string()),
            badge: Some("Recommended".to_string()),
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}browse",
                SKILL_ACTION_PREFIX
            ))),
            search_value: Some("browse catalog skills".to_string()),
        },
        InlineListItem {
            title: "List skills".to_string(),
            subtitle: Some("Show all discoverable skills".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}list",
                SKILL_ACTION_PREFIX
            ))),
            search_value: Some("list skills".to_string()),
        },
        InlineListItem {
            title: "Search skills".to_string(),
            subtitle: Some("Filter skills by name or description".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}search",
                SKILL_ACTION_PREFIX
            ))),
            search_value: Some("search query".to_string()),
        },
        InlineListItem {
            title: "Create skill".to_string(),
            subtitle: Some("Scaffold a new skill template".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}create",
                SKILL_ACTION_PREFIX
            ))),
            search_value: Some("create scaffold".to_string()),
        },
        InlineListItem {
            title: "Enable skill".to_string(),
            subtitle: Some("Load a skill into the current session".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}load",
                SKILL_ACTION_PREFIX
            ))),
            search_value: Some("enable load".to_string()),
        },
        InlineListItem {
            title: "Disable skill".to_string(),
            subtitle: Some("Unload an enabled skill from this session".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}unload",
                SKILL_ACTION_PREFIX
            ))),
            search_value: Some("disable unload".to_string()),
        },
        InlineListItem {
            title: "View skill details".to_string(),
            subtitle: Some("Show metadata and instructions".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}info",
                SKILL_ACTION_PREFIX
            ))),
            search_value: Some("details info metadata".to_string()),
        },
        InlineListItem {
            title: "Run skill".to_string(),
            subtitle: Some("Execute a skill with optional input".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}use",
                SKILL_ACTION_PREFIX
            ))),
            search_value: Some("run execute use".to_string()),
        },
        InlineListItem {
            title: "Validate skill".to_string(),
            subtitle: Some("Validate skill structure".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}validate",
                SKILL_ACTION_PREFIX
            ))),
            search_value: Some("validate lint".to_string()),
        },
        InlineListItem {
            title: "Package skill".to_string(),
            subtitle: Some("Package a skill to .skill file".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}package",
                SKILL_ACTION_PREFIX
            ))),
            search_value: Some("package bundle".to_string()),
        },
        InlineListItem {
            title: "Regenerate index".to_string(),
            subtitle: Some("Rebuild skills index file".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}regen",
                SKILL_ACTION_PREFIX
            ))),
            search_value: Some("index regenerate".to_string()),
        },
        InlineListItem {
            title: "Show help".to_string(),
            subtitle: Some("Display `/skills` command help".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}help",
                SKILL_ACTION_PREFIX
            ))),
            search_value: Some("help commands".to_string()),
        },
        InlineListItem {
            title: "Back".to_string(),
            subtitle: Some("Close skills manager".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(
                SKILL_ACTION_BACK.to_string(),
            )),
            search_value: Some("back close".to_string()),
        },
    ];

    ctx.renderer.show_list_modal(
        "Skills Manager",
        vec![
            "Configure skills interactively.".to_string(),
            "Use Enter to run an action, Esc to close.".to_string(),
        ],
        items,
        Some(InlineListSelection::ConfigAction(format!(
            "{}browse",
            SKILL_ACTION_PREFIX
        ))),
        None,
    );
}

fn show_skills_list_modal(ctx: &mut SlashCommandContext<'_>, entries: &[InteractiveSkillEntry]) {
    let mut items: Vec<InlineListItem> = entries
        .iter()
        .map(|entry| InlineListItem {
            title: entry.name.clone(),
            subtitle: Some(entry.description.clone()),
            badge: Some(entry.badge()),
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}{}",
                SKILL_OPEN_PREFIX, entry.name
            ))),
            search_value: Some(format!("{} {}", entry.name, entry.description)),
        })
        .collect();

    items.push(InlineListItem {
        title: "Back".to_string(),
        subtitle: Some("Return to skills actions".to_string()),
        badge: None,
        indent: 0,
        selection: Some(InlineListSelection::ConfigAction(
            SKILL_PICK_BACK_ACTION.to_string(),
        )),
        search_value: Some("back".to_string()),
    });

    let selected = entries.first().map(|entry| {
        InlineListSelection::ConfigAction(format!("{}{}", SKILL_OPEN_PREFIX, entry.name))
    });

    ctx.renderer.show_list_modal(
        "Skills Manager",
        vec![
            "Browse and manage discovered skills.".to_string(),
            "Browse skills and press Enter for actions.".to_string(),
        ],
        items,
        selected,
        Some(InlineListSearchConfig {
            label: "Search skills".to_string(),
            placeholder: Some("Type skill name or description".to_string()),
        }),
    );
}

fn show_skill_picker_modal(
    ctx: &mut SlashCommandContext<'_>,
    title: &str,
    description: &str,
    entries: &[InteractiveSkillEntry],
) {
    let mut items: Vec<InlineListItem> = entries
        .iter()
        .map(|entry| InlineListItem {
            title: entry.name.clone(),
            subtitle: Some(entry.description.clone()),
            badge: Some(entry.badge()),
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}{}",
                SKILL_PICK_PREFIX, entry.name
            ))),
            search_value: Some(format!("{} {}", entry.name, entry.description)),
        })
        .collect();

    items.push(InlineListItem {
        title: "Back".to_string(),
        subtitle: Some("Cancel and return".to_string()),
        badge: None,
        indent: 0,
        selection: Some(InlineListSelection::ConfigAction(
            SKILL_PICK_BACK_ACTION.to_string(),
        )),
        search_value: Some("back cancel".to_string()),
    });

    let selected = entries.first().map(|entry| {
        InlineListSelection::ConfigAction(format!("{}{}", SKILL_PICK_PREFIX, entry.name))
    });

    ctx.renderer.show_list_modal(
        title,
        vec![description.to_string()],
        items,
        selected,
        Some(InlineListSearchConfig {
            label: "Search skills".to_string(),
            placeholder: Some("Type skill name or description".to_string()),
        }),
    );
}

fn show_skill_actions_modal(ctx: &mut SlashCommandContext<'_>, entry: &InteractiveSkillEntry) {
    let mut items = Vec::new();
    if entry.is_loadable() && entry.loaded {
        items.push(InlineListItem {
            title: "Disable for this session".to_string(),
            subtitle: Some("Unload this skill from the active session".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}{}",
                SKILL_DISABLE_PREFIX, entry.name
            ))),
            search_value: Some("disable unload session".to_string()),
        });
    } else if entry.is_loadable() {
        items.push(InlineListItem {
            title: "Enable for this session".to_string(),
            subtitle: Some("Load this skill into the active session".to_string()),
            badge: Some("Recommended".to_string()),
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}{}",
                SKILL_ENABLE_PREFIX, entry.name
            ))),
            search_value: Some("enable load session".to_string()),
        });
    }

    items.push(InlineListItem {
        title: "View details".to_string(),
        subtitle: Some("Show full skill metadata and instructions".to_string()),
        badge: None,
        indent: 0,
        selection: Some(InlineListSelection::ConfigAction(format!(
            "{}{}",
            SKILL_INFO_PREFIX, entry.name
        ))),
        search_value: Some("details info metadata".to_string()),
    });

    items.push(InlineListItem {
        title: "Run skill".to_string(),
        subtitle: Some("Execute this skill with optional input".to_string()),
        badge: None,
        indent: 0,
        selection: Some(InlineListSelection::ConfigAction(format!(
            "{}{}",
            SKILL_USE_PREFIX, entry.name
        ))),
        search_value: Some("run execute use".to_string()),
    });

    if entry.supports_validation() {
        items.push(InlineListItem {
            title: "Validate".to_string(),
            subtitle: Some("Validate this skill structure".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}{}",
                SKILL_VALIDATE_PREFIX, entry.name
            ))),
            search_value: Some("validate".to_string()),
        });
    }

    if entry.supports_packaging() {
        items.push(InlineListItem {
            title: "Package".to_string(),
            subtitle: Some("Package this skill to .skill".to_string()),
            badge: None,
            indent: 0,
            selection: Some(InlineListSelection::ConfigAction(format!(
                "{}{}",
                SKILL_PACKAGE_PREFIX, entry.name
            ))),
            search_value: Some("package bundle".to_string()),
        });
    }

    items.push(InlineListItem {
        title: "Back".to_string(),
        subtitle: Some("Return to skills list".to_string()),
        badge: None,
        indent: 0,
        selection: Some(InlineListSelection::ConfigAction(
            SKILL_BACK_ACTION.to_string(),
        )),
        search_value: Some("back return".to_string()),
    });

    let default_selection = items.first().and_then(|item| item.selection.clone());
    let title = format!("Skill: {}", entry.name);
    ctx.renderer.show_list_modal(
        &title,
        vec![entry.description.clone()],
        items,
        default_selection,
        None,
    );
}

#[cfg(test)]
mod tests {
    use super::skill_invocation_status_message;

    #[test]
    fn command_skills_use_slash_alias_in_status() {
        assert_eq!(
            skill_invocation_status_message("cmd-review"),
            "Running /review..."
        );
    }

    #[test]
    fn non_command_skills_use_skill_name_in_status() {
        assert_eq!(
            skill_invocation_status_message("rust-skills"),
            "Running rust-skills..."
        );
    }
}
