def get_bash_completion_script(name: str = "") -> str:
    """
    Generate a bash completion script for poe.

    The script provides completion for:
    - Global CLI options (--help, -C, etc.)
    - Task names
    - Task-specific arguments (options and positionals)
    - Argument choices when defined
    - -C/--directory passthrough for alternate project paths
    """
    from pathlib import Path

    from ..app import PoeThePoet

    name = name or "poe"
    func_name = f"_{name}_complete"

    # Get global options from argparse
    app = PoeThePoet(cwd=Path().resolve())
    parser = app.ui.build_parser()

    global_opts: list[str] = []
    opts_with_values: list[str] = []
    for opt in parser._action_groups[1]._group_actions:
        if opt.help == "==SUPPRESS==":
            continue
        global_opts.extend(opt.option_strings)
        # Track options that take values
        takes_value = opt.nargs != 0 and not (
            opt.const is not None and opt.nargs is None and opt.type is None
        )
        if takes_value:
            opts_with_values.extend(opt.option_strings)

    global_opts_str = " ".join(global_opts)
    opts_with_values_str = "|".join(opts_with_values) if opts_with_values else "_NONE_"

    return f"""# Bash completion for {name}
# Generated by poethepoet

{func_name}() {{
    local cur prev words cword
    _init_completion -n = 2>/dev/null || {{
        COMPREPLY=()
        # Merge = signs split by COMP_WORDBREAKS (replicates -n = behavior)
        local _i _w _last_idx
        local -a _mw=()
        local _mc=0
        for (( _i=0; _i < ${{#COMP_WORDS[@]}}; _i++ )); do
            _w="${{COMP_WORDS[_i]}}"
            _last_idx=$(( ${{#_mw[@]}} - 1 ))
            if [[ "$_w" == "=" && ${{#_mw[@]}} -gt 0 ]]; then
                _mw[$_last_idx]+="="
                (( _i == COMP_CWORD )) && _mc=$_last_idx
            elif [[ ${{#_mw[@]}} -gt 0 && "${{_mw[$_last_idx]}}" == *= ]]; then
                _mw[$_last_idx]+="$_w"
                (( _i == COMP_CWORD )) && _mc=$_last_idx
            else
                (( _i == COMP_CWORD )) && _mc=${{#_mw[@]}}
                _mw+=("$_w")
            fi
        done
        words=("${{_mw[@]}}")
        cword=$_mc
        cur="${{words[cword]}}"
        if (( cword > 0 )); then prev="${{words[cword-1]}}"; else prev=""; fi
    }}

    local target_path=""
    local task_position=""
    local potential_task=""
    local after_separator=""
    local i

    # Extract target_path from -C/--directory/--root and find task position
    for ((i=1; i < ${{#words[@]}}; i++)); do
        # Once we have a task, check for -- separator
        if [[ -n "$task_position" ]]; then
            if [[ "${{words[i]}}" == "--" ]]; then
                after_separator=1
            fi
            continue
        fi
        case "${{words[i]}}" in
            -C=*|--directory=*|--root=*)
                target_path="${{words[i]#*=}}"
                ;;
            -C|--directory|--root)
                if ((i+1 < ${{#words[@]}})); then
                    target_path="${{words[i+1]}}"
                    ((i++))
                fi
                ;;
            -*)
                # Skip options and their values (only if no = inline)
                if [[ "${{words[i]}}" != *=* ]]; then
                    case "${{words[i]}}" in
                        {opts_with_values_str})
                            ((i++))  # skip the value too
                            ;;
                    esac
                fi
                ;;
            *)
                # First non-option word is the task
                if [[ -z "$task_position" ]]; then
                    task_position=$i
                    potential_task="${{words[i]}}"
                fi
                ;;
        esac
    done

    # Complete global option values (early return) — only before the task
    if [[ -z "$task_position" ]] || ((cword <= task_position)); then
        # Handle --opt=value style for global options
        case "$cur" in
            -C=*|--directory=*|--root=*)
                local val="${{cur#*=}}" prefix="${{cur%%=*}}="
                _filedir 2>/dev/null || COMPREPLY=($(compgen -f -- "$val"))
                COMPREPLY=("${{COMPREPLY[@]/#/$prefix}}")
                return
                ;;
            -e=*|--executor=*)
                local val="${{cur#*=}}" prefix="${{cur%%=*}}="
                local -a vals
                vals=($(compgen -W "auto poetry simple uv virtualenv" -- "$val"))
                COMPREPLY=("${{vals[@]/#/$prefix}}")
                return
                ;;
        esac
        case "$prev" in
            -C|--directory|--root)
                _filedir 2>/dev/null || COMPREPLY=($(compgen -f -- "$cur"))
                return
                ;;
            -e|--executor)
                COMPREPLY=($(compgen -W "auto poetry simple uv virtualenv" -- "$cur"))
                return
                ;;
        esac
    fi

    # If cursor is at or before task position, complete tasks/global options
    if [[ -z "$task_position" ]] || ((cword <= task_position)); then
        if [[ "$cur" == -* ]]; then
            COMPREPLY=($(compgen -W "{global_opts_str}" -- "$cur"))
        else
            # Only call _list_tasks when actually completing task names
            local tasks
            tasks=$({name} _list_tasks "$target_path" 2>/dev/null)
            COMPREPLY=($(compgen -W "$tasks" -- "$cur"))
        fi
        return
    fi

    # After --, only offer file completions (pass-through args to task)
    if [[ "$after_separator" == "1" ]] && ((cword > task_position)); then
        _filedir 2>/dev/null || COMPREPLY=($(compgen -f -- "$cur"))
        return
    fi

    # We're past the task position - complete task args
    local current_task="$potential_task"
    local show_task_opts=false

    # Get full task args info (tab-separated: opts, type, help, choices)
    local task_args_data
    task_args_data=$({name} _describe_task_args \
        "$current_task" "$target_path" 2>/dev/null)

    # Build list of option strings and track positional args
    local -a task_opts=()
    local -a positional_choices=()
    local -a boolean_opts=()
    while IFS=$'\\t' read -r opts arg_type help_text choices; do
        [[ -z "$opts" ]] && continue
        [[ "$choices" == "_" ]] && choices=""
        if [[ "$arg_type" == "positional" ]]; then
            positional_choices+=("$choices")
        else
            # Split comma-separated options
            IFS=',' read -ra opt_arr <<< "$opts"
            task_opts+=("${{opt_arr[@]}}")
            if [[ "$arg_type" == "boolean" ]]; then
                boolean_opts+=("${{opt_arr[@]}}")
            fi
        fi
    done <<< "$task_args_data"

    # Count positional args already provided
    local positional_index=0
    for ((i=task_position+1; i < cword; i++)); do
        local word="${{words[i]}}"
        if [[ "$word" == -* ]]; then
            # Check if this is a known option (strip =value for matching)
            local opt_name="${{word%%=*}}"
            local is_opt=false is_bool=false
            for opt in "${{task_opts[@]}}"; do
                [[ "$opt_name" == "$opt" ]] && is_opt=true && break
            done
            if [[ "$is_opt" == true ]]; then
                for opt in "${{boolean_opts[@]}}"; do
                    [[ "$opt_name" == "$opt" ]] && is_bool=true && break
                done
                # Only skip next word if no = inline and not boolean
                [[ "$is_bool" == false && "$word" != *=* ]] && ((i++))
            fi
        else
            ((positional_index++))
        fi
    done

    # Handle --opt=value style for task options
    if [[ "$cur" == -*=* ]]; then
        local opt_part="${{cur%%=*}}" val_part="${{cur#*=}}"
        local opt_choices=""
        while IFS=$'\\t' read -r opts arg_type help_text choices; do
            [[ -z "$opts" ]] && continue
            [[ "$choices" == "_" ]] && choices=""
            if [[ ",$opts," == *",$opt_part,"* ]]; then
                opt_choices="$choices"
                break
            fi
        done <<< "$task_args_data"
        local prefix="${{opt_part}}="
        if [[ -n "$opt_choices" ]]; then
            local -a vals
            vals=($(compgen -W "$opt_choices" -- "$val_part"))
            COMPREPLY=("${{vals[@]/#/$prefix}}")
        else
            _filedir 2>/dev/null || COMPREPLY=($(compgen -f -- "$val_part"))
            COMPREPLY=("${{COMPREPLY[@]/#/$prefix}}")
        fi
        return
    fi

    # If prev is an option, try to complete its value
    if [[ "$prev" == -* ]]; then
        # Find choices for this option
        local opt_choices=""
        local is_boolean=false
        while IFS=$'\\t' read -r opts arg_type help_text choices; do
            [[ -z "$opts" ]] && continue
            [[ "$choices" == "_" ]] && choices=""
            if [[ ",$opts," == *",$prev,"* ]]; then
                if [[ "$arg_type" == "boolean" ]]; then
                    is_boolean=true
                else
                    opt_choices="$choices"
                fi
                break
            fi
        done <<< "$task_args_data"

        if [[ "$is_boolean" == true ]]; then
            show_task_opts=true
        elif [[ -n "$opt_choices" ]]; then
            COMPREPLY=($(compgen -W "$opt_choices" -- "$cur"))
            return
        elif [[ "$cur" == -* ]]; then
            show_task_opts=true
        else
            _filedir 2>/dev/null || COMPREPLY=($(compgen -f -- "$cur"))
            return
        fi
    fi

    # Show task options if cur starts with - OR we just handled a boolean flag
    if [[ "$cur" == -* ]] || [[ "$show_task_opts" == true ]]; then
        # Build set of all used option forms (including equivalents) in ONE pass
        # This avoids O(n²) re-parsing of task_args_data for each option
        local used_forms=" "
        for ((j=task_position+1; j < ${{#words[@]}}; j++)); do
            local word="${{words[j]}}"
            [[ "$word" != -* ]] && continue
            word="${{word%%=*}}"  # Strip =value for matching
            # Find this word's equivalence group and add ALL forms
            while IFS=$'\\t' read -r opts arg_type help_text choices; do
                [[ -z "$opts" ]] && continue
                if [[ ",$opts," == *",$word,"* ]]; then
                    # Add all equivalent forms to used set
                    used_forms+="${{opts//,/ }} "
                    break
                fi
            done <<< "$task_args_data"
        done

        # Now filter: simple membership check
        local filtered_args=""
        for arg in "${{task_opts[@]}}"; do
            [[ "$used_forms" != *" $arg "* ]] && filtered_args+=" $arg"
        done

        if [[ -n "$filtered_args" ]]; then
            COMPREPLY=($(compgen -W "$filtered_args" -- "$cur"))
            return
        fi
    fi

    # Try positional argument choices
    if (( positional_index < ${{#positional_choices[@]}} )); then
        local pos_choices="${{positional_choices[positional_index]}}"
        if [[ -n "$pos_choices" ]]; then
            COMPREPLY=($(compgen -W "$pos_choices" -- "$cur"))
            return
        fi
    fi

    # Fall back to file completion
    _filedir 2>/dev/null || COMPREPLY=($(compgen -f -- "$cur"))
}}

complete -F {func_name} {name}
"""
