use crate::commands::detect_shell;

pub(crate) async fn run(shell: String) -> eyre::Result<()> {
    let integration = match shell.as_str() {
        "zsh" => generate_zsh_integration(),
        "bash" => generate_bash_integration(),
        "fish" => generate_fish_integration(),
        "auto" => generate_auto_integration()?,
        _ => eyre::bail!("Unsupported shell: {}", shell),
    };

    println!("{}", integration);
    Ok(())
}

fn generate_auto_integration() -> eyre::Result<&'static str> {
    let shell = detect_shell();
    match shell.as_deref() {
        Some("zsh") => Ok(generate_zsh_integration()),
        Some("bash") => Ok(generate_bash_integration()),
        Some("fish") => Ok(generate_fish_integration()),
        Some(s) => eyre::bail!("Unsupported shell: {}", s),
        None => eyre::bail!("Could not detect shell"),
    }
}

/// Generate the zsh integration function - pure function for easy testing
pub fn generate_zsh_integration() -> &'static str {
    r#"
_atuin_ai_cleanup() {
    true
}

# zle reset-prompt anchors the repaint at the cursor row: a multi-line
# prompt grows *upward*, overwriting whatever the inline TUI left on the
# rows above. Pad with prompt-height - 1 newlines first so the repaint
# lands on blank rows below the conversation instead.
_atuin_ai_reset_prompt() {
    local -a _prompt_lines=("${(@f)${(%%)PROMPT}}")
    local -i _pad=$(( ${#_prompt_lines} - 1 ))
    (( _pad > 0 )) && printf '\n%.0s' {1..$_pad} >/dev/tty
    zle reset-prompt
}

# Question mark at start of line - natural language mode.
# Named with 'self-' prefix so bracketed-paste-magic activates it during
# paste, allowing url-quote-magic to escape ? in pasted URLs via self-insert.
self-atuin-ai-question-mark() {
    # If buffer is empty or just contains '?', trigger natural language mode
    if [[ -z "$BUFFER" || "$BUFFER" == "?" ]]; then
        BUFFER=""
        # Close the semantic prompt zone (OSC 133 C, "command output
        # starts") before handing the terminal to the TUI. Without it,
        # terminals with shell integration (Ghostty) believe we are
        # still at the prompt, and their resize-time prompt reflow
        # erases everything below the prompt mark — including the
        # conversation the TUI printed.
        printf '\033]133;C\007' > /dev/tty
        local output
        output=$(atuin ai inline --hook 3>&1 1>&2 2>&3)

        # Clean up the inline viewport
        _atuin_ai_cleanup

        if [[ $output == __atuin_ai_print__:* ]]; then
            echo "${output#__atuin_ai_print__:}"
            _atuin_ai_reset_prompt
        elif [[ $output == __atuin_ai_cancel__ ]]; then
            _atuin_ai_reset_prompt
        elif [[ $output == __atuin_ai_execute__:* ]]; then
            RBUFFER=""
            LBUFFER=${output#__atuin_ai_execute__:}
            _atuin_ai_reset_prompt
            zle accept-line
        elif [[ $output == __atuin_ai_insert__:* ]]; then
            RBUFFER=""
            LBUFFER=${output#__atuin_ai_insert__:}
            _atuin_ai_reset_prompt
        elif [[ -n $output ]]; then
            RBUFFER=""
            LBUFFER=$output
            _atuin_ai_reset_prompt
        else
            _atuin_ai_reset_prompt
        fi
    else
        zle self-insert
    fi
}

# Set up keybindings
zle -N self-atuin-ai-question-mark
bindkey '?' self-atuin-ai-question-mark # Question mark
"#
    .trim()
}

/// Generate the bash integration function - pure function for easy testing
pub fn generate_bash_integration() -> &'static str {
    r#"
# Question mark at start of line - natural language mode
_atuin_ai_question_mark() {
    # If buffer is empty or just contains '?', trigger natural language mode
    if [[ -z "$READLINE_LINE" || "$READLINE_LINE" == "?" ]]; then
        READLINE_LINE=""
        READLINE_POINT=0

        # Close the semantic prompt zone (OSC 133 C) so terminals with
        # shell integration don't erase the TUI's output during their
        # resize-time prompt reflow.
        printf '\033]133;C\007' > /dev/tty
        local output
        output=$(atuin ai inline --hook 3>&1 1>&2 2>&3)

        if [[ $output == __atuin_ai_print__:* ]]; then
            echo "${output#__atuin_ai_print__:}"
            READLINE_LINE=""
            READLINE_POINT=0
        elif [[ $output == __atuin_ai_cancel__ ]]; then
            READLINE_LINE=""
            READLINE_POINT=0
        elif [[ $output == __atuin_ai_execute__:* ]]; then
            # Execute the command immediately
            READLINE_LINE=${output#__atuin_ai_execute__:}
            READLINE_POINT=${#READLINE_LINE}
            # Note: We can't directly execute in bash bind -x, but we can
            # use a workaround by binding to a macro that accepts the line
            bind '"\C-x\C-a": accept-line'
            bind -x '"\C-x\C-e": _atuin_ai_question_mark'
        elif [[ $output == __atuin_ai_insert__:* ]]; then
            # Insert the command for editing
            READLINE_LINE=${output#__atuin_ai_insert__:}
            READLINE_POINT=${#READLINE_LINE}
        elif [[ -n $output ]]; then
            # Default: insert for editing
            READLINE_LINE=$output
            READLINE_POINT=${#READLINE_LINE}
        fi
    else
        # Not at empty prompt, just insert the question mark
        READLINE_LINE="${READLINE_LINE:0:READLINE_POINT}?${READLINE_LINE:READLINE_POINT}"
        ((READLINE_POINT++))
    fi
}

# Set up keybindings
# Bash requires special handling: we use bind -x for the function,
# but need a two-step approach for execute mode
__atuin_ai_accept_line=""

_atuin_ai_question_mark_wrapper() {
    _atuin_ai_question_mark
    if [[ -n "$__atuin_ai_accept_line" ]]; then
        __atuin_ai_accept_line=""
    fi
}

bind -x '"?": _atuin_ai_question_mark'
"#
    .trim()
}

/// Generate the fish integration function - pure function for easy testing
pub fn generate_fish_integration() -> &'static str {
    r#"
# Question mark at start of line - natural language mode
function _atuin_ai_question_mark
    set -l buf (commandline -b)

    # If buffer is empty or just contains '?', trigger natural language mode
    if test -z "$buf" -o "$buf" = "?"
        commandline -r ""

        # Close the semantic prompt zone (OSC 133 C) so terminals with
        # shell integration don't erase the TUI's output during their
        # resize-time prompt reflow.
        printf '\033]133;C\007' > /dev/tty

        # Run atuin ai inline, swapping stdout and stderr
        set -l output (atuin ai inline --hook 3>&1 1>&2 2>&3 | string collect)

        if string match --quiet '__atuin_ai_print__:*' "$output"
            echo (string replace "__atuin_ai_print__:" "" -- "$output" | string collect)
            commandline -f repaint
        else if test "$output" = "__atuin_ai_cancel__"
            commandline -f repaint
        else if string match --quiet '__atuin_ai_execute__:*' "$output"
            # Execute the command immediately
            set -l cmd (string replace "__atuin_ai_execute__:" "" -- "$output" | string collect)
            commandline -r "$cmd"
            commandline -f repaint
            commandline -f execute
        else if string match --quiet '__atuin_ai_insert__:*' "$output"
            # Insert the command for editing
            set -l cmd (string replace "__atuin_ai_insert__:" "" -- "$output" | string collect)
            commandline -r "$cmd"
            commandline -f repaint
        else if test -n "$output"
            # Default: insert for editing
            commandline -r "$output"
            commandline -f repaint
        else
            commandline -f repaint
        end
    else if not contains -- "$fish_key_bindings" fish_vi_key_bindings fish_hybrid_key_bindings
        # Not at empty prompt, just insert the question mark
        commandline -i "?"
    end
end

# Set up keybindings
bind "?" _atuin_ai_question_mark
"#
    .trim()
}

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

    #[rstest]
    #[case::zsh(
        generate_zsh_integration(),
        &["self-atuin-ai-question-mark", "bindkey", "zle self-insert"]
    )]
    #[case::bash(
        generate_bash_integration(),
        &["_atuin_ai_question_mark", "bind", "READLINE_LINE"]
    )]
    #[case::fish(
        generate_fish_integration(),
        &["_atuin_ai_question_mark", "bind", "commandline"]
    )]
    fn generates_shell_integration(#[case] result: &str, #[case] extras: &[&str]) {
        for t in [
            "atuin ai inline --hook",
            "__atuin_ai_print__",
            "__atuin_ai_cancel__",
            "__atuin_ai_execute__",
            "__atuin_ai_insert__",
        ] {
            assert!(result.contains(t), "missing common token {t}");
        }
        for t in extras {
            assert!(result.contains(t), "missing shell token {t}");
        }
    }
}
