#!/usr/bin/env bash
set -euo pipefail
# POS: ai alias — manage AI agent aliases
# POS_SUBCMDS: create edit remove list show

source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
source "$(dirname "$0")/../lib/menu-lib.sh" 2>/dev/null || source "$(dirname "$0")/menu-lib.sh"

# ── Paths & constants ──────────────────────────────────────────
ENV_FILE="${CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/linux_post_install}/ai-aliases.env"
SH_FILE="${CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/linux_post_install}/ai-aliases.sh"

# Provider discovery — same pattern as bin/pos-ai (lines 17-18)
PROVIDER_DIR="$(dirname "$0")/../lib/ai-providers"
[ -d "$PROVIDER_DIR" ] || PROVIDER_DIR="$(dirname "$0")/ai-providers"

# ── Core helpers ───────────────────────────────────────────────

_alias_load() {
    _ALIAS_NAMES=(); _ALIAS_PROVIDERS=(); _ALIAS_SESSIONS=(); _ALIAS_PROMPTS=()
    _ALIAS_TRUSTED=()
    [ -f "$ENV_FILE" ] || return 0
    # NOTE: loop vars use _l* prefix to avoid dynamic-scope collision with
    # callers that declare 'local name' (bash read clobbers the nearest
    # matching variable up the call chain).
    local _ln _lp _ls _lp2 _lr
    while IFS='|' read -r _ln _lp _ls _lp2 _lr; do
        [[ "$_ln" =~ ^[[:space:]]*# ]] && continue
        [[ -z "${_ln// /}" ]] && continue
        _ln="${_ln## }"; _ln="${_ln%% }"
        [[ "$_ln" =~ ^[a-zA-Z][a-zA-Z0-9_-]*$ ]] || continue
        _lp="${_lp## }"; _lp="${_lp%% }"
        _ls="${_ls## }"; _ls="${_ls%% }"
        _ALIAS_NAMES+=("$_ln")
        _ALIAS_PROVIDERS+=("$_lp")
        _ALIAS_SESSIONS+=("$_ls")
        _ALIAS_PROMPTS+=("$(_alias_prompt_decode "$_lp2")")
        _ALIAS_TRUSTED+=("${_lr:-0}")
    done < <(grep -v '^[[:space:]]*#' "$ENV_FILE" | grep -v '^[[:space:]]*$' || true)
}

_alias_save() {
    mkdir -p "$(dirname "$ENV_FILE")"
    {
        printf '%s\n' "# AI aliases — managed by pos ai alias (do not hand-edit)"
        printf '%s\n' "# Format: alias_name|provider|session_name|system_prompt|trusted"
        printf '%s\n' "#"
        local i
        for ((i = 0; i < ${#_ALIAS_NAMES[@]}; i++)); do
            printf '%s|%s|%s|%s|%s\n' "${_ALIAS_NAMES[$i]}" "${_ALIAS_PROVIDERS[$i]}" \
                "${_ALIAS_SESSIONS[$i]}" "$(_alias_prompt_encode "${_ALIAS_PROMPTS[$i]}")" "${_ALIAS_TRUSTED[$i]:-0}"
        done
    } >"$ENV_FILE"
    chmod 600 "$ENV_FILE"
}

# Build the pos-ai command for an alias with the prompt safely quoted as
# ONE shell word (printf %q) — shared by regen (stored form) and show
# (copy-pasteable display form). Empty prompt → no --system fragment.
_alias_quote_cmd() {
    local provider="$1" session="$2" prompt="${3:-}" trusted="${4:-0}" q_prompt
    printf -v q_prompt '%q' "$prompt"
    printf 'pos ai %s ask --session %s' "$provider" "$session"
    [ "$trusted" -eq 1 ] && printf ' --trust'
    [ -n "$prompt" ] && printf ' --system %s' "$q_prompt"
    return 0
}

# ── Activation artifacts (Option B) ────────────────────────────
# ENV stays the single source of truth; each alias is materialized as an
# executable wrapper script at ~/.local/bin/<name>. Every invocation re-reads
# current bytes, so a stale snapshot (the old sourced-alias failure mode) is
# impossible by construction. No shell sourcing of any kind.

_wrapper_path() {
    printf '%s/.local/bin/%s' "$HOME" "$1"
}

# Ownership test: line 2 must carry our generator marker. Files failing this
# test are NEVER overwritten or deleted.
_alias_owned() {
    [ -f "$1" ] && sed -n '2p' "$1" 2>/dev/null | grep -q 'Managed by pos ai alias'
}

# Render one wrapper to stdout (args: name provider session prompt [trusted]).
# The exec line reuses _alias_quote_cmd's double-%q mechanics so the prompt
# lands as exactly ONE shell word; "$@" passes user args through.
_wrapper_render() {
    local name="$1" provider="$2" session="$3" prompt="${4:-}" trusted="${5:-0}"
    cat <<WRAPPER_EOF
#!/usr/bin/env bash
# Managed by pos ai alias — regenerated automatically; hand-edits are overwritten.
# Alias: ${name} | provider: ${provider} | session: ${session}
set -euo pipefail
exec $(_alias_quote_cmd "$provider" "$session" "$prompt" "$trusted") "\$@"
WRAPPER_EOF
}

# Atomically install/refresh one wrapper. Skips the write when the rendered
# content already matches (stable mtimes → sync idempotence is observable).
# Pre-commit validation: bash -n on the rendered file; failure keeps previous.
_wrapper_install() {   # name provider session prompt [trusted]
    local path="$(_wrapper_path "$1")" tmp
    tmp="$(mktemp "${HOME}/.local/bin/.pos-alias.XXXXXX")"
    _wrapper_render "$1" "$2" "$3" "$4" "${5:-0}" >"$tmp"
    if cmp -s "$tmp" "$path"; then
        rm -f "$tmp"
        return 0
    fi
    if ! bash -n "$tmp" 2>/dev/null; then
        warn "Wrapper for '$1' failed syntax check — keeping previous version" >&2
        rm -f "$tmp"
        return 1
    fi
    mv "$tmp" "$path"
    chmod 755 "$path"
}

# rc 0 iff ~/.local/bin is on PATH.
_alias_check_path() {
    case ":$PATH:" in
        *":$HOME/.local/bin:"*) return 0 ;;
        *) return 1 ;;
    esac
}

# Legacy ~/.config/.../ai-aliases.sh retirement: activation moved to wrapper
# scripts, and a stale sourced alias would shadow them (interactive bash gives
# aliases precedence over PATH lookups). Marker-guarded auto-remove only —
# foreign files are warned about and left untouched.
_alias_retire_legacy_sh() {
    [ -f "$SH_FILE" ] || return 0
    if ! head -n 3 "$SH_FILE" | grep -q 'Auto-generated by pos ai alias'; then
        warn "$SH_FILE was not generated by pos ai alias — left untouched; review manually"
        return 0
    fi
    local stale
    stale="$(sed -n 's/^alias \([A-Za-z_][A-Za-z0-9_-]*\)=.*/\1/p' "$SH_FILE" | tr '\n' ' ')"
    stale="${stale% }"
    rm -f "$SH_FILE"
    {
        echo "[!] Alias activation moved to executable scripts in ~/.local/bin/ — legacy file removed: $SH_FILE"
        [ -n "$stale" ] && echo "    Stale sourced aliases shadow the new scripts until cleaned — run: unalias $stale"
        echo "    (or simply start a new shell)"
    } >&2
    return 0
}

# Two-way reconciliation on EVERY invocation:
#   forward:  each ENV entry → render-diff-install (first-run migration,
#             create/edit/remove consistency, silent heal of hand-edited wrappers)
#   reverse:  owned wrappers whose name is not in ENV → deleted (covers remove,
#             manual ENV edits, and the empty-set case)
#   plus: legacy .sh retirement; PATH guidance when owned wrappers exist but
#   ~/.local/bin is absent from PATH (wrappers are written regardless).
_alias_sync() {
    _alias_load
    local bin_dir="${HOME}/.local/bin" i name f base match any=0
    mkdir -p "$bin_dir"
    for ((i = 0; i < ${#_ALIAS_NAMES[@]}; i++)); do
        _wrapper_install "${_ALIAS_NAMES[$i]}" "${_ALIAS_PROVIDERS[$i]}" \
            "${_ALIAS_SESSIONS[$i]}" "${_ALIAS_PROMPTS[$i]}" "${_ALIAS_TRUSTED[$i]:-0}" || :
    done
    for f in "$bin_dir"/*; do
        [ -f "$f" ] || continue
        _alias_owned "$f" || continue
        base="${f##*/}"
        match=0
        for name in ${_ALIAS_NAMES[@]+"${_ALIAS_NAMES[@]}"}; do
            [ "$base" = "$name" ] && { match=1; break; }
        done
        [ "$match" -eq 1 ] || rm -f "$f"
    done
    _alias_retire_legacy_sh
    if ! _alias_check_path; then
        for f in "$bin_dir"/*; do
            [ -f "$f" ] && _alias_owned "$f" && { any=1; break; }
        done
        if [ "$any" -eq 1 ]; then
            warn "~/.local/bin is not on your PATH — alias scripts will not resolve by name."
            warn "  Fix now:     export PATH=\"\$HOME/.local/bin:\$PATH\""
            warn "  Persist it:  echo 'export PATH=\"\$HOME/.local/bin:\$PATH\"' >> ~/.profile"
        fi
    fi
    return 0
}

_alias_provider_pick() {
    local providers=()
    for f in "$PROVIDER_DIR"/*.sh; do
        [ -f "$f" ] || continue
        providers+=("$(basename "$f" .sh)")
    done
    if [ ${#providers[@]} -eq 0 ]; then
        err "No AI providers installed — run 'pos ai' setup first"
    fi
    menu_pick "Pick provider" "${providers[@]}"
}

_alias_find() {
    local name="$1" i
    for ((i = 0; i < ${#_ALIAS_NAMES[@]}; i++)); do
        if [ "${_ALIAS_NAMES[$i]}" = "$name" ]; then
            echo "$i"
            return 0
        fi
    done
    echo "-1"
    return 0
}

_alias_name_valid() {
    [[ "$1" =~ ^[a-zA-Z][a-zA-Z0-9_-]*$ ]]
}

_alias_prompt_truncate() {
    local p="${1//$'\n'/\\n}" max="${2:-42}"
    if [ ${#p} -gt "$max" ]; then
        printf '%s…' "${p:0:max}"
    else
        printf '%s' "$p"
    fi
}

# ── Prompt encoding for the line-based env file ────────────────
# menu-lib's reader accepts literal multiline prompts; the ENV file is one
# record per line, so prompts are escaped on save and unescaped on load:
#   backslash → \\   newline → \n
# ('|' is already rejected at input, so | never needs escaping.)
_alias_prompt_encode() {
    local s="$1" out="" c="" i
    for ((i = 0; i < ${#s}; i++)); do
        c="${s:i:1}"
        if [ "$c" = '\' ]; then
            out+='\\'
        elif [ "$c" = $'\n' ]; then
            out+='\n'
        else
            out+="$c"
        fi
    done
    printf '%s' "$out"
}

# Decode is order-safe: \\n (escaped newline text) is backslash + n, which
# decodes to literal '\n' only after both escapes are consumed in order.
_alias_prompt_decode() {
    local s="$1" out="" c="" n="" i=0
    while [ "$i" -lt "${#s}" ]; do
        c="${s:i:1}"
        if [ "$c" = '\' ] && [ $((i + 1)) -lt "${#s}" ]; then
            n="${s:i+1:1}"
            if [ "$n" = 'n' ]; then
                out+=$'\n'; i=$((i + 2)); continue
            elif [ "$n" = '\' ]; then
                out+='\'; i=$((i + 2)); continue
            fi
        fi
        out+="$c"
        i=$((i + 1))
    done
    printf '%s' "$out"
}

# ── Non-interactive output ─────────────────────────────────────

# SINGLE alias-table renderer — used by `list` (stdout) and the menu
# pre-render (inside its stderr display block). One source of truth for the
# grid so the two contexts can never drift or duplicate each other.
_alias_table() {
    local count=${#_ALIAS_NAMES[@]} i
    [ "$count" -eq 0 ] && return 0
    printf '  %-12s %-12s %-12s %-5s %s\n' "Name" "Provider" "Session" "Trust" "Prompt"
    printf '  %-12s %-12s %-12s %-5s %s\n' "------------" "------------" "------------" "-----" \
        "------------------------------------------"
    for ((i = 0; i < count; i++)); do
        local _tmark="—"
        [ "${_ALIAS_TRUSTED[$i]:-0}" = "1" ] && _tmark="yes"
        printf '  %-12s %-12s %-12s %-5s %s\n' "${_ALIAS_NAMES[$i]}" "${_ALIAS_PROVIDERS[$i]}" \
            "${_ALIAS_SESSIONS[$i]}" "$_tmark" "$(_alias_prompt_truncate "${_ALIAS_PROMPTS[$i]}")"
    done
}

_alias_list() {
    printf 'Aliases (%d):\n' "${#_ALIAS_NAMES[@]}"
    _alias_table
}

_alias_show() {
    local idx
    idx="$(_alias_find "$1")"
    [ "$idx" = "-1" ] && err "Alias '$1' not found"
    local name="${_ALIAS_NAMES[$idx]}" provider="${_ALIAS_PROVIDERS[$idx]}"
    local session="${_ALIAS_SESSIONS[$idx]}" prompt="${_ALIAS_PROMPTS[$idx]}"
    local trusted="${_ALIAS_TRUSTED[$idx]:-0}"
    [ -z "$session" ] && session="$name"
    printf '  %-12s %s\n' "Alias:" "$name"
    printf '  %-12s %s\n' "Provider:" "$provider"
    printf '  %-12s %s\n' "Session:" "$session"
    if _alias_check_path; then
        printf '  %-12s %s\n' "Wrapper:" "$(_wrapper_path "$name")"
    else
        printf '  %-12s %s\n' "Wrapper:" "(not installed — ~/.local/bin not on PATH)"
    fi
    printf '  %-12s %s\n' "Prompt:" "${prompt:-$(printf '%s' "(default)")}"
    printf '  %-12s %s\n' "Trusted:" "$([ "$trusted" = "1" ] && echo "yes (auto-executes commands)" || echo "no (prompts before running)")"
    # Show the resolved command (same quoting mechanism as the generated
    # wrapper — what users copy from here pastes into a shell verbatim)
    printf '  %-12s %s\n' "Command:" "$(_alias_quote_cmd "$provider" "$session" "$prompt" "$trusted")"
}

# ── Interactive: main menu ─────────────────────────────────────

_alias_menu() {
    menu_guard || return 1
    while true; do
        {
            _alias_load
            if [ ${#_ALIAS_NAMES[@]} -eq 0 ]; then
                echo "${YELLOW}[!] No aliases defined yet — create one with option 1.${RESET}"
            else
                _alias_table
                printf '  %d alias(es)\n' "${#_ALIAS_NAMES[@]}"
            fi
            echo >&2
        } >&2
        local choice
        choice="$(menu_run "AI Agent Aliases" "Create new alias" "Edit existing alias" \
            "Remove alias" "List aliases")" || return 0
        case "$choice" in
            1) _alias_create ;;
            2) _alias_edit ;;
            3) _alias_remove ;;
            4) : ;;  # List aliases — the loop's pre-render above IS the current
                     # table (single renderer, redrawn fresh every iteration);
                     # option 4 returns to the loop for a fresh render instead
                     # of printing a second copy (dup-table bug fix).
        esac
    done
}

# ── Interactive: create ────────────────────────────────────────

_alias_create() {
    local preset_name="${1:-}"
    section "Create AI Agent Alias" >&2

    # Step 1: Alias name
    local name="$preset_name"
    while true; do
        if [ -z "$name" ]; then
            step 1 5 "Alias Name" >&2
            name="$(menu_ask_value --allow-empty "Alias name" "")" || return 0
        fi
        [ -z "$name" ] && { warn "Alias name cannot be empty" >&2; name=""; continue; }
        if ! _alias_name_valid "$name"; then
            warn "Invalid name '$name' — use letters, digits, hyphens, underscores (start with a letter)" >&2
            name=""; continue
        fi
        _alias_load
        local existing
        existing="$(_alias_find "$name")"
        if [ "$existing" != "-1" ]; then
            warn "Alias '$name' already exists — use 'pos ai alias edit $name' instead" >&2
            [ -n "$preset_name" ] && return 1
            name=""; continue
        fi
        # Collision refusals (never clobber foreign files or real binaries):
        #   1. wrapper exists WITH our marker → fine, sync regenerates it
        #   2. file exists WITHOUT marker → refuse
        #   3. name resolves to another binary on PATH → refuse, naming it
        local wpath
        wpath="$(_wrapper_path "$name")"
        if [ -e "$wpath" ]; then
            _alias_owned "$wpath" || err "File '~/.local/bin/$name' already exists and was not created by pos ai alias — pick another name"
        elif command -v "$name" >/dev/null 2>&1; then
            err "'$name' already exists on PATH as $(command -v "$name") — pick another name"
        fi
        break
    done

    # Step 2: Provider
    step 2 5 "Provider" >&2
    local pidx
    pidx="$(_alias_provider_pick)" || return 0
    local providers=()
    for f in "$PROVIDER_DIR"/*.sh; do
        [ -f "$f" ] || continue
        providers+=("$(basename "$f" .sh)")
    done
    local provider="${providers[$((pidx - 1))]}"

    # Step 3: Session name
    local session=""
    while true; do
        step 3 5 "Session Name" >&2
        session="$(menu_ask_value "Session name" "$name")" || return 0
        if [ -n "$session" ] && ! _alias_name_valid "$session"; then
            warn "Invalid session '$session' — use letters, digits, hyphens, underscores" >&2
            session=""; continue
        fi
        break
    done
    [ -z "$session" ] && session="$name"

    # Step 4: System prompt
    local prompt=""
    while true; do
        step 4 5 "System Prompt" >&2
        prompt="$(menu_ask_value --allow-empty "System prompt (empty = use built-in)" "")" || return 0
        if [[ "$prompt" == *'|'* ]]; then
            warn "System prompt must not contain '|' characters" >&2
            prompt=""; continue
        fi
        if [ ${#prompt} -gt 500 ]; then
            warn "Prompt is ${#prompt} chars — consider keeping it concise" >&2
        fi
        break
    done

    # Step 5: Trust level
    local trusted="0"
    while true; do
        step 5 5 "Trust Level" >&2
        {
            echo "  TRUSTED aliases auto-execute commands from the agent"
            echo "  WITHOUT asking for confirmation."
            echo ""
            echo "  Only enable this for aliases you fully trust with"
            echo "  unrestricted shell access on this machine."
        } >&2
        local trust_ans
        trust_ans="$(menu_ask_value "Trust this alias? (y/N)" "N")" || return 0
        case "${trust_ans,,}" in
            y|yes) trusted="1"; break ;;
            n|no|"") trusted="0"; break ;;
            *) warn "Please answer y or n" >&2 ;;
        esac
    done

    # Confirmation
    {
        echo "────────────────────────────────────────────"
        printf '  Create alias '\''%s'\''?\n' "$name"
        printf '    Provider:  %s\n' "$provider"
        printf '    Session:   %s\n' "$session"
        local dp="$(_alias_prompt_truncate "$prompt" 50)"
        printf '    Prompt:    %s\n' "${dp:-<built-in>}"
        printf '    Trusted:   %s\n' "$([ "$trusted" = "1" ] && echo "yes (auto-execute)" || echo "no (confirm before run)")"
        echo "────────────────────────────────────────────"
    } >&2
    if ! confirm "Create alias '$name'?" y; then
        log "Aborted." >&2
        return 0
    fi

    _alias_load
    _ALIAS_NAMES+=("$name")
    _ALIAS_PROVIDERS+=("$provider")
    _ALIAS_SESSIONS+=("$session")
    _ALIAS_PROMPTS+=("$prompt")
    _ALIAS_TRUSTED+=("$trusted")
    _alias_save
    _alias_sync
    log "Alias '$name' created." >&2
    log "Available immediately: $(_wrapper_path "$name")" >&2
}

# ── Interactive: edit ──────────────────────────────────────────

_alias_edit() {
    local preset_name="${1:-}"
    _alias_load
    if [ ${#_ALIAS_NAMES[@]} -eq 0 ]; then
        warn "No aliases to edit — create one first" >&2
        return 0
    fi

    local name="$preset_name"
    if [ -z "$name" ]; then
        section "Edit AI Agent Alias" >&2
        local display_items=() i
        for ((i = 0; i < ${#_ALIAS_NAMES[@]}; i++)); do
            local p="${_ALIAS_PROMPTS[$i]}"
            if [ ${#p} -gt 30 ]; then
                p="${p:0:30}…"
            fi
            display_items+=("${_ALIAS_NAMES[$i]} [${_ALIAS_PROVIDERS[$i]}] ${p}")
        done
        local picked
        picked="$(menu_pick "Pick alias to edit" "${display_items[@]}")" || return 0
        name="${_ALIAS_NAMES[$((picked - 1))]}"
    fi

    local idx
    idx="$(_alias_find "$name")"
    if [ "$idx" = "-1" ]; then
        err "Alias '$name' not found"
    fi

    # Show current values
    {
        echo "  Current values for '$name':"
        printf '    Provider:  %s\n' "${_ALIAS_PROVIDERS[$idx]}"
        printf '    Session:   %s\n' "${_ALIAS_SESSIONS[$idx]}"
        local cp="${_ALIAS_PROMPTS[$idx]}"
        [ -z "$cp" ] && cp="(default)"
        printf '    Prompt:    %s\n' "$cp"
        printf '    Trusted:   %s\n' "$([ "${_ALIAS_TRUSTED[$idx]:-0}" = "1" ] && echo "yes" || echo "no")"
        echo >&2
    } >&2

    local new_provider="${_ALIAS_PROVIDERS[$idx]}"
    local new_session="${_ALIAS_SESSIONS[$idx]}"
    local new_prompt="${_ALIAS_PROMPTS[$idx]}"
    local new_trusted="${_ALIAS_TRUSTED[$idx]:-0}"
    local changed=0

    # Edit provider
    step 1 4 "Provider" >&2
    local pidx
    pidx="$(_alias_provider_pick)" || return 0
    local providers=()
    for f in "$PROVIDER_DIR"/*.sh; do
        [ -f "$f" ] || continue
        providers+=("$(basename "$f" .sh)")
    done
    local picked_provider="${providers[$((pidx - 1))]}"
    if [ "$picked_provider" != "$new_provider" ]; then
        new_provider="$picked_provider"
        changed=1
    fi

    # Edit session
    local tmp_session=""
    while true; do
        step 2 4 "Session Name" >&2
        tmp_session="$(menu_ask_value "Session name" "$new_session")" || return 0
        if [ -n "$tmp_session" ] && ! _alias_name_valid "$tmp_session"; then
            warn "Invalid session '$tmp_session' — use letters, digits, hyphens, underscores" >&2
            tmp_session=""; continue
        fi
        break
    done
    [ -n "$tmp_session" ] && new_session="$tmp_session"
    [ "$new_session" != "${_ALIAS_SESSIONS[$idx]}" ] && changed=1

    # Edit prompt
    local tmp_prompt=""
    local default_prompt="${_ALIAS_PROMPTS[$idx]}"
    # Display-safe default (newlines → \n, truncated); Enter on it keeps the
    # FULL original prompt — a long/multiline prompt must never be silently
    # replaced by its truncated display form.
    local default_display="$(_alias_prompt_truncate "$default_prompt" 80)"
    while true; do
        step 3 4 "System Prompt" >&2
        if ! tmp_prompt="$(menu_ask_value "System prompt" "$default_display")"; then
            # EOF/cancel: empty-answer abort only when there IS a default;
            # Enter on an empty original prompt keeps it empty and continues.
            [ -z "$default_prompt" ] && tmp_prompt="" || return 0
        fi
        if [ "$tmp_prompt" = "$default_display" ]; then
            tmp_prompt="$default_prompt"      # Enter → keep the full original
        fi
        if [[ "$tmp_prompt" == *'|'* ]]; then
            warn "System prompt must not contain '|' characters" >&2
            tmp_prompt=""; continue
        fi
        if [ ${#tmp_prompt} -gt 500 ]; then
            warn "Prompt is ${#tmp_prompt} chars — consider keeping it concise" >&2
        fi
        break
    done
    # Keep full current if user pressed Enter (tmp_prompt = default_prompt value)
    if [ -n "$tmp_prompt" ]; then
        new_prompt="$tmp_prompt"
    fi
    [ "$new_prompt" != "${_ALIAS_PROMPTS[$idx]}" ] && changed=1

    # Edit trust
    step 4 4 "Trust Level" >&2
    local cur_trust_label="no"
    [ "$new_trusted" = "1" ] && cur_trust_label="yes"
    local trust_ans
    trust_ans="$(menu_ask_value "Trust this alias? (y/N)" "$cur_trust_label")" || return 0
    case "${trust_ans,,}" in
        y|yes) new_trusted="1" ;;
        n|no|"") new_trusted="$new_trusted" ;;
        *) warn "Please answer y or n" >&2 ;;
    esac
    [ "$new_trusted" != "${_ALIAS_TRUSTED[$idx]:-0}" ] && changed=1

    # No changes?
    if [ "$changed" -eq 0 ]; then
        log "No changes — nothing to save." >&2
        return 0
    fi

    # Show diff summary
    {
        echo "────────────────────────────────────────────"
        printf '  Save changes to '\''%s'\''?\n' "$name"
        local tag_p tag_s tag_pr tag_t
        [ "$new_provider" = "${_ALIAS_PROVIDERS[$idx]}" ] && tag_p="(unchanged)" || tag_p="(changed)"
        [ "$new_session" = "${_ALIAS_SESSIONS[$idx]}" ] && tag_s="(unchanged)" || tag_s="(changed)"
        [ "$new_prompt" = "${_ALIAS_PROMPTS[$idx]}" ] && tag_pr="(unchanged)" || tag_pr="(changed)"
        [ "$new_trusted" = "${_ALIAS_TRUSTED[$idx]:-0}" ] && tag_t="(unchanged)" || tag_t="(changed)"
        printf '    Provider:  %-12s %s\n' "$new_provider" "$tag_p"
        printf '    Session:   %-12s %s\n' "$new_session" "$tag_s"
        local dp="$(_alias_prompt_truncate "$new_prompt" 40)"
        [ -z "$dp" ] && dp="<built-in>"
        printf '    Prompt:    %s %s\n' "$dp" "$tag_pr"
        printf '    Trusted:   %-12s %s\n' "$([ "$new_trusted" = "1" ] && echo "yes" || echo "no")" "$tag_t"
        echo "────────────────────────────────────────────"
    } >&2
    if ! confirm "Save changes to '$name'?" y; then
        log "Discarded." >&2
        return 0
    fi

    _alias_load
    _ALIAS_PROVIDERS[$idx]="$new_provider"
    _ALIAS_SESSIONS[$idx]="$new_session"
    _ALIAS_PROMPTS[$idx]="$new_prompt"
    _ALIAS_TRUSTED[$idx]="$new_trusted"
    _alias_save
    _alias_sync
    log "Alias '$name' updated — the change is live on next invocation." >&2
}

# ── Interactive: remove ────────────────────────────────────────

_alias_remove() {
    local preset_name="${1:-}"
    _alias_load
    if [ ${#_ALIAS_NAMES[@]} -eq 0 ]; then
        warn "No aliases to remove" >&2
        return 0
    fi

    local name="$preset_name"
    if [ -z "$name" ]; then
        section "Remove AI Agent Alias" >&2
        local display_items=() i
        for ((i = 0; i < ${#_ALIAS_NAMES[@]}; i++)); do
            local p="${_ALIAS_PROMPTS[$i]}"
            if [ ${#p} -gt 30 ]; then
                p="${p:0:30}…"
            fi
            display_items+=("${_ALIAS_NAMES[$i]} [${_ALIAS_PROVIDERS[$i]}] ${p}")
        done
        local picked
        picked="$(menu_pick "Pick alias to remove" "${display_items[@]}")" || return 0
        name="${_ALIAS_NAMES[$((picked - 1))]}"
    fi

    local idx
    idx="$(_alias_find "$name")"
    if [ "$idx" = "-1" ]; then
        err "Alias '$name' not found"
    fi

    # Show alias detail
    {
        echo "  Alias:    $name"
        printf '  Provider: %s\n' "${_ALIAS_PROVIDERS[$idx]}"
        printf '  Session:  %s\n' "${_ALIAS_SESSIONS[$idx]}"
        printf '  Prompt:   %s\n' "${_ALIAS_PROMPTS[$idx]:-<built-in>}"
        echo >&2
    } >&2

    if ! confirm "Remove alias '$name'? This cannot be undone." n; then
        log "Cancelled." >&2
        return 0
    fi

    _alias_load
    local new_names=() new_providers=() new_sessions=() new_prompts=() i
    for ((i = 0; i < ${#_ALIAS_NAMES[@]}; i++)); do
        if [ "${_ALIAS_NAMES[$i]}" != "$name" ]; then
            new_names+=("${_ALIAS_NAMES[$i]}")
            new_providers+=("${_ALIAS_PROVIDERS[$i]}")
            new_sessions+=("${_ALIAS_SESSIONS[$i]}")
            new_prompts+=("${_ALIAS_PROMPTS[$i]}")
        fi
    done
    _ALIAS_NAMES=("${new_names[@]+"${new_names[@]}"}")
    _ALIAS_PROVIDERS=("${new_providers[@]+"${new_providers[@]}"}")
    _ALIAS_SESSIONS=("${new_sessions[@]+"${new_sessions[@]}"}")
    _ALIAS_PROMPTS=("${new_prompts[@]+"${new_prompts[@]}"}")
    _alias_save
    _alias_sync
    log "Alias '$name' removed — script deleted from $(_wrapper_path "$name")." >&2
    log "If the name still autocompletes stale in this shell, run: hash -r" >&2
}

# ── show <name> ───────────────────────────────────────────────

# (defined above as _alias_show)

# ── Usage ──────────────────────────────────────────────────────

usage() {
    cat <<'EOF'
Usage: pos ai alias [subcommand] [args]

Manage named AI agent aliases — create, edit, remove, list, and show
configured aliases. Each alias maps a name to a provider, session,
optional system prompt, and a trust level.

Trusted aliases auto-execute the agent's commands without asking for
confirmation. Only enable for aliases you fully trust with shell access.

Subcommands:
  (no args)         Interactive menu
  create [name]     Create a new alias (interactive prompts for each field)
  edit [name]       Edit an existing alias (interactive, Enter = keep)
  remove [name]     Remove an alias (interactive, default = no)
  list              List all aliases (non-interactive, machine-readable)
  show <name>       Show one alias's details

Activation: every alias is materialized as an executable script at
~/.local/bin/<name>, synced automatically on every invocation — no shell
sourcing required. Changes are live on the next invocation, and the
scripts work identically in interactive shells, scripts, cron, and
non-login ssh sessions.

Options:
  -h|--help         Show this help.

Examples:
  pos ai alias                     # interactive menu
  pos ai alias list                # show all aliases
  pos ai alias create              # interactive create
  pos ai alias create mybot        # create 'mybot' alias
  pos ai alias edit mybot          # edit the 'mybot' alias
  pos ai alias remove mybot        # remove 'mybot' (with confirm)
  pos ai alias show mybot          # show alias details
EOF
    exit 0
}

# ── Main dispatch ──────────────────────────────────────────────
# Every subcommand syncs first: artifacts always equal ENV truth before any
# subcommand logic runs (migration, healing, retraction — all automatic).

case "${1:-}" in
    -h|--help) usage ;;
    create)  shift; _alias_sync; _alias_create "${1:-}" ;;
    edit)    shift; _alias_sync; _alias_edit "${1:-}" ;;
    remove)  shift; _alias_sync; _alias_remove "${1:-}" ;;
    list)    _alias_sync; _alias_list ;;
    show)
        [ -n "${2:-}" ] || err "Usage: pos ai alias show <name>"
        _alias_sync
        _alias_show "$2"
        ;;
    "") _alias_sync; _alias_menu ;;
    *)  err "Unknown subcommand '$1' (use -h for help)" ;;
esac
