#!/bin/bash

# filem - Unified save tool for AGENTS.md, plugins, skills, and subagents
# Usage: filem
# Interactive 4-column selection mode for saving files

set -e

STORAGE_DIR="$HOME/00_central/volatile"
TYPE_DESC_FILE="$STORAGE_DIR/.type_descriptions"
PLUGINS_DIR=".opencode/plugins"
SKILLS_DIR=".opencode/skills"
AGENTS_DIR=".opencode/agents"
ASKILLS_DIR=".agents/skills"
CURRENT_DIR="$(pwd)"
TRACKABLE_MD_NAMES=(AGENTS.md CLAUDE.md GEMINI.md)

# Check dependencies
compare_files() {
    local f1="$1"
    local f2="$2"
    if command -v icdiff >/dev/null 2>&1; then
        icdiff "$f1" "$f2" -t --show-no-spaces --no-bold | grep -ve "^\s*$" || true
    else
        diff -u "$f1" "$f2" || true
    fi
}

normalize_type_category() {
    local cat="$1"
    local lowered="${cat,,}"
    lowered="${lowered%.md}"
    lowered="${lowered// /}"
    case "$lowered" in
        agent|agents) echo "AGENTS.md" ;;
        plugin|plugins) echo "PLUGIN" ;;
        skill|skills) echo "SKILL" ;;
        subagent|subagents) echo "SUBAGENT" ;;
        *) echo "$cat" ;;
    esac
}

load_type_descriptions() {
    TYPE_DESCRIPTIONS=()
    if [[ -f "$TYPE_DESC_FILE" ]]; then
        while IFS='|' read -r cat type_name desc; do
            cat="$(normalize_type_category "$cat")"
            TYPE_DESCRIPTIONS["${cat}:${type_name}"]="$desc"
        done < "$TYPE_DESC_FILE"
    fi
}

save_type_description() {
    local category="$1"
    local type_name="$2"
    local description="$3"
    local tmp_file
    tmp_file=$(mktemp)
    if [[ -f "$TYPE_DESC_FILE" ]]; then
        grep -v "^${category}|${type_name}|" "$TYPE_DESC_FILE" > "$tmp_file" 2>/dev/null || true
    else
        : > "$tmp_file"
    fi
    echo "${category}|${type_name}|${description}" >> "$tmp_file"
    mv "$tmp_file" "$TYPE_DESC_FILE"
}

ensure_type_description() {
    local category="$1"
    local type_name="$2"
    local label="$3"

    if [[ -n "${TYPE_DESCRIPTIONS["${category}:${type_name}"]}" ]]; then
        return 0
    fi

    local description=""
    echo -en "\033[33mEnter description for ${label} '${type_name}': \033[0m" >&2
    read -r description
    if [[ -z "$description" ]]; then
        echo -e "\033[31mDescription is required\033[0m" >&2
        return 1
    fi

    save_type_description "$category" "$type_name" "$description"
    TYPE_DESCRIPTIONS["${category}:${type_name}"]="$description"
}

# Ensure storage exists
mkdir -p "$STORAGE_DIR"

# --- Data Gathering ---

# Column 1: Trackable .md files (AGENTS.md, CLAUDE.md, etc.)
get_trackable_md_files() {
    local result=()
    local md_file

    for md_file in "${TRACKABLE_MD_NAMES[@]}"; do
        if [[ -f "$CURRENT_DIR/$md_file" ]]; then
            result+=("$md_file")
            continue
        fi

        if ls "$STORAGE_DIR"/*_"$md_file" >/dev/null 2>&1; then
            result+=("$md_file")
        fi
    done

    printf '%s\n' "${result[@]}"
}

# Column 2: Available plugins
get_available_plugins() {
    local result=()
    if [[ -d "$PLUGINS_DIR" ]]; then
        while IFS= read -r -d '' file; do
            result+=("$(basename "$file")")
        done < <(find "$PLUGINS_DIR" -maxdepth 1 -type f \
            ! -name "*~" \
            ! -name "#*#" \
            ! -name ".#*" \
            -print0 | sort -z)
    fi
    printf '%s\n' "${result[@]}"
}

# Column 3: Available skills (from both .opencode/skills and .agents/skills)
# SKILL_SOURCES maps skill_name -> "opencode"|"agents"|"both"
init_available_skills() {
    AVAILABLE_SKILLS=()
    SKILL_SOURCES=()
    SKILL_OC_PATH=()
    SKILL_OC_KIND=()
    SKILL_AG_PATH=()
    SKILL_AG_KIND=()
    local -A seen_skills=()

    if [[ -d "$SKILLS_DIR" ]]; then
        for dir in "$SKILLS_DIR"/*; do
            if [[ -d "$dir" && -f "$dir/SKILL.md" ]]; then
                local name
                name="$(basename "$dir")"
                if [[ -z "${seen_skills[$name]}" ]]; then
                    seen_skills["$name"]=1
                    AVAILABLE_SKILLS+=("$name")
                fi
                SKILL_OC_PATH["$name"]="$dir"
                SKILL_OC_KIND["$name"]="dir"
            elif [[ -f "$dir" && "$dir" == *.md ]]; then
                local name
                name="$(basename "$dir" .md)"
                if [[ -z "${seen_skills[$name]}" ]]; then
                    seen_skills["$name"]=1
                    AVAILABLE_SKILLS+=("$name")
                fi
                SKILL_OC_PATH["$name"]="$dir"
                SKILL_OC_KIND["$name"]="file"
            fi
        done
    fi

    if [[ -d "$ASKILLS_DIR" ]]; then
        for dir in "$ASKILLS_DIR"/*; do
            if [[ -d "$dir" && -f "$dir/SKILL.md" ]]; then
                local name
                name="$(basename "$dir")"
                if [[ -z "${seen_skills[$name]}" ]]; then
                    seen_skills["$name"]=1
                    AVAILABLE_SKILLS+=("$name")
                fi
                SKILL_AG_PATH["$name"]="$dir"
                SKILL_AG_KIND["$name"]="dir"
            elif [[ -f "$dir" && "$dir" == *.md ]]; then
                local name
                name="$(basename "$dir" .md)"
                if [[ -z "${seen_skills[$name]}" ]]; then
                    seen_skills["$name"]=1
                    AVAILABLE_SKILLS+=("$name")
                fi
                SKILL_AG_PATH["$name"]="$dir"
                SKILL_AG_KIND["$name"]="file"
            fi
        done
    fi

    local name
    for name in "${AVAILABLE_SKILLS[@]}"; do
        if [[ -n "${SKILL_OC_PATH[$name]}" && -n "${SKILL_AG_PATH[$name]}" ]]; then
            SKILL_SOURCES["$name"]="both"
        elif [[ -n "${SKILL_OC_PATH[$name]}" ]]; then
            SKILL_SOURCES["$name"]="opencode"
        elif [[ -n "${SKILL_AG_PATH[$name]}" ]]; then
            SKILL_SOURCES["$name"]="agents"
        fi
    done
}

# Column 4: Available subagents
get_available_subagents() {
    local result=()
    if [[ -d "$AGENTS_DIR" ]]; then
        while IFS= read -r -d '' file; do
            local basename=$(basename "$file")
            # Remove .md extension for display
            local name="${basename%.md}"
            result+=("$name")
        done < <(find "$AGENTS_DIR" -maxdepth 1 -type f -name "*.md" \
            ! -name "*~" \
            ! -name "#*#" \
            ! -name ".#*" \
            -print0 | sort -z)
    fi
    printf '%s\n' "${result[@]}"
}

# Get existing types for a given MD file
get_types_for_md_file() {
    local md_file="$1"
    ls "$STORAGE_DIR" 2>/dev/null | grep "_${md_file}$" | sed -E 's/.*_([^_]+)_.*/\1/' | sort -u
}

# Selection states
SELECTED_MD_FILE=""
declare -A SELECTED_PLUGINS
declare -A SELECTED_SKILLS
declare -A SELECTED_SUBAGENTS
declare -A SKILL_SOURCES
declare -A SKILL_OC_PATH
declare -A SKILL_OC_KIND
declare -A SKILL_AG_PATH
declare -A SKILL_AG_KIND
declare -A TYPE_DESCRIPTIONS

MD_FILES=($(get_trackable_md_files))
AVAILABLE_PLUGINS=($(get_available_plugins))
AVAILABLE_SKILLS=()
init_available_skills
AVAILABLE_SUBAGENTS=($(get_available_subagents))

# Index-to-display: 1..9, 11..N (skip 10)
_idx2disp() { local i=$1; if [[ $i -lt 9 ]]; then echo $((i+1)); else echo $((i+2)); fi; }
# Display-to-index: inverse
_disp2idx() { local n=$1; if [[ $n -le 9 ]]; then echo $((n-1)); else echo $((n-2)); fi; }
ALL_LETTERS=(a b c d e f g h i j k l m n o p r s t u v w x y z)

PLUGIN_KEYS=()
SKILL_KEYS=()
SUBAGENT_KEYS=()
_ki=0
for ((i=0; i<${#AVAILABLE_PLUGINS[@]}; i++)); do
    PLUGIN_KEYS+=("${ALL_LETTERS[$_ki]}")
    _ki=$((_ki+1))
done
for ((i=0; i<${#AVAILABLE_SKILLS[@]}; i++)); do
    SKILL_KEYS+=("${ALL_LETTERS[$_ki]}")
    _ki=$((_ki+1))
done
for ((i=0; i<${#AVAILABLE_SUBAGENTS[@]}; i++)); do
    SUBAGENT_KEYS+=("${ALL_LETTERS[$_ki]}")
    _ki=$((_ki+1))
done
key_category() {
    local key="$1"
    local i
    for i in "${!PLUGIN_KEYS[@]}"; do
        if [[ "${PLUGIN_KEYS[$i]}" == "$key" ]]; then
            echo "plugin"
            return 0
        fi
    done
    for i in "${!SKILL_KEYS[@]}"; do
        if [[ "${SKILL_KEYS[$i]}" == "$key" ]]; then
            echo "skill"
            return 0
        fi
    done
    for i in "${!SUBAGENT_KEYS[@]}"; do
        if [[ "${SUBAGENT_KEYS[$i]}" == "$key" ]]; then
            echo "subagent"
            return 0
        fi
    done
    return 1
}

key_range() {
    local -n arr=$1
    if [[ ${#arr[@]} -eq 0 ]]; then
        echo "-"
        return
    fi
    if [[ ${#arr[@]} -eq 1 ]]; then
        echo "${arr[0]}"
        return
    fi
    echo "${arr[0]}-${arr[-1]}"
}

key_for_index() {
    local prefix="$1"
    local idx="$2"
    case "$prefix" in
        plugin) echo "${PLUGIN_KEYS[$idx]}" ;;
        skill) echo "${SKILL_KEYS[$idx]}" ;;
        subagent) echo "${SUBAGENT_KEYS[$idx]}" ;;
    esac
}

index_for_key() {
    local prefix="$1"
    local key="$2"
    local -n keys_ref="$3"
    local i
    for i in "${!keys_ref[@]}"; do
        if [[ "${keys_ref[$i]}" == "$key" ]]; then
            echo "$i"
            return 0
        fi
    done
    return 1
}

# --- UI Functions ---

draw_menu() {
    clear
    echo -e "\033[33m=== filem - Unified Save Tool ===\033[0m"
    echo ""
    printf "%-25s %-25s %-25s %-25s\n" "COL 1: MD FILES" "COL 2: PLUGINS" "COL 3: SKILLS" "COL 4: SUBAGENTS"
    printf "%-25s %-25s %-25s %-25s\n" "---------------" "--------------" "-------------" "----------------"

    local max_rows=${#MD_FILES[@]}
    [[ ${#AVAILABLE_PLUGINS[@]} -gt $max_rows ]] && max_rows=${#AVAILABLE_PLUGINS[@]}
    [[ ${#AVAILABLE_SKILLS[@]} -gt $max_rows ]] && max_rows=${#AVAILABLE_SKILLS[@]}
    [[ ${#AVAILABLE_SUBAGENTS[@]} -gt $max_rows ]] && max_rows=${#AVAILABLE_SUBAGENTS[@]}

    for ((i=0; i<max_rows; i++)); do
        # Column 1: MD Files (numbered keys, skip 10)
        local col1_text=""
        if [[ $i -lt ${#MD_FILES[@]} ]]; then
            local file="${MD_FILES[$i]}"
            local key=$(_idx2disp $i)
            local mark="[ ]"
            [[ "$SELECTED_MD_FILE" == "$file" ]] && mark="[x]"
            col1_text="${key}) $mark $file"
        fi

        # Column 2: Plugins (keys a-i)
        local col2_text=""
        if [[ $i -lt ${#AVAILABLE_PLUGINS[@]} ]]; then
            local plugin="${AVAILABLE_PLUGINS[$i]}"
            local key="$(key_for_index plugin "$i")"
            local mark="[ ]"
            [[ "${SELECTED_PLUGINS[$plugin]}" == "1" ]] && mark="[x]"
            # Truncate long plugin names
            local display_name="$plugin"
            if [[ ${#display_name} -gt 18 ]]; then
                display_name="${display_name:0:15}..."
            fi
            col2_text="${key}) $mark $display_name"
        fi

        # Column 3: Skills (keys j-r)
        local col3_text=""
        if [[ $i -lt ${#AVAILABLE_SKILLS[@]} ]]; then
            local skill="${AVAILABLE_SKILLS[$i]}"
            local key="$(key_for_index skill "$i")"
            local mark="[ ]"
            [[ "${SELECTED_SKILLS[$skill]}" == "1" ]] && mark="[x]"
            local source_tag=""
            case "${SKILL_SOURCES[$skill]}" in
                "agents") source_tag="@" ;;
                "opencode"|"both") source_tag="*" ;;
            esac
            col3_text="${key}) $mark ${skill}${source_tag}"
        fi

        # Column 4: Subagents (keys s-z)
        local col4_text=""
        if [[ $i -lt ${#AVAILABLE_SUBAGENTS[@]} ]]; then
            local subagent="${AVAILABLE_SUBAGENTS[$i]}"
            local key="$(key_for_index subagent "$i")"
            local mark="[ ]"
            [[ "${SELECTED_SUBAGENTS[$subagent]}" == "1" ]] && mark="[x]"
            col4_text="${key}) $mark $subagent"
        fi

        printf "%-25s %-25s %-25s %-25s\n" "$col1_text" "$col2_text" "$col3_text" "$col4_text"
    done

    echo ""
    echo -e "\033[33m[number]: MD file | [$(key_range PLUGIN_KEYS)]: Plugins | [$(key_range SKILL_KEYS)]: Skills | [$(key_range SUBAGENT_KEYS)]: Subagents | [ENTER]: Save | [q]: Quit\033[0m"
    echo -e "\033[2mSkills: @ .agents | * .opencode\033[0m"
}

# Prompt for type selection when saving MD file
# Global variable to hold the selected type
SELECTED_MD_TYPE=""

prompt_for_md_type() {
    local md_file="$1"
    SELECTED_MD_TYPE=""
    
    local types=($(get_types_for_md_file "$md_file"))
    
    if [[ ${#types[@]} -eq 0 ]]; then
        # No existing types, ask for new one
        echo "" >&2
        echo -e "\033[7m\033[33m=== WAITING FOR INPUT ===\033[0m" >&2
        echo -e "\033[33mFile: $md_file\033[0m" >&2
        echo -en "\033[33mEnter type tag: \033[0m" >&2
        read TYPE
        if [[ -z "$TYPE" ]]; then
            echo -e "\033[31mInvalid type\033[0m" >&2
            return 1
        fi
        local NEW_DESC=""
        echo -en "\033[33mEnter description: \033[0m" >&2
        read NEW_DESC
        if [[ -z "$NEW_DESC" ]]; then
            echo -e "\033[31mDescription is required\033[0m" >&2
            return 1
        fi
        save_type_description "$md_file" "$TYPE" "$NEW_DESC"
        SELECTED_MD_TYPE="$TYPE"
        return 0
    fi
    
    echo "" >&2
    echo -e "\033[7m\033[33m=== WAITING FOR INPUT ===\033[0m" >&2
    echo -e "\033[33mFile: $md_file\033[0m" >&2
    echo "Select type:" >&2
    echo "---" >&2
    for i in "${!types[@]}"; do
        local type="${types[$i]}"
        local desc="${TYPE_DESCRIPTIONS["${md_file}:${type}"]}"
        local dnum=$(_idx2disp "$i")
        if [[ -n "$desc" ]]; then
            printf "%2s) \033[32m%-20s\033[0m  \033[2m%s\033[0m\n" "$dnum" "$type" "$desc" >&2
        else
            printf "%2s) \033[32m%s\033[0m\n" "$dnum" "$type" >&2
        fi
    done
    echo "---" >&2
    echo -e "n) \033[32mnew type\033[0m" >&2
    local maxdisp=$(_idx2disp $((${#types[@]}-1)))
    echo -en "\033[33mSelection (1-${maxdisp}, n for new type, q to quit): \033[0m" >&2
    # Clear any pending input first
    while IFS= read -r -t 0; do : ; done 2>/dev/null || true
    read -r REPLY
    
    if [[ "$REPLY" =~ ^[qQ]$ ]]; then
        return 1
    fi
    
    if [[ "$REPLY" =~ ^[nN]$ ]]; then
        echo -en "\033[33mEnter new type: \033[0m" >&2
        read TYPE
        if [[ -z "$TYPE" ]]; then
            echo -e "\033[31mInvalid type\033[0m" >&2
            return 1
        fi
        local NEW_DESC=""
        echo -en "\033[33mEnter description: \033[0m" >&2
        read NEW_DESC
        if [[ -z "$NEW_DESC" ]]; then
            echo -e "\033[31mDescription is required\033[0m" >&2
            return 1
        fi
        save_type_description "$md_file" "$TYPE" "$NEW_DESC"
        SELECTED_MD_TYPE="$TYPE"
        return 0
    elif [[ "$REPLY" =~ ^[0-9]+$ ]] && [[ "$REPLY" -ne 10 ]]; then
        local sel_idx=$(_disp2idx "$REPLY")
        if [[ $sel_idx -ge 0 ]] && [[ $sel_idx -lt ${#types[@]} ]]; then
            SELECTED_MD_TYPE="${types[$sel_idx]}"
            return 0
        fi
    else
        echo -e "\033[31mInvalid selection\033[0m" >&2
        return 1
    fi
}

# Prompt for plugin type tag (auto-uses default)
prompt_for_plugin_type() {
    local plugin_name="$1"
    local basename="${plugin_name%.*}"

    echo -e "\033[33mUsing default type tag: $basename\033[0m" >&2
    echo "$basename"
}

# Prompt for skill type tag (auto-uses default)
prompt_for_skill_type() {
    local skill_name="$1"

    echo -e "\033[33mUsing default type tag: $skill_name\033[0m" >&2
    echo "$skill_name"
}



# --- Save Logic ---

save_md_file() {
    local md_file="$1"
    
    if [[ ! -f "$md_file" ]]; then
        echo -e "\033[31mFile not found: $md_file\033[0m"
        return 1
    fi
    
    prompt_for_md_type "$md_file"
    if [[ $? -ne 0 ]]; then
        echo -e "\033[31mAborted saving $md_file\033[0m"
        return 1
    fi
    
    local type="$SELECTED_MD_TYPE"
    
    echo -e "\n\033[34m>>> Saving $md_file (type: $type)\033[0m"
    
    local latest_file=$(ls -t "$STORAGE_DIR"/*_"${type}_${md_file}" 2>/dev/null | head -1)
    
    if [[ -n "$latest_file" ]]; then
        echo "Comparing with latest version:"
        echo "---"
        
        if cmp -s "$latest_file" "$md_file"; then
            echo -e "\033[33mNo changes detected in $md_file\033[0m"
            echo -e "\033[32mSkipping save (file unchanged)\033[0m"
            return 0
        else
            compare_files "$latest_file" "$md_file"
            echo "---"
            echo -n -e "\033[33mSave new version? (y/N) \033[0m"
            read -n 1 -r
            echo
            if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then
                echo -e "\033[31mSkipped saving $md_file\033[0m"
                return 0
            fi
        fi
    fi
    
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local new_file="${STORAGE_DIR}/${timestamp}_${type}_${md_file}"
    cp "$md_file" "$new_file"
    echo -e "\033[32mSaved to: $new_file\033[0m"
}

save_plugin() {
    local plugin_name="$1"
    local plugin_path="$PLUGINS_DIR/$plugin_name"
    
    if [[ ! -f "$plugin_path" ]]; then
        echo -e "\033[31mPlugin not found: $plugin_path\033[0m"
        return 1
    fi
    
    # Get extension
    local ext="${plugin_name##*.}"
    if [[ "$ext" == "$plugin_name" ]]; then
        ext=""
    fi
    local basename="${plugin_name%.*}"
    
    echo ""
    local type_tag
    type_tag=$(prompt_for_plugin_type "$plugin_name")
    ensure_type_description "PLUGIN" "$type_tag" "plugin" || return 1
    
    echo -e "\n\033[34m>>> Saving plugin $plugin_name (type: $type_tag)\033[0m"
    
    local latest_file
    if [[ -n "$ext" ]]; then
        latest_file=$(ls -t "$STORAGE_DIR"/*_"${type_tag}_PLUGIN.${ext}" 2>/dev/null | head -1)
    else
        latest_file=$(ls -t "$STORAGE_DIR"/*_"${type_tag}_PLUGIN" 2>/dev/null | head -1)
    fi
    
    if [[ -n "$latest_file" ]]; then
        if diff -q "$latest_file" "$plugin_path" > /dev/null; then
            echo -e "\033[33mNo changes. Skipping save.\033[0m"
            return 0
        else
            echo "---"
            echo -e "\033[33mPlugin differs from latest saved version.\033[0m"
            echo ""
            compare_files "$latest_file" "$plugin_path"
            echo ""
            echo -n -e "Press \033[33mEnter\033[0m to save, Ctrl+C to abort: "
            read -r
        fi
    fi
    
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local new_file
    if [[ -n "$ext" ]]; then
        new_file="${STORAGE_DIR}/${timestamp}_${type_tag}_PLUGIN.${ext}"
    else
        new_file="${STORAGE_DIR}/${timestamp}_${type_tag}_PLUGIN"
    fi
    
    cp "$plugin_path" "$new_file"
    echo -e "\033[32mSaved plugin to: $new_file\033[0m"
}

save_skill() {
    local skill_name="$1"
    local skill_dir="${SKILL_OC_PATH[$skill_name]}"
    local skill_kind="${SKILL_OC_KIND[$skill_name]}"
    local askill_dir="${SKILL_AG_PATH[$skill_name]}"
    local askill_kind="${SKILL_AG_KIND[$skill_name]}"
    local source="${SKILL_SOURCES[$skill_name]}"

    if [[ "$source" != "opencode" ]] && [[ "$source" != "agents" ]] && [[ "$source" != "both" ]]; then
        echo -e "\033[31mSkill directory not found: $skill_dir or $askill_dir\033[0m"
        return 1
    fi

    echo ""
    local type_tag
    type_tag=$(prompt_for_skill_type "$skill_name")
    ensure_type_description "SKILL" "$type_tag" "skill" || return 1

    local source_label="$source"
    [[ "$source" == "opencode" ]] && source_label=".opencode"
    [[ "$source" == "agents" ]] && source_label=".agents"
    [[ "$source" == "both" ]] && source_label=".opencode+.agents"
    echo -e "\n\033[34m>>> Saving skill $skill_name (type: $type_tag) [$source_label]\033[0m"

    local new_file="${STORAGE_DIR}/$(date +%Y%m%d_%H%M%S)_${type_tag}_SKILL.tar.gz"
    local latest_file=$(ls -t "$STORAGE_DIR"/*_"${type_tag}_SKILL.tar.gz" 2>/dev/null | head -1)

    local has_opencode=0
    local has_agents=0
    [[ -n "$skill_dir" ]] && has_opencode=1
    [[ -n "$askill_dir" ]] && has_agents=1

    if [[ -n "$latest_file" ]]; then
        local temp_dir=$(mktemp -d)
        tar -xzf "$latest_file" -C "$temp_dir"

        local need_save=0
        local diff_parts=""

        if [[ $has_opencode -eq 1 ]]; then
            local archive_oc
            if [[ "$skill_kind" == "dir" ]]; then
                archive_oc="$temp_dir/$skill_name"
            else
                archive_oc="$temp_dir/${skill_name}.md"
            fi

            if [[ -e "$archive_oc" ]]; then
                local d
                if [[ "$skill_kind" == "dir" ]]; then
                    d=$(diff -rq \
                        --exclude="*undo-tree~" \
                        --exclude="__pycache__" \
                        --exclude="*~" \
                        --exclude="*.pyc" \
                        --exclude="*.pyo" \
                        "$archive_oc" "$skill_dir" 2>&1 || true)
                else
                    d=$(diff -u "$archive_oc" "$skill_dir" 2>&1 || true)
                fi
                if [[ -n "$d" ]]; then
                    diff_parts+="$d"$'\n'
                    need_save=1
                fi
            else
                diff_parts+="[.opencode] New skill directory"$'\n'
                need_save=1
            fi
        fi

        if [[ $has_agents -eq 1 ]]; then
            local archive_ag
            if [[ "$askill_kind" == "dir" ]]; then
                archive_ag="$temp_dir/.agents/skills/$skill_name"
            else
                archive_ag="$temp_dir/.agents/skills/${skill_name}.md"
            fi

            if [[ -e "$archive_ag" ]]; then
                local d
                if [[ "$askill_kind" == "dir" ]]; then
                    d=$(diff -rq \
                        --exclude="*undo-tree~" \
                        --exclude="__pycache__" \
                        --exclude="*~" \
                        --exclude="*.pyc" \
                        --exclude="*.pyo" \
                        "$archive_ag" "$askill_dir" 2>&1 || true)
                else
                    d=$(diff -u "$archive_ag" "$askill_dir" 2>&1 || true)
                fi
                if [[ -n "$d" ]]; then
                    diff_parts+="$d"$'\n'
                    need_save=1
                fi
            else
                diff_parts+="[.agents] New skill directory"$'\n'
                need_save=1
            fi
        fi

        rm -rf "$temp_dir"

        if [[ $need_save -eq 1 ]]; then
            echo "---"
            echo -e "\033[33mFiles that differ:\033[0m"
            echo "$diff_parts" | sed "s|$skill_dir/||g; s|$askill_dir/||g"
            echo ""
            echo -n -e "Press \033[33mEnter\033[0m to save, Ctrl+C to abort: "
            read -r
        else
            echo -e "\033[33mNo changes. Skipping save.\033[0m"
            return 0
        fi
    fi

    # Create staging directory for archive
    local stage_dir=$(mktemp -d)

    if [[ $has_opencode -eq 1 ]]; then
        if [[ "$skill_kind" == "dir" ]]; then
            cp -a "$skill_dir" "$stage_dir/$skill_name"
        else
            cp -a "$skill_dir" "$stage_dir/${skill_name}.md"
        fi
    fi

    if [[ $has_agents -eq 1 ]]; then
        mkdir -p "$stage_dir/.agents/skills"
        if [[ "$askill_kind" == "dir" ]]; then
            cp -a "$askill_dir" "$stage_dir/.agents/skills/$skill_name"
        else
            cp -a "$askill_dir" "$stage_dir/.agents/skills/${skill_name}.md"
        fi
    fi

    local tar_items=()
    if [[ $has_opencode -eq 1 ]]; then
        if [[ "$skill_kind" == "dir" ]]; then
            tar_items+=("$skill_name")
        else
            tar_items+=("${skill_name}.md")
        fi
    fi
    if [[ $has_agents -eq 1 ]]; then
        tar_items+=(".agents")
    fi

    tar -czf "$new_file" -C "$stage_dir" \
        --exclude="*undo-tree~" \
        --exclude="__pycache__" \
        --exclude="*~" \
        --exclude="*.pyc" \
        --exclude="*.pyo" \
        "${tar_items[@]}"

    rm -rf "$stage_dir"
    echo -e "\033[32mSaved skill archive to: $new_file\033[0m"
}

save_subagent() {
    local subagent_name="$1"
    local subagent_path="$AGENTS_DIR/${subagent_name}.md"
    local type_tag="$subagent_name"
    
    if [[ ! -f "$subagent_path" ]]; then
        echo -e "\033[31mSubagent not found: $subagent_path\033[0m"
        return 1
    fi
    
    echo -e "\n\033[34m>>> Saving subagent $subagent_name\033[0m"
    ensure_type_description "SUBAGENT" "$type_tag" "subagent" || return 1
    
    local latest_file=$(ls -t "$STORAGE_DIR"/*"_${type_tag}_SUBAGENT.md" 2>/dev/null | head -1)
    
    if [[ -n "$latest_file" ]]; then
        echo "Comparing with latest version:"
        echo "---"
        
        if cmp -s "$latest_file" "$subagent_path"; then
            echo -e "\033[33mNo changes detected in $subagent_name\033[0m"
            echo -e "\033[32mSkipping save (file unchanged)\033[0m"
            return 0
        else
            compare_files "$latest_file" "$subagent_path"
            echo "---"
            echo -n -e "\033[33mSave new version? (y/N) \033[0m"
            read -n 1 -r
            echo
            if [[ ! "$REPLY" =~ ^[Yy]$ ]]; then
                echo -e "\033[31mSkipped saving $subagent_name\033[0m"
                return 0
            fi
        fi
    fi
    
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local new_file="${STORAGE_DIR}/${timestamp}_${type_tag}_SUBAGENT.md"
    cp "$subagent_path" "$new_file"
    echo -e "\033[32mSaved subagent to: $new_file\033[0m"
}

load_type_descriptions

# --- Main Loop ---

while true; do
    draw_menu
    read -r input
    
    if [[ "$input" == "q" ]] || [[ "$input" == "Q" ]]; then
        echo -e "\033[31mAborted.\033[0m"
        exit 0
    elif [[ -z "$input" ]]; then
        break
    fi
    
    category=$(key_category "$input" || true)
    if [[ "$category" == "plugin" ]]; then
        idx=$(index_for_key plugin "$input" PLUGIN_KEYS || true)
        if [[ -n "$idx" ]] && [[ $idx -lt ${#AVAILABLE_PLUGINS[@]} ]]; then
            plugin="${AVAILABLE_PLUGINS[$idx]}"
            if [[ "${SELECTED_PLUGINS[$plugin]}" == "1" ]]; then
                unset SELECTED_PLUGINS["$plugin"]
            else
                SELECTED_PLUGINS[$plugin]="1"
            fi
        fi
    elif [[ "$category" == "skill" ]]; then
        idx=$(index_for_key skill "$input" SKILL_KEYS || true)
        if [[ -n "$idx" ]] && [[ $idx -lt ${#AVAILABLE_SKILLS[@]} ]]; then
            skill="${AVAILABLE_SKILLS[$idx]}"
            if [[ "${SELECTED_SKILLS[$skill]}" == "1" ]]; then
                unset SELECTED_SKILLS["$skill"]
            else
                SELECTED_SKILLS[$skill]="1"
            fi
        fi
    elif [[ "$category" == "subagent" ]]; then
        idx=$(index_for_key subagent "$input" SUBAGENT_KEYS || true)
        if [[ -n "$idx" ]] && [[ $idx -lt ${#AVAILABLE_SUBAGENTS[@]} ]]; then
            subagent="${AVAILABLE_SUBAGENTS[$idx]}"
            if [[ "${SELECTED_SUBAGENTS[$subagent]}" == "1" ]]; then
                unset SELECTED_SUBAGENTS["$subagent"]
            else
                SELECTED_SUBAGENTS[$subagent]="1"
            fi
        fi
    elif [[ "$input" =~ ^[0-9]+$ ]] && [[ "$input" -ne 10 ]] && [[ "$input" -ge 1 ]]; then
        idx=$(_disp2idx "$input")
        if [[ $idx -ge 0 ]] && [[ $idx -lt ${#MD_FILES[@]} ]]; then
            file="${MD_FILES[$idx]}"
            if [[ "$SELECTED_MD_FILE" == "$file" ]]; then
                SELECTED_MD_FILE=""
            else
                SELECTED_MD_FILE="$file"
            fi
        fi
    fi
done

# --- Execute Saves ---

# Clear any leftover input before starting saves
while IFS= read -r -t 0.1; do : ; done 2>/dev/null || true

clear
echo -e "\033[33m=== Processing Saves ===\033[0m"
echo ""

if [[ -n "$SELECTED_MD_FILE" ]]; then
    save_md_file "$SELECTED_MD_FILE"
fi

for plugin in "${!SELECTED_PLUGINS[@]}"; do
    save_plugin "$plugin"
done

for skill in "${!SELECTED_SKILLS[@]}"; do
    save_skill "$skill"
done

for subagent in "${!SELECTED_SUBAGENTS[@]}"; do
    save_subagent "$subagent"
done

echo ""
echo -e "\033[32mAll tasks completed.\033[0m"
