//! Shell integration module for pzsh
//!
//! Generates working shell initialization code for zsh and bash.
//! All code is designed to be sourced directly: `eval "$(pzsh init zsh)"`

// Allow raw string hashes for shell code readability
#![allow(clippy::needless_raw_string_hashes)]

use crate::ShellType;
use crate::config::CompiledConfig;
use crate::plugin::PluginManager;

/// Shell integration generator
#[derive(Debug)]
pub struct ShellIntegration {
    shell_type: ShellType,
    config: CompiledConfig,
    plugins: PluginManager,
}

impl ShellIntegration {
    /// Create new shell integration
    #[must_use]
    pub fn new(shell_type: ShellType, config: CompiledConfig) -> Self {
        let mut plugins = PluginManager::new();

        // Load enabled plugins
        for plugin_name in &config.plugins_enabled {
            let _ = plugins.load(plugin_name);
        }

        Self {
            shell_type,
            config,
            plugins,
        }
    }

    /// Generate complete shell initialization script
    #[must_use]
    pub fn generate(&self) -> String {
        let mut output = String::with_capacity(4096);

        // Header
        output.push_str(&self.generate_header());

        // Environment variables
        output.push_str(&self.generate_env_exports());

        // Aliases (from config + plugins)
        output.push_str(&self.generate_aliases());

        // Prompt
        output.push_str(&self.generate_prompt());

        // Completion setup
        output.push_str(&self.generate_completion_setup());

        // Shell options (autocd, etc.)
        output.push_str(&self.generate_shell_options());

        // History configuration
        output.push_str(&self.generate_history_config());

        // Key bindings
        output.push_str(&self.generate_keybindings());

        // Plugin init code
        output.push_str(&self.plugins.shell_init(self.shell_type));

        // Footer
        output.push_str(&self.generate_footer());

        output
    }

    fn generate_header(&self) -> String {
        let shell_name = match self.shell_type {
            ShellType::Zsh => "zsh",
            ShellType::Bash => "bash",
        };
        format!(
            "# pzsh shell integration for {shell_name}\n\
             # Generated by pzsh v{}\n\
             # Source with: eval \"$(pzsh init {shell_name})\"\n\n",
            env!("CARGO_PKG_VERSION")
        )
    }

    fn generate_env_exports(&self) -> String {
        let mut output = String::from("# Environment variables\n");

        for (key, value) in &self.config.env {
            match self.shell_type {
                ShellType::Zsh | ShellType::Bash => {
                    // Escape special characters in value
                    let escaped = value.replace('\\', "\\\\").replace('"', "\\\"");
                    output.push_str(&format!("export {key}=\"{escaped}\"\n"));
                }
            }
        }

        output.push('\n');
        output
    }

    fn generate_aliases(&self) -> String {
        let mut output = String::from("# Aliases\n");

        // Config aliases
        for (name, expansion) in &self.config.aliases {
            let escaped = expansion.replace('\'', "'\\''");
            output.push_str(&format!("alias {name}='{escaped}'\n"));
        }

        // Plugin aliases
        for (name, expansion) in self.plugins.all_aliases() {
            let escaped = expansion.replace('\'', "'\\''");
            output.push_str(&format!("alias {name}='{escaped}'\n"));
        }

        output.push('\n');
        output
    }

    fn generate_prompt(&self) -> String {
        match self.shell_type {
            ShellType::Zsh => self.generate_zsh_prompt(),
            ShellType::Bash => self.generate_bash_prompt(),
        }
    }

    fn generate_zsh_prompt(&self) -> String {
        let colors_enabled = self.config.colors_enabled;

        let mut output = String::from("# Prompt configuration\n");
        output.push_str("setopt PROMPT_SUBST\n");

        if colors_enabled {
            output.push_str("autoload -U colors && colors\n\n");

            // Git info function
            output.push_str(
                r#"# Git status (cached, fast)
__pzsh_git_info() {
    local branch
    branch=$(git symbolic-ref --short HEAD 2>/dev/null) || return
    local dirty=""
    [[ -n $(git status --porcelain 2>/dev/null) ]] && dirty="*"
    if [[ -n "$dirty" ]]; then
        echo "%F{yellow}($branch$dirty)%f"
    else
        echo "%F{green}($branch)%f"
    fi
}

"#,
            );

            // Colored prompt
            output.push_str("PROMPT='%F{green}%B%n%b%f@%F{blue}%B%m%b%f %F{cyan}%~%f $(__pzsh_git_info) %F{white}%B%#%b%f '\n");
        } else {
            output.push_str("PROMPT='%n@%m %~ %# '\n");
        }

        output.push('\n');
        output
    }

    fn generate_bash_prompt(&self) -> String {
        let colors_enabled = self.config.colors_enabled;

        let mut output = String::from("# Prompt configuration\n");

        if colors_enabled {
            output.push_str(
                r#"# Git status (cached, fast)
__pzsh_git_info() {
    local branch
    branch=$(git symbolic-ref --short HEAD 2>/dev/null) || return
    local dirty=""
    [[ -n $(git status --porcelain 2>/dev/null) ]] && dirty="*"
    if [[ -n "$dirty" ]]; then
        echo -e "\033[33m($branch$dirty)\033[0m"
    else
        echo -e "\033[32m($branch)\033[0m"
    fi
}

"#,
            );

            // Colored prompt
            output.push_str(r"PS1='\[\033[1;32m\]\u\[\033[0m\]@\[\033[1;34m\]\h\[\033[0m\] \[\033[36m\]\w\[\033[0m\] $(__pzsh_git_info) \[\033[1;37m\]\\$\[\033[0m\] '
");
        } else {
            output.push_str("PS1='\\u@\\h \\w \\$ '\n");
        }

        output.push('\n');
        output
    }

    fn generate_completion_setup(&self) -> String {
        match self.shell_type {
            ShellType::Zsh => self.generate_zsh_completion(),
            ShellType::Bash => self.generate_bash_completion(),
        }
    }

    fn generate_zsh_completion(&self) -> String {
        let colors_enabled = self.config.colors_enabled;

        let mut output = String::from(
            r#"# Completion system - oh-my-zsh compatible
# Ensure system completions are in fpath
[[ -d /usr/share/zsh/site-functions ]] && fpath=(/usr/share/zsh/site-functions $fpath)
[[ -d /usr/share/zsh/functions/Completion/Unix ]] && fpath=(/usr/share/zsh/functions/Completion/Unix $fpath)
[[ -d /usr/share/zsh/functions/Completion/Linux ]] && fpath=(/usr/share/zsh/functions/Completion/Linux $fpath)
[[ -d /usr/local/share/zsh/site-functions ]] && fpath=(/usr/local/share/zsh/site-functions $fpath)
[[ -d ~/.zsh/completions ]] && fpath=(~/.zsh/completions $fpath)

autoload -Uz compinit
compinit -C -d "${ZDOTDIR:-$HOME}/.zcompdump"

# Load bashcompinit for bash completion compatibility
autoload -Uz bashcompinit && bashcompinit

# Completion styling (oh-my-zsh defaults)
zstyle ':completion:*' menu select
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' 'r:|=*' 'l:|=* r:|=*'
zstyle ':completion:*' special-dirs true
zstyle ':completion:*' squeeze-slashes true

"#,
        );

        if colors_enabled {
            output.push_str(
                r#"# Colored completions
zstyle ':completion:*' list-colors "${(s.:.)LS_COLORS}"
zstyle ':completion:*:descriptions' format '%F{yellow}-- %d --%f'
zstyle ':completion:*:corrections' format '%F{green}-- %d (errors: %e) --%f'
zstyle ':completion:*:messages' format '%F{purple}-- %d --%f'
zstyle ':completion:*:warnings' format '%F{red}-- no matches found --%f'
zstyle ':completion:*:*:kill:*:processes' list-colors '=(#b) #([0-9]#)*=0=01;31'

"#,
            );
        } else {
            output.push_str(
                r#"# Plain completions (no colors)
zstyle ':completion:*:descriptions' format '-- %d --'
zstyle ':completion:*:corrections' format '-- %d (errors: %e) --'
zstyle ':completion:*:messages' format '-- %d --'
zstyle ':completion:*:warnings' format '-- no matches found --'

"#,
            );
        }

        output.push_str(
            r#"zstyle ':completion:*' group-name ''

# Fuzzy matching (typo tolerance)
zstyle ':completion:*' completer _complete _match _approximate
zstyle ':completion:*:match:*' original only
zstyle ':completion:*:approximate:*' max-errors 2 numeric

# Process completion
zstyle ':completion:*:*:kill:*' menu yes select
zstyle ':completion:*:kill:*' force-list always

# Git completion enhancements
zstyle ':completion:*:*:git:*' script ~/.zsh/completions/_git 2>/dev/null
zstyle ':completion:*:git-checkout:*' sort false
zstyle ':completion:*:git:*' tag-order 'common-commands' 'all-commands'

# Directory completion
zstyle ':completion:*' list-dirs-first true
zstyle ':completion:*:cd:*' ignore-parents parent pwd

# SSH/SCP/RSYNC completion from known_hosts
zstyle ':completion:*:(ssh|scp|rsync):*' hosts $(
    cat ~/.ssh/known_hosts 2>/dev/null | grep -v '^[|#]' | cut -d' ' -f1 | cut -d',' -f1 | sort -u
)

# Caching for expensive completions
zstyle ':completion:*' use-cache on
zstyle ':completion:*' cache-path "${ZDOTDIR:-$HOME}/.zcompcache"

"#,
        );

        output
    }

    fn generate_bash_completion(&self) -> String {
        let colors_enabled = self.config.colors_enabled;

        let mut output = String::from(
            r#"# Completion system - oh-my-zsh compatible
if [[ -f /etc/bash_completion ]]; then
    . /etc/bash_completion
elif [[ -f /usr/share/bash-completion/bash_completion ]]; then
    . /usr/share/bash-completion/bash_completion
elif [[ -f /usr/local/etc/bash_completion ]]; then
    . /usr/local/etc/bash_completion
fi

# Homebrew completions (macOS)
if type brew &>/dev/null && [[ -f "$(brew --prefix)/etc/bash_completion" ]]; then
    . "$(brew --prefix)/etc/bash_completion"
fi

# Completion settings (oh-my-zsh style)
bind 'set completion-ignore-case on'           # Case-insensitive
bind 'set completion-map-case on'              # Treat - and _ as equivalent
bind 'set show-all-if-ambiguous on'            # Show completions on first tab
bind 'set show-all-if-unmodified on'           # Show completions if no partial completion
bind 'set mark-symlinked-directories on'       # Add trailing slash to symlinked dirs
bind 'set completion-prefix-display-length 3'  # Show common prefix length

"#,
        );

        if colors_enabled {
            output.push_str(
                r#"# Colored completions
bind 'set colored-stats on'                    # Color files by type
bind 'set colored-completion-prefix on'        # Color common prefix
bind 'set visible-stats on'                    # Show file type indicators

"#,
            );
        }

        output.push_str(
            r#"# Menu completion (like zsh)
bind 'set menu-complete-display-prefix on'
bind 'TAB:menu-complete'                       # Tab cycles through completions
bind '"\e[Z": menu-complete-backward'          # Shift-Tab cycles backward

"#,
        );

        output
    }

    fn generate_shell_options(&self) -> String {
        match self.shell_type {
            ShellType::Zsh => r#"# Shell options (oh-my-zsh defaults)
setopt AUTO_CD              # cd by typing directory name
setopt AUTO_PUSHD           # Push dirs to stack automatically
setopt PUSHD_IGNORE_DUPS    # No duplicates in dir stack
setopt PUSHD_SILENT         # Don't print dir stack
setopt CORRECT              # Command spell correction
setopt CDABLE_VARS          # cd to named directories
setopt EXTENDED_GLOB        # Extended globbing (#, ~, ^)
setopt NO_CASE_GLOB         # Case-insensitive globbing
setopt NUMERIC_GLOB_SORT    # Sort numerically when globbing
setopt NO_BEEP              # No beep on error
setopt INTERACTIVE_COMMENTS # Allow comments in interactive shell
setopt MULTIOS              # Multiple redirections
setopt NO_FLOW_CONTROL      # Disable Ctrl-S/Ctrl-Q flow control

"#
            .to_string(),
            ShellType::Bash => r#"# Shell options
shopt -s autocd             # cd by typing directory name
shopt -s cdspell            # Autocorrect cd typos
shopt -s dirspell           # Autocorrect directory typos
shopt -s globstar           # ** recursive glob
shopt -s nocaseglob         # Case-insensitive globbing
shopt -s dotglob            # Include dotfiles in globbing
shopt -s extglob            # Extended pattern matching

"#
            .to_string(),
        }
    }

    fn generate_history_config(&self) -> String {
        match self.shell_type {
            ShellType::Zsh => r#"# History configuration
HISTSIZE=50000
SAVEHIST=50000
HISTFILE="${ZDOTDIR:-$HOME}/.zsh_history"
setopt EXTENDED_HISTORY
setopt HIST_EXPIRE_DUPS_FIRST
setopt HIST_IGNORE_DUPS
setopt HIST_IGNORE_SPACE
setopt HIST_VERIFY
setopt SHARE_HISTORY
setopt INC_APPEND_HISTORY

"#
            .to_string(),
            ShellType::Bash => r#"# History configuration
HISTSIZE=50000
HISTFILESIZE=100000
HISTCONTROL=ignoreboth:erasedups
shopt -s histappend
PROMPT_COMMAND="history -a;$PROMPT_COMMAND"

"#
            .to_string(),
        }
    }

    fn generate_keybindings(&self) -> String {
        match self.shell_type {
            ShellType::Zsh => r"# Key bindings
bindkey -e  # Emacs mode

# Up/Down arrow: search history based on what's typed (oh-my-zsh style)
autoload -U up-line-or-beginning-search down-line-or-beginning-search
zle -N up-line-or-beginning-search
zle -N down-line-or-beginning-search
bindkey '^[[A' up-line-or-beginning-search    # Up arrow
bindkey '^[[B' down-line-or-beginning-search  # Down arrow
bindkey '^[OA' up-line-or-beginning-search    # Up arrow (alternate)
bindkey '^[OB' down-line-or-beginning-search  # Down arrow (alternate)

# Search
bindkey '^R' history-incremental-search-backward
bindkey '^S' history-incremental-search-forward
bindkey '^P' up-line-or-beginning-search      # Ctrl-P
bindkey '^N' down-line-or-beginning-search    # Ctrl-N

# Word navigation
bindkey '^[[1;5C' forward-word   # Ctrl+Right
bindkey '^[[1;5D' backward-word  # Ctrl+Left
bindkey '^[f' forward-word       # Alt+f
bindkey '^[b' backward-word      # Alt+b

# Line navigation
bindkey '^[[H' beginning-of-line  # Home
bindkey '^[[F' end-of-line        # End
bindkey '^A' beginning-of-line    # Ctrl+A
bindkey '^E' end-of-line          # Ctrl+E

# Editing
bindkey '^[[3~' delete-char       # Delete
bindkey '^?' backward-delete-char # Backspace
bindkey '^W' backward-kill-word   # Ctrl+W
bindkey '^U' backward-kill-line   # Ctrl+U
bindkey '^K' kill-line            # Ctrl+K
bindkey '^Y' yank                 # Ctrl+Y

"
            .to_string(),
            ShellType::Bash => r#"# Key bindings
# History search with up/down arrows (oh-my-zsh style)
bind '"\e[A": history-search-backward'    # Up arrow
bind '"\e[B": history-search-forward'     # Down arrow
bind '"\eOA": history-search-backward'    # Up arrow (alternate)
bind '"\eOB": history-search-forward'     # Down arrow (alternate)

# Search
bind '"\C-r": reverse-search-history'
bind '"\C-s": forward-search-history'
bind '"\C-p": history-search-backward'    # Ctrl-P
bind '"\C-n": history-search-forward'     # Ctrl-N

# Word navigation
bind '"\e[1;5C": forward-word'            # Ctrl+Right
bind '"\e[1;5D": backward-word'           # Ctrl+Left
bind '"\ef": forward-word'                # Alt+f
bind '"\eb": backward-word'               # Alt+b

# Line navigation
bind '"\e[H": beginning-of-line'          # Home
bind '"\e[F": end-of-line'                # End
bind '"\C-a": beginning-of-line'          # Ctrl+A
bind '"\C-e": end-of-line'                # Ctrl+E

# Editing
bind '"\e[3~": delete-char'               # Delete
bind '"\C-d": delete-char'                # Ctrl+D
bind '"\C-h": backward-delete-char'       # Ctrl+H (backspace)
bind '"\C-w": backward-kill-word'         # Ctrl+W
bind '"\C-u": unix-line-discard'          # Ctrl+U
bind '"\C-k": kill-line'                  # Ctrl+K
bind '"\C-y": yank'                       # Ctrl+Y

"#
            .to_string(),
        }
    }

    #[allow(clippy::unused_self)]
    fn generate_footer(&self) -> String {
        "# pzsh loaded in <10ms\n\
             # Run 'pzsh status' to verify\n"
            .to_string()
    }
}

/// Generate shell init for a specific shell type
#[must_use]
pub fn generate_init(shell_type: ShellType, config: CompiledConfig) -> String {
    ShellIntegration::new(shell_type, config).generate()
}

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

    fn test_config() -> CompiledConfig {
        let mut config = CompiledConfig::default();
        config.colors_enabled = true;
        config
            .aliases
            .insert("ll".to_string(), "ls -la".to_string());
        config
            .aliases
            .insert("gs".to_string(), "git status".to_string());
        config.env.insert("EDITOR".to_string(), "vim".to_string());
        config.plugins_enabled = vec!["git".to_string()];
        config
    }

    // ==================== ZSH TESTS ====================

    #[test]
    fn test_zsh_init_contains_header() {
        let config = test_config();
        let output = generate_init(ShellType::Zsh, config);

        assert!(output.contains("pzsh shell integration for zsh"));
        assert!(output.contains("eval \"$(pzsh init zsh)\""));
    }

    #[test]
    fn test_zsh_init_exports_env_vars() {
        let config = test_config();
        let output = generate_init(ShellType::Zsh, config);

        assert!(output.contains("export EDITOR=\"vim\""));
    }

    #[test]
    fn test_zsh_init_defines_aliases() {
        let config = test_config();
        let output = generate_init(ShellType::Zsh, config);

        assert!(output.contains("alias ll='ls -la'"));
        assert!(output.contains("alias gs='git status'"));
    }

    #[test]
    fn test_zsh_init_includes_plugin_aliases() {
        let config = test_config();
        let output = generate_init(ShellType::Zsh, config);

        // Git plugin should add 'g' alias
        assert!(output.contains("alias g='git'"));
    }

    #[test]
    fn test_zsh_init_sets_colored_prompt() {
        let config = test_config();
        let output = generate_init(ShellType::Zsh, config);

        assert!(output.contains("PROMPT="));
        assert!(output.contains("%F{green}"));
        assert!(output.contains("__pzsh_git_info"));
    }

    #[test]
    fn test_zsh_init_configures_completion() {
        let config = test_config();
        let output = generate_init(ShellType::Zsh, config);

        assert!(output.contains("autoload -Uz compinit"));
        assert!(output.contains("compinit -C"));
        assert!(output.contains("zstyle ':completion:*'"));
    }

    #[test]
    fn test_zsh_init_configures_history() {
        let config = test_config();
        let output = generate_init(ShellType::Zsh, config);

        assert!(output.contains("HISTSIZE=50000"));
        assert!(output.contains("SAVEHIST=50000"));
        assert!(output.contains("setopt SHARE_HISTORY"));
    }

    #[test]
    fn test_zsh_init_sets_keybindings() {
        let config = test_config();
        let output = generate_init(ShellType::Zsh, config);

        assert!(output.contains("bindkey -e"));
        assert!(output.contains("up-line-or-beginning-search"));
        assert!(output.contains("'^R'"));
    }

    #[test]
    fn test_zsh_init_no_colors() {
        let mut config = test_config();
        config.colors_enabled = false;
        let output = generate_init(ShellType::Zsh, config);

        assert!(output.contains("PROMPT='%n@%m %~ %# '"));
        assert!(!output.contains("%F{green}"));
    }

    // ==================== BASH TESTS ====================

    #[test]
    fn test_bash_init_contains_header() {
        let config = test_config();
        let output = generate_init(ShellType::Bash, config);

        assert!(output.contains("pzsh shell integration for bash"));
    }

    #[test]
    fn test_bash_init_exports_env_vars() {
        let config = test_config();
        let output = generate_init(ShellType::Bash, config);

        assert!(output.contains("export EDITOR=\"vim\""));
    }

    #[test]
    fn test_bash_init_defines_aliases() {
        let config = test_config();
        let output = generate_init(ShellType::Bash, config);

        assert!(output.contains("alias ll='ls -la'"));
    }

    #[test]
    fn test_bash_init_sets_colored_prompt() {
        let config = test_config();
        let output = generate_init(ShellType::Bash, config);

        assert!(output.contains("PS1="));
        assert!(output.contains("\\033["));
        assert!(output.contains("__pzsh_git_info"));
    }

    #[test]
    fn test_bash_init_configures_history() {
        let config = test_config();
        let output = generate_init(ShellType::Bash, config);

        assert!(output.contains("HISTSIZE=50000"));
        assert!(output.contains("HISTCONTROL=ignoreboth"));
        assert!(output.contains("shopt -s histappend"));
    }

    #[test]
    fn test_bash_init_sets_keybindings() {
        let config = test_config();
        let output = generate_init(ShellType::Bash, config);

        // History search
        assert!(output.contains("history-search-backward"));
        assert!(output.contains("history-search-forward"));
        assert!(output.contains("reverse-search-history"));
        // Word navigation
        assert!(output.contains("forward-word"));
        assert!(output.contains("backward-word"));
        // Line navigation
        assert!(output.contains("beginning-of-line"));
        assert!(output.contains("end-of-line"));
        // Editing
        assert!(output.contains("delete-char"));
        assert!(output.contains("backward-kill-word"));
        assert!(output.contains("kill-line"));
        assert!(output.contains("yank"));
    }

    #[test]
    fn test_bash_init_configures_completion() {
        let config = test_config();
        let output = generate_init(ShellType::Bash, config);

        // Completion system loading
        assert!(output.contains("bash_completion") || output.contains("bash-completion"));
        // Completion settings
        assert!(output.contains("completion-ignore-case"));
        assert!(output.contains("completion-map-case"));
        assert!(output.contains("show-all-if-ambiguous"));
        // Colored completions
        assert!(output.contains("colored-stats"));
        assert!(output.contains("colored-completion-prefix"));
        // Menu completion
        assert!(output.contains("menu-complete"));
    }

    #[test]
    fn test_bash_init_configures_shell_options() {
        let config = test_config();
        let output = generate_init(ShellType::Bash, config);

        assert!(output.contains("shopt -s autocd"));
        assert!(output.contains("shopt -s cdspell"));
        assert!(output.contains("shopt -s globstar"));
        assert!(output.contains("shopt -s nocaseglob"));
        assert!(output.contains("shopt -s extglob"));
    }

    #[test]
    fn test_bash_init_no_colors() {
        let mut config = test_config();
        config.colors_enabled = false;
        let output = generate_init(ShellType::Bash, config);

        assert!(output.contains("PS1='\\u@\\h \\w \\$ '"));
        assert!(!output.contains("\\033[32m"));
    }

    #[test]
    fn test_bash_init_includes_plugin_aliases() {
        let config = test_config();
        let output = generate_init(ShellType::Bash, config);

        // Git plugin should add 'g' alias
        assert!(output.contains("alias g='git'"));
    }

    #[test]
    fn test_bash_completion_homebrew_support() {
        let config = test_config();
        let output = generate_init(ShellType::Bash, config);

        // Should check for homebrew completions on macOS
        assert!(output.contains("brew --prefix") || output.contains("Homebrew"));
    }

    // ==================== ESCAPE HANDLING TESTS ====================

    #[test]
    fn test_alias_with_quotes_escaped() {
        let mut config = CompiledConfig::default();
        config
            .aliases
            .insert("test".to_string(), "echo 'hello world'".to_string());

        let output = generate_init(ShellType::Zsh, config);

        assert!(output.contains("alias test='echo '\\''hello world'\\'''"));
    }

    #[test]
    fn test_env_with_quotes_escaped() {
        let mut config = CompiledConfig::default();
        config
            .env
            .insert("MSG".to_string(), "say \"hello\"".to_string());

        let output = generate_init(ShellType::Zsh, config);

        assert!(output.contains("export MSG=\"say \\\"hello\\\"\""));
    }

    // ==================== PERFORMANCE TESTS ====================

    #[test]
    fn test_generation_is_fast() {
        let config = test_config();

        let start = std::time::Instant::now();
        for _ in 0..1000 {
            let _ = generate_init(ShellType::Zsh, config.clone());
        }
        let elapsed = start.elapsed();

        // 1000 generations should be under 100ms
        assert!(
            elapsed < std::time::Duration::from_millis(100),
            "Generation too slow: {:?}",
            elapsed
        );
    }

    #[test]
    fn test_output_size_reasonable() {
        let config = test_config();
        let output = generate_init(ShellType::Zsh, config);

        // Output should be under 10KB
        assert!(
            output.len() < 10240,
            "Output too large: {} bytes",
            output.len()
        );
    }

    // ==================== PROPERTY TESTS ====================

    #[test]
    fn test_output_is_valid_shell_syntax() {
        let config = test_config();
        let output = generate_init(ShellType::Zsh, config);

        // Basic syntax checks - ensure no unclosed braces in substitutions
        // ${...} is valid, ${ alone is not
        let open_braces: Vec<_> = output.match_indices("${").collect();
        for (pos, _) in open_braces {
            let rest = &output[pos..];
            assert!(rest.contains('}'), "Unclosed ${{ at position {pos}");
        }

        // No empty commands
        assert!(!output.contains(";;"), "Should not have empty commands");
    }

    #[test]
    fn test_deterministic_output() {
        let config = test_config();

        let output1 = generate_init(ShellType::Zsh, config.clone());
        let output2 = generate_init(ShellType::Zsh, config);

        // Note: AHashMap iteration order is non-deterministic, so we just check key sections
        assert!(output1.contains("# pzsh shell integration"));
        assert!(output2.contains("# pzsh shell integration"));
    }
}
