#!/opt/local/bin/bash
# Whitelist management functionality
# Shows actual files that would be deleted by dry-run

set -euo pipefail

# Get script directory and source dependencies
_MOLE_MANAGE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$_MOLE_MANAGE_DIR/../core/common.sh"
source "$_MOLE_MANAGE_DIR/../ui/menu_simple.sh"

# Config file paths
readonly WHITELIST_CONFIG_CLEAN="$HOME/.config/mole/whitelist"
readonly WHITELIST_CONFIG_OPTIMIZE="$HOME/.config/mole/whitelist_optimize"
readonly WHITELIST_CONFIG_OPTIMIZE_LEGACY="$HOME/.config/mole/whitelist_checks"

# Default whitelist patterns defined in lib/core/common.sh:
# - DEFAULT_WHITELIST_PATTERNS
# - FINDER_METADATA_SENTINEL

# Save whitelist patterns to config (defaults to "clean" for legacy callers)
save_whitelist_patterns() {
    local mode="clean"
    if [[ $# -gt 0 ]]; then
        case "$1" in
            clean | optimize)
                mode="$1"
                shift
                ;;
        esac
    fi

    local -a patterns
    patterns=("$@")

    local config_file
    local header_text

    if [[ "$mode" == "optimize" ]]; then
        config_file="$WHITELIST_CONFIG_OPTIMIZE"
        header_text="# Mole Optimization Whitelist - These checks will be skipped during optimization"
    else
        config_file="$WHITELIST_CONFIG_CLEAN"
        header_text="# Mole Whitelist - Protected paths won't be deleted\n# Default protections: Playwright browsers, HuggingFace models, Maven repo, Ollama models, Surge Mac, R renv, Finder metadata\n# Add one pattern per line to keep items safe."
    fi

    ensure_user_file "$config_file"

    echo -e "$header_text" > "$config_file"

    if [[ ${#patterns[@]} -gt 0 ]]; then
        local -a unique_patterns=()
        for pattern in "${patterns[@]}"; do
            local duplicate="false"
            if [[ ${#unique_patterns[@]} -gt 0 ]]; then
                for existing in "${unique_patterns[@]}"; do
                    if patterns_equivalent "$pattern" "$existing"; then
                        duplicate="true"
                        break
                    fi
                done
            fi
            [[ "$duplicate" == "true" ]] && continue
            unique_patterns+=("$pattern")
        done

        if [[ ${#unique_patterns[@]} -gt 0 ]]; then
            printf '\n' >> "$config_file"
            for pattern in "${unique_patterns[@]}"; do
                echo "$pattern" >> "$config_file"
            done
        fi
    fi
}

# Get all cache items with their patterns
get_all_cache_items() {
    # Format: "display_name|pattern|category"
    cat << 'EOF'
Apple Mail cache|$HOME/Library/Caches/com.apple.mail/*|system_cache
Gradle build cache (Android Studio, Gradle projects)|$HOME/.gradle/caches/*|ide_cache
Gradle daemon processes cache|$HOME/.gradle/daemon/*|ide_cache
Xcode DerivedData (build outputs, indexes)|$HOME/Library/Developer/Xcode/DerivedData/*|ide_cache
Xcode archives (built app packages)|$HOME/Library/Developer/Xcode/Archives/*|ide_cache
Xcode internal cache files|$HOME/Library/Caches/com.apple.dt.Xcode/*|ide_cache
Xcode iOS device support symbols|$HOME/Library/Developer/Xcode/iOS DeviceSupport/*/Symbols/System/Library/Caches/*|ide_cache
Maven local repository (Java dependencies)|$HOME/.m2/repository/*|ide_cache
JetBrains IDEs data (IntelliJ, PyCharm, WebStorm, GoLand)|$HOME/Library/Application Support/JetBrains/*|ide_cache
JetBrains IDEs cache|$HOME/Library/Caches/JetBrains/*|ide_cache
Android Studio cache and indexes|$HOME/Library/Caches/Google/AndroidStudio*/*|ide_cache
Android build cache|$HOME/.android/build-cache/*|ide_cache
VS Code runtime cache|$HOME/Library/Application Support/Code/Cache/*|ide_cache
VS Code extension and update cache|$HOME/Library/Application Support/Code/CachedData/*|ide_cache
VS Code system cache (Cursor, VSCodium)|$HOME/Library/Caches/com.microsoft.VSCode/*|ide_cache
Cursor editor cache|$HOME/Library/Caches/com.todesktop.230313mzl4w4u92/*|ide_cache
Bazel build cache|$HOME/.cache/bazel/*|compiler_cache
Go build cache|$HOME/Library/Caches/go-build/*|compiler_cache
Go module cache|$HOME/go/pkg/mod/*|compiler_cache
Rust Cargo registry cache|$HOME/.cargo/registry/cache/*|compiler_cache
Rust documentation cache|$HOME/.rustup/toolchains/*/share/doc/*|compiler_cache
Rustup toolchain downloads|$HOME/.rustup/downloads/*|compiler_cache
ccache compiler cache|$HOME/.ccache/*|compiler_cache
sccache distributed compiler cache|$HOME/.cache/sccache/*|compiler_cache
SBT Scala build cache|$HOME/.sbt/*|compiler_cache
Ivy dependency cache|$HOME/.ivy2/cache/*|compiler_cache
Turbo monorepo build cache|$HOME/.turbo/*|compiler_cache
Next.js build cache|$HOME/.next/*|compiler_cache
Vite build cache|$HOME/.vite/*|compiler_cache
Parcel bundler cache|$HOME/.parcel-cache/*|compiler_cache
pre-commit hooks cache|$HOME/.cache/pre-commit/*|compiler_cache
Ruff Python linter cache|$HOME/.cache/ruff/*|compiler_cache
MyPy type checker cache|$HOME/.cache/mypy/*|compiler_cache
Pytest test cache|$HOME/.pytest_cache/*|compiler_cache
Flutter SDK cache|$HOME/.cache/flutter/*|compiler_cache
Swift Package Manager cache|$HOME/.cache/swift-package-manager/*|compiler_cache
Zig compiler cache|$HOME/.cache/zig/*|compiler_cache
Deno cache|$HOME/Library/Caches/deno/*|compiler_cache
CocoaPods cache (iOS dependencies)|$HOME/Library/Caches/CocoaPods/*|package_manager
npm package cache|$HOME/.npm/_cacache/*|package_manager
pip Python package cache|$HOME/.cache/pip/*|package_manager
uv Python package cache|$HOME/.cache/uv/*|package_manager
R renv global cache (virtual environments)|$HOME/Library/Caches/org.R-project.R/R/renv/*|package_manager
Homebrew downloaded packages|$HOME/Library/Caches/Homebrew/*|package_manager
Yarn package manager cache|$HOME/.cache/yarn/*|package_manager
pnpm package store|$HOME/Library/pnpm/store/*|package_manager
Composer PHP dependencies cache|$HOME/.composer/cache/*|package_manager
RubyGems cache|$HOME/.gem/cache/*|package_manager
Conda packages cache|$HOME/.conda/pkgs/*|package_manager
Anaconda packages cache|$HOME/anaconda3/pkgs/*|package_manager
PyTorch model cache|$HOME/.cache/torch/*|ai_ml_cache
TensorFlow model and dataset cache|$HOME/.cache/tensorflow/*|ai_ml_cache
HuggingFace models and datasets|$HOME/.cache/huggingface/*|ai_ml_cache
Playwright browser binaries|$HOME/Library/Caches/ms-playwright*|ai_ml_cache
Selenium WebDriver binaries|$HOME/.cache/selenium/*|ai_ml_cache
Ollama local AI models|$HOME/.ollama/models/*|ai_ml_cache
Weights & Biases ML experiments cache|$HOME/.cache/wandb/*|ai_ml_cache
Safari web browser cache|$HOME/Library/Caches/com.apple.Safari/*|browser_cache
Chrome browser cache|$HOME/Library/Caches/Google/Chrome/*|browser_cache
Firefox browser cache|$HOME/Library/Caches/Firefox/*|browser_cache
Brave browser cache|$HOME/Library/Caches/BraveSoftware/Brave-Browser/*|browser_cache
Surge proxy cache|$HOME/Library/Caches/com.nssurge.surge-mac/*|network_tools
Surge configuration and data|$HOME/Library/Application Support/com.nssurge.surge-mac/*|network_tools
Docker Desktop image cache|$HOME/Library/Containers/com.docker.docker/Data/*|container_cache
Podman container cache|$HOME/.local/share/containers/cache/*|container_cache
Font cache|$HOME/Library/Caches/com.apple.FontRegistry/*|system_cache
Spotlight metadata cache|$HOME/Library/Caches/com.apple.spotlight/*|system_cache
CloudKit cache|$HOME/Library/Caches/CloudKit/*|system_cache
Trash|$HOME/.Trash|system_cache
EOF
    # Add FINDER_METADATA with constant reference
    echo "Finder metadata, .DS_Store|$FINDER_METADATA_SENTINEL|system_cache"
}

# Get all optimize items with their patterns
get_optimize_whitelist_items() {
    # Format: "display_name|pattern|category"
    cat << 'EOF'
macOS Firewall check|firewall|security_check
Gatekeeper check|gatekeeper|security_check
macOS system updates check|check_macos_updates|update_check
Mole updates check|check_mole_update|update_check
Homebrew health check (doctor)|check_brew_health|health_check
SIP status check|check_sip|security_check
FileVault status check|check_filevault|security_check
TouchID sudo check|check_touchid|config_check
Rosetta 2 check|check_rosetta|config_check
Git configuration check|check_git_config|config_check
Login items check|check_login_items|config_check
EOF
}

patterns_equivalent() {
    local first="${1/#~/$HOME}"
    local second="${2/#~/$HOME}"

    # Only exact string match, no glob expansion
    [[ "$first" == "$second" ]] && return 0
    return 1
}

load_whitelist() {
    local mode="${1:-clean}"
    local -a patterns=()
    local config_file
    local legacy_file=""

    if [[ "$mode" == "optimize" ]]; then
        config_file="$WHITELIST_CONFIG_OPTIMIZE"
        legacy_file="$WHITELIST_CONFIG_OPTIMIZE_LEGACY"
    else
        config_file="$WHITELIST_CONFIG_CLEAN"
    fi

    local using_legacy="false"
    if [[ ! -f "$config_file" && -n "$legacy_file" && -f "$legacy_file" ]]; then
        config_file="$legacy_file"
        using_legacy="true"
    fi

    if [[ -f "$config_file" ]]; then
        while IFS= read -r line; do
            # shellcheck disable=SC2295
            line="${line#"${line%%[![:space:]]*}"}"
            # shellcheck disable=SC2295
            line="${line%"${line##*[![:space:]]}"}"
            [[ -z "$line" || "$line" =~ ^# ]] && continue
            patterns+=("$line")
        done < "$config_file"
    else
        if [[ "$mode" == "clean" ]]; then
            patterns=("${DEFAULT_WHITELIST_PATTERNS[@]}")
        elif [[ "$mode" == "optimize" ]]; then
            patterns=("${DEFAULT_OPTIMIZE_WHITELIST_PATTERNS[@]}")
        fi
    fi

    if [[ ${#patterns[@]} -gt 0 ]]; then
        local -a unique_patterns=()
        for pattern in "${patterns[@]}"; do
            local duplicate="false"
            if [[ ${#unique_patterns[@]} -gt 0 ]]; then
                for existing in "${unique_patterns[@]}"; do
                    if patterns_equivalent "$pattern" "$existing"; then
                        duplicate="true"
                        break
                    fi
                done
            fi
            [[ "$duplicate" == "true" ]] && continue
            unique_patterns+=("$pattern")
        done
        CURRENT_WHITELIST_PATTERNS=("${unique_patterns[@]}")

        # Migrate legacy optimize config to the new path automatically
        if [[ "$mode" == "optimize" && "$using_legacy" == "true" && "$config_file" != "$WHITELIST_CONFIG_OPTIMIZE" ]]; then
            save_whitelist_patterns "$mode" "${CURRENT_WHITELIST_PATTERNS[@]}"
        fi
    else
        CURRENT_WHITELIST_PATTERNS=()
    fi
}

is_whitelisted() {
    local pattern="$1"
    local check_pattern="${pattern/#\~/$HOME}"

    if [[ ${#CURRENT_WHITELIST_PATTERNS[@]} -eq 0 ]]; then
        return 1
    fi

    for existing in "${CURRENT_WHITELIST_PATTERNS[@]}"; do
        local existing_expanded="${existing/#\~/$HOME}"
        # Only use exact string match to prevent glob expansion security issues
        if [[ "$check_pattern" == "$existing_expanded" ]]; then
            return 0
        fi
    done
    return 1
}

manage_whitelist() {
    local mode="${1:-clean}"
    manage_whitelist_categories "$mode"
}

manage_whitelist_categories() {
    local mode="$1"

    # Load currently enabled patterns from both sources
    load_whitelist "$mode"

    # Build cache items list
    local -a cache_items=()
    local -a cache_patterns=()
    local -a menu_options=()
    local index=0

    # Choose source based on mode
    local items_source
    local menu_title
    local active_config_file

    if [[ "$mode" == "optimize" ]]; then
        items_source=$(get_optimize_whitelist_items)
        active_config_file="$WHITELIST_CONFIG_OPTIMIZE"
        local display_config="${active_config_file/#$HOME/~}"
        menu_title="Whitelist Manager, Select system checks to ignore
${GRAY}Edit: ${display_config}${NC}"
    else
        items_source=$(get_all_cache_items)
        active_config_file="$WHITELIST_CONFIG_CLEAN"
        local display_config="${active_config_file/#$HOME/~}"
        menu_title="Whitelist Manager, Select caches to protect
${GRAY}Edit: ${display_config}${NC}"
    fi

    while IFS='|' read -r display_name pattern _; do
        # Expand $HOME in pattern
        pattern="${pattern/\$HOME/$HOME}"

        cache_items+=("$display_name")
        cache_patterns+=("$pattern")
        menu_options+=("$display_name")

        index=$((index + 1))
    done <<< "$items_source"

    # Identify custom patterns (not in predefined list)
    local -a custom_patterns=()
    if [[ ${#CURRENT_WHITELIST_PATTERNS[@]} -gt 0 ]]; then
        for current_pattern in "${CURRENT_WHITELIST_PATTERNS[@]}"; do
            local is_predefined=false
            for predefined_pattern in "${cache_patterns[@]}"; do
                if patterns_equivalent "$current_pattern" "$predefined_pattern"; then
                    is_predefined=true
                    break
                fi
            done
            if [[ "$is_predefined" == "false" ]]; then
                custom_patterns+=("$current_pattern")
            fi
        done
    fi

    # Prioritize already-selected items to appear first
    local -a selected_cache_items=()
    local -a selected_cache_patterns=()
    local -a selected_menu_options=()
    local -a remaining_cache_items=()
    local -a remaining_cache_patterns=()
    local -a remaining_menu_options=()

    for ((i = 0; i < ${#cache_patterns[@]}; i++)); do
        if is_whitelisted "${cache_patterns[i]}"; then
            selected_cache_items+=("${cache_items[i]}")
            selected_cache_patterns+=("${cache_patterns[i]}")
            selected_menu_options+=("${menu_options[i]}")
        else
            remaining_cache_items+=("${cache_items[i]}")
            remaining_cache_patterns+=("${cache_patterns[i]}")
            remaining_menu_options+=("${menu_options[i]}")
        fi
    done

    cache_items=()
    cache_patterns=()
    menu_options=()
    if [[ ${#selected_cache_items[@]} -gt 0 ]]; then
        cache_items=("${selected_cache_items[@]}")
        cache_patterns=("${selected_cache_patterns[@]}")
        menu_options=("${selected_menu_options[@]}")
    fi
    if [[ ${#remaining_cache_items[@]} -gt 0 ]]; then
        cache_items+=("${remaining_cache_items[@]}")
        cache_patterns+=("${remaining_cache_patterns[@]}")
        menu_options+=("${remaining_menu_options[@]}")
    fi

    if [[ ${#selected_cache_patterns[@]} -gt 0 ]]; then
        local -a preselected_indices=()
        for ((i = 0; i < ${#selected_cache_patterns[@]}; i++)); do
            preselected_indices+=("$i")
        done
        local IFS=','
        export MOLE_PRESELECTED_INDICES="${preselected_indices[*]}"
    else
        unset MOLE_PRESELECTED_INDICES
    fi

    MOLE_SELECTION_RESULT=""
    paginated_multi_select "$menu_title" "${menu_options[@]}"
    unset MOLE_PRESELECTED_INDICES
    local exit_code=$?

    # Normal exit or cancel
    if [[ $exit_code -ne 0 ]]; then
        return 1
    fi

    # Convert selected indices to patterns
    local -a selected_patterns=()
    if [[ -n "$MOLE_SELECTION_RESULT" ]]; then
        local -a selected_indices
        IFS=',' read -ra selected_indices <<< "$MOLE_SELECTION_RESULT"
        for idx in "${selected_indices[@]}"; do
            if [[ $idx -ge 0 && $idx -lt ${#cache_patterns[@]} ]]; then
                local pattern="${cache_patterns[$idx]}"
                # Convert back to portable format with ~
                pattern="${pattern/#$HOME/~}"
                selected_patterns+=("$pattern")
            fi
        done
    fi

    # Merge custom patterns with selected patterns
    local -a all_patterns=()
    if [[ ${#selected_patterns[@]} -gt 0 ]]; then
        all_patterns=("${selected_patterns[@]}")
    fi
    if [[ ${#custom_patterns[@]} -gt 0 ]]; then
        for custom_pattern in "${custom_patterns[@]}"; do
            all_patterns+=("$custom_pattern")
        done
    fi

    # Save to whitelist config (bash 3.2 + set -u safe)
    if [[ ${#all_patterns[@]} -gt 0 ]]; then
        save_whitelist_patterns "$mode" "${all_patterns[@]}"
    else
        save_whitelist_patterns "$mode"
    fi

    local total_protected=$((${#selected_patterns[@]} + ${#custom_patterns[@]}))
    local -a summary_lines=()
    summary_lines+=("Whitelist Updated")
    if [[ ${#custom_patterns[@]} -gt 0 ]]; then
        summary_lines+=("Protected ${#selected_patterns[@]} predefined + ${#custom_patterns[@]} custom patterns")
    else
        summary_lines+=("Protected ${total_protected} caches")
    fi
    local display_config="${active_config_file/#$HOME/~}"
    summary_lines+=("Config: ${GRAY}${display_config}${NC}")

    print_summary_block "${summary_lines[@]}"
    printf '\n'
}

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    manage_whitelist
fi
