//! Zsh shell integration

use crate::{file_resolution_check, scoop_version_check};

/// Generate zsh initialization script
pub fn init_script() -> &'static str {
    concat!(
        r#"# scoop shell integration for zsh

# Disable completion sorting for scoop (preserves command order)
zstyle ':completion:*:scoop:*' sort false

# Wrapper function for scoop
scoop() {
    local command="${1:-}"

    case "$command" in
        use)
            command scoop "$@"
            local ret=$?
            if [[ $ret -eq 0 ]]; then
                shift  # remove 'use'
                local name=""
                for arg in "$@"; do
                    case "$arg" in
                        -*) ;;  # skip options
                        *) name="$arg"; break ;;
                    esac
                done
                if [[ -n "$name" ]]; then
                    eval "$(command scoop activate "$name")"
                fi
            fi
            return $ret
            ;;
        activate|deactivate|shell)
            # Pass through help/version flags without eval
            if [[ "$*" == *--help* ]] || [[ "$*" == *-h* ]] || [[ "$*" == *--version* ]] || [[ "$*" == *-V* ]]; then
                command scoop "$@"
            else
                eval "$(command scoop "$@")"
            fi
            ;;
        *)
            command scoop "$@"
            ;;
    esac
}

# Auto-activate hook
_scoop_hook() {
"#,
        scoop_version_check!(zsh),
        file_resolution_check!(zsh),
        r#"
}

# Set up chpwd hook for auto-activate
if [[ -z "$SCOOP_NO_AUTO" ]]; then
    autoload -Uz add-zsh-hook
    add-zsh-hook chpwd _scoop_hook
fi

# Run hook on startup
_scoop_hook

# Zsh completion for scoop
_scoop() {
    local curcontext="$curcontext" state line
    typeset -A opt_args
    local cur="${words[$CURRENT]}"

    _arguments -C \
        '1: :->command' \
        '*: :->args'

    case $state in
        command)
            local commands=(
                'list:List all virtual environments'
                'use:Set local environment for current directory'
                'create:Create a new virtual environment'
                'remove:Remove a virtual environment'
                'info:Show detailed information about a virtual environment'
                'install:Install a Python version'
                'uninstall:Uninstall a Python version'
                'doctor:Diagnose installation issues'
                'init:Output shell initialization script'
                'completions:Output shell completion script'
                'activate:Activate a virtual environment'
                'deactivate:Deactivate current virtual environment'
                'shell:Set shell-specific environment'
                'migrate:Migrate environments from other tools'
                'lang:Set or show language preference'
            )
            _describe -V 'command' commands
            ;;
        args)
            case $line[1] in
                use)
                    if [[ $cur == -* ]]; then
                        local opts=('--help:Show help')
                        local has_unset=false has_global=false has_link=false has_quiet=false has_nocolor=false
                        for w in "${words[@]}"; do
                            case "$w" in
                                --unset) has_unset=true ;;
                                --global) has_global=true ;;
                                --link|--no-link) has_link=true ;;
                                -q|--quiet) has_quiet=true ;;
                                --no-color) has_nocolor=true ;;
                            esac
                        done
                        [[ $has_unset == false ]] && opts+=('--unset:Remove version setting')
                        [[ $has_link == false ]] && opts+=('--link:Create .venv symlink' '--no-link:Do not create .venv symlink')
                        [[ $has_global == false ]] && opts+=('--global:Set as global default')
                        [[ $has_quiet == false ]] && opts+=('-q:Suppress all output' '--quiet:Suppress all output')
                        [[ $has_nocolor == false ]] && opts+=('--no-color:Disable colored output')
                        _describe 'option' opts
                    else
                        # Check if env name already provided (exclude current word being typed)
                        local has_env=false
                        local prev_args=("${words[@]:2:$((CURRENT-3))}")
                        for w in "${prev_args[@]}"; do
                            [[ $w != -* && -n $w ]] && has_env=true && break
                        done
                        if [[ $has_env == false ]]; then
                            local envs=(${(f)"$(command scoop list --bare 2>/dev/null)"})
                            compadd -a envs
                        fi
                    fi
                    ;;
                remove)
                    if [[ $cur == -* ]]; then
                        local opts=('--help:Show help')
                        local has_force=false has_quiet=false has_nocolor=false
                        for w in "${words[@]}"; do
                            case "$w" in
                                --force) has_force=true ;;
                                -q|--quiet) has_quiet=true ;;
                                --no-color) has_nocolor=true ;;
                            esac
                        done
                        [[ $has_force == false ]] && opts+=('--force:Skip confirmation')
                        [[ $has_quiet == false ]] && opts+=('-q:Suppress all output' '--quiet:Suppress all output')
                        [[ $has_nocolor == false ]] && opts+=('--no-color:Disable colored output')
                        _describe 'option' opts
                    else
                        # Check if env name already provided (exclude current word being typed)
                        local has_env=false
                        local prev_args=("${words[@]:2:$((CURRENT-3))}")
                        for w in "${prev_args[@]}"; do
                            [[ $w != -* && -n $w ]] && has_env=true && break
                        done
                        if [[ $has_env == false ]]; then
                            local envs=(${(f)"$(command scoop list --bare 2>/dev/null)"})
                            compadd -a envs
                        fi
                    fi
                    ;;
                info)
                    if [[ $cur == -* ]]; then
                        local opts=('--help:Show help')
                        local has_json=false has_allpackages=false has_nosize=false has_quiet=false has_nocolor=false
                        for w in "${words[@]}"; do
                            case "$w" in
                                --json) has_json=true ;;
                                --all-packages) has_allpackages=true ;;
                                --no-size) has_nosize=true ;;
                                -q|--quiet) has_quiet=true ;;
                                --no-color) has_nocolor=true ;;
                            esac
                        done
                        [[ $has_json == false ]] && opts+=('--json:Output as JSON')
                        [[ $has_allpackages == false ]] && opts+=('--all-packages:Show all installed packages')
                        [[ $has_nosize == false ]] && opts+=('--no-size:Skip directory size calculation')
                        [[ $has_quiet == false ]] && opts+=('-q:Suppress all output' '--quiet:Suppress all output')
                        [[ $has_nocolor == false ]] && opts+=('--no-color:Disable colored output')
                        _describe 'option' opts
                    else
                        # Check if env name already provided (exclude current word being typed)
                        local has_env=false
                        local prev_args=("${words[@]:2:$((CURRENT-3))}")
                        for w in "${prev_args[@]}"; do
                            [[ $w != -* && -n $w ]] && has_env=true && break
                        done
                        if [[ $has_env == false ]]; then
                            local envs=(${(f)"$(command scoop list --bare 2>/dev/null)"})
                            compadd -a envs
                        fi
                    fi
                    ;;
                activate)
                    # Check if env name already provided (exclude current word being typed)
                    local has_env=false
                    local prev_args=("${words[@]:2:$((CURRENT-3))}")
                    for w in "${prev_args[@]}"; do
                        [[ $w != -* && -n $w ]] && has_env=true && break
                    done
                    if [[ $has_env == false ]]; then
                        local envs=(${(f)"$(command scoop list --bare 2>/dev/null)"})
                        compadd -a envs
                    fi
                    ;;
                install)
                    if [[ $cur == -* ]]; then
                        local opts=('--help:Show help')
                        local has_version_opt=false has_quiet=false has_nocolor=false
                        for w in "${words[@]}"; do
                            case "$w" in
                                --latest|--stable) has_version_opt=true ;;
                                -q|--quiet) has_quiet=true ;;
                                --no-color) has_nocolor=true ;;
                            esac
                        done
                        [[ $has_version_opt == false ]] && opts+=('--latest:Install latest stable Python' '--stable:Install oldest fully-supported Python')
                        [[ $has_quiet == false ]] && opts+=('-q:Suppress all output' '--quiet:Suppress all output')
                        [[ $has_nocolor == false ]] && opts+=('--no-color:Disable colored output')
                        _describe 'option' opts
                    fi
                    ;;
                uninstall)
                    if [[ $cur == -* ]]; then
                        local opts=('--help:Show help')
                        local has_quiet=false has_nocolor=false
                        for w in "${words[@]}"; do
                            case "$w" in
                                -q|--quiet) has_quiet=true ;;
                                --no-color) has_nocolor=true ;;
                            esac
                        done
                        [[ $has_quiet == false ]] && opts+=('-q:Suppress all output' '--quiet:Suppress all output')
                        [[ $has_nocolor == false ]] && opts+=('--no-color:Disable colored output')
                        _describe 'option' opts
                    else
                        # Check if version already provided (exclude current word being typed)
                        local has_ver=false
                        local prev_args=("${words[@]:2:$((CURRENT-3))}")
                        for w in "${prev_args[@]}"; do
                            [[ $w != -* && -n $w ]] && has_ver=true && break
                        done
                        if [[ $has_ver == false ]]; then
                            local pythons=(${(uf)"$(command scoop list --pythons --bare 2>/dev/null)"})
                            compadd -a pythons
                        fi
                    fi
                    ;;
                list)
                    # `--sort` value completion: when the previous word is
                    # `--sort`, surface the enum values instead of the
                    # flag list. The `--sort=` form is handled by zsh's
                    # built-in option-with-value matcher when the user
                    # types `=`.
                    local prev_word="${words[CURRENT-1]:-}"
                    if [[ "$prev_word" == "--sort" ]]; then
                        local sort_vals=(
                            'name:Alphabetical (default)'
                            'created:Newest created first'
                            'last-used:Most recently used first'
                        )
                        _describe 'sort mode' sort_vals
                        return 0
                    fi
                    if [[ $cur == -* ]]; then
                        local opts=('--help:Show help')
                        local has_pythons=false has_sort=false has_json=false has_quiet=false has_nocolor=false
                        for w in "${words[@]}"; do
                            case "$w" in
                                --pythons) has_pythons=true ;;
                                --sort|--sort=*) has_sort=true ;;
                                --json) has_json=true ;;
                                -q|--quiet) has_quiet=true ;;
                                --no-color) has_nocolor=true ;;
                            esac
                        done
                        [[ $has_pythons == false ]] && opts+=('--pythons:Show installed Python versions')
                        [[ $has_sort == false ]] && opts+=('--sort:Sort order (name|created|last-used)')
                        [[ $has_json == false ]] && opts+=('--json:Output as JSON')
                        [[ $has_quiet == false ]] && opts+=('-q:Suppress all output' '--quiet:Suppress all output')
                        [[ $has_nocolor == false ]] && opts+=('--no-color:Disable colored output')
                        _describe 'option' opts
                    fi
                    ;;
                doctor)
                    if [[ $cur == -* ]]; then
                        local opts=('--help:Show help')
                        # Check which options are already used
                        local has_verbose=false has_quiet=false has_json=false has_nocolor=false
                        for w in "${words[@]}"; do
                            case "$w" in
                                -v|--verbose) has_verbose=true ;;
                                -q|--quiet) has_quiet=true ;;
                                --json) has_json=true ;;
                                --no-color) has_nocolor=true ;;
                            esac
                        done
                        # Add only unused options
                        [[ $has_verbose == false ]] && opts+=('-v:Increase verbosity' '--verbose:Increase verbosity')
                        [[ $has_quiet == false ]] && opts+=('-q:Suppress all output' '--quiet:Suppress all output')
                        [[ $has_json == false ]] && opts+=('--json:Output as JSON')
                        [[ $has_nocolor == false ]] && opts+=('--no-color:Disable colored output')
                        _describe 'option' opts
                    fi
                    ;;
                create)
                    if [[ $cur == -* ]]; then
                        local opts=('--help:Show help')
                        local has_force=false has_quiet=false has_nocolor=false
                        for w in "${words[@]}"; do
                            case "$w" in
                                --force) has_force=true ;;
                                -q|--quiet) has_quiet=true ;;
                                --no-color) has_nocolor=true ;;
                            esac
                        done
                        [[ $has_force == false ]] && opts+=('--force:Overwrite existing environment')
                        [[ $has_quiet == false ]] && opts+=('-q:Suppress all output' '--quiet:Suppress all output')
                        [[ $has_nocolor == false ]] && opts+=('--no-color:Disable colored output')
                        _describe 'option' opts
                    else
                        # Count positional args before current word
                        local pos_count=0
                        local prev_args=("${words[@]:2:$((CURRENT-3))}")
                        for w in "${prev_args[@]}"; do
                            [[ $w != -* && -n $w ]] && ((pos_count++))
                        done
                        if [[ $pos_count -eq 1 ]]; then
                            # Second positional arg: python version
                            local pythons=(${(uf)"$(command scoop list --pythons --bare 2>/dev/null)"})
                            compadd -a pythons
                        fi
                    fi
                    ;;
                init|completions)
                    local shells=('bash:Bash shell' 'zsh:Zsh shell' 'fish:Fish shell' 'powershell:PowerShell')
                    _describe 'shell' shells
                    ;;
                lang)
                    if [[ $cur == -* ]]; then
                        local opts=('--help:Show help')
                        local has_list=false has_reset=false has_json=false
                        for w in "${words[@]}"; do
                            case "$w" in
                                --list) has_list=true ;;
                                --reset) has_reset=true ;;
                                --json) has_json=true ;;
                            esac
                        done
                        [[ $has_list == false ]] && opts+=('--list:List supported languages')
                        [[ $has_reset == false ]] && opts+=('--reset:Reset to system default')
                        [[ $has_json == false ]] && opts+=('--json:Output as JSON')
                        _describe 'option' opts
                    else
                        local langs=('en:English' 'ko:Korean' 'ja:Japanese' 'pt-BR:Portuguese (Brazilian)')
                        _describe 'language' langs
                    fi
                    ;;
                shell)
                    if [[ $cur == -* ]]; then
                        local opts=('--help:Show help')
                        local has_unset=false has_quiet=false has_nocolor=false
                        for w in "${words[@]}"; do
                            case "$w" in
                                --unset) has_unset=true ;;
                                -q|--quiet) has_quiet=true ;;
                                --no-color) has_nocolor=true ;;
                            esac
                        done
                        [[ $has_unset == false ]] && opts+=('--unset:Clear shell-specific environment')
                        [[ $has_quiet == false ]] && opts+=('-q:Suppress all output' '--quiet:Suppress all output')
                        [[ $has_nocolor == false ]] && opts+=('--no-color:Disable colored output')
                        _describe 'option' opts
                    else
                        # Check if env name already provided (exclude current word being typed)
                        local has_env=false
                        local prev_args=("${words[@]:2:$((CURRENT-3))}")
                        for w in "${prev_args[@]}"; do
                            [[ $w != -* && -n $w ]] && has_env=true && break
                        done
                        if [[ $has_env == false ]]; then
                            local envs=(${(f)"$(command scoop list --bare 2>/dev/null)"})
                            compadd -a envs
                        fi
                    fi
                    ;;
                migrate)
                    if [[ $cur == -* ]]; then
                        local opts=('--help:Show help')
                        _describe 'option' opts
                    else
                        local subcmds=('list:List environments available for migration' 'all:Migrate all environments' '@env:Migrate a specific environment')
                        _describe 'subcommand' subcmds
                    fi
                    ;;
            esac
            ;;
    esac
}

# Register completion only if compdef is available (requires compinit)
(( $+functions[compdef] )) && compdef _scoop scoop
"#
    )
}

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

    // =========================================================================
    // Real Test: Syntax Validation with zsh -n
    // =========================================================================

    /// Validates that the generated script has valid zsh syntax.
    /// This is a REAL test - it actually runs zsh to check the script.
    #[test]
    #[cfg(unix)]
    fn test_init_script_has_valid_zsh_syntax() {
        let script = init_script();

        // Use zsh -n for syntax checking (parse only, don't execute)
        let output = std::process::Command::new("zsh")
            .arg("-n") // syntax check only
            .arg("-c")
            .arg(script)
            .output();

        match output {
            Ok(result) => {
                assert!(
                    result.status.success(),
                    "Zsh script has syntax errors:\n{}",
                    String::from_utf8_lossy(&result.stderr)
                );
            }
            Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
                // zsh not available, skip test
                eprintln!("Skipping zsh syntax test: zsh not found");
            }
            Err(e) => panic!("Failed to run zsh: {}", e),
        }
    }

    // =========================================================================
    // Structural Tests: Minimal checks for required components
    // =========================================================================

    #[test]
    fn test_init_script_not_empty() {
        let script = init_script();
        assert!(!script.is_empty(), "Script should not be empty");
    }

    #[test]
    fn test_init_script_defines_required_functions() {
        let script = init_script();

        // These functions MUST exist for the shell integration to work
        let required_functions = ["scoop()", "_scoop_hook()", "_scoop()"];

        for func in required_functions {
            assert!(
                script.contains(func),
                "Script missing required function: {}",
                func
            );
        }
    }

    #[test]
    fn test_init_script_registers_chpwd_hook() {
        let script = init_script();

        // zsh uses chpwd hook for directory change detection
        assert!(
            script.contains("add-zsh-hook chpwd _scoop_hook"),
            "Script must register chpwd hook for auto-activation"
        );
    }

    #[test]
    fn test_init_script_registers_completion() {
        let script = init_script();

        // Must register completion function with compdef
        assert!(
            script.contains("compdef _scoop scoop"),
            "Script must register zsh completion"
        );
    }

    #[test]
    fn test_init_script_loads_zsh_hooks_module() {
        let script = init_script();

        // Must autoload zsh hooks module
        assert!(
            script.contains("autoload -Uz add-zsh-hook"),
            "Script must load zsh hooks module"
        );
    }

    // =========================================================================
    // Real Test: Best Practices Validation with shellcheck
    // =========================================================================

    /// Validates that the generated script follows shell best practices.
    /// Note: shellcheck uses bash mode for zsh (no native zsh support),
    /// but catches most common issues.
    #[test]
    #[cfg(unix)]
    fn test_init_script_passes_shellcheck() {
        let script = init_script();

        // Write script to temp file (shellcheck requires file input)
        let temp_file = tempfile::NamedTempFile::new().unwrap();
        std::fs::write(temp_file.path(), script).unwrap();

        // shellcheck doesn't have native zsh support, but bash mode catches most issues
        // Use --exclude for zsh-specific constructs that bash doesn't understand
        let output = std::process::Command::new("shellcheck")
            .arg("--shell=bash")
            .arg("--severity=warning")
            // Exclude zsh-specific constructs:
            // SC1087: $line[1] array syntax (zsh doesn't require braces)
            // SC2034: Unused variable (zsh uses typeset -A)
            // SC2154: Variable referenced but not assigned (zsh completion vars)
            // SC2168: 'local' outside function (zsh completion context)
            // SC2206: Quote to prevent word splitting (zsh handles this differently)
            // SC2207: COMPREPLY=($(compgen ...)) is standard completion idiom
            // SC2296: ${(f)...} parameter expansion flags (zsh-specific)
            // SC3030: Array syntax (zsh-specific)
            // SC3057: Associative array syntax (zsh-specific)
            .arg("--exclude=SC1087,SC2034,SC2154,SC2168,SC2206,SC2207,SC2296,SC3030,SC3057")
            .arg(temp_file.path())
            .output();

        match output {
            Ok(result) => {
                assert!(
                    result.status.success(),
                    "shellcheck found issues in zsh script:\n{}",
                    String::from_utf8_lossy(&result.stdout)
                );
            }
            Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
                eprintln!(
                    "Skipping shellcheck test: shellcheck not found (install: brew install shellcheck)"
                );
            }
            Err(e) => panic!("Failed to run shellcheck: {}", e),
        }
    }
}
