#!/usr/bin/env bash
# ssr-mackup — Manage auto-generated Mackup app definitions for dot-dirs.
#
# Subcommands:
#   scan    Per-item prompts for new ~/.mackup/ssr-*.cfg files.
#   list    List existing ~/.mackup/ssr-*.cfg files with the path they cover.
#   clean   Remove all generated ssr-*.cfg files (prompts first).
#   unlink  Run `mackup link uninstall` — only for undoing symlink-based Mackup
#           (legacy SSR or manual `mackup link`); copies from storage and removes symlinks.

set -euo pipefail
SSR_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
# shellcheck source=../lib/common.sh
source "$SSR_ROOT/lib/common.sh"
# shellcheck source=../lib/cloud.sh
source "$SSR_ROOT/lib/cloud.sh"
# shellcheck source=../lib/mackup.sh
source "$SSR_ROOT/lib/mackup.sh"

ssr::require_macos
ssr::load_config

usage() {
    cat <<EOF
Usage: ssr mackup <subcommand>

Manage auto-generated Mackup app definitions for dot-dirs not covered
by Mackup's built-in app definitions.

Subcommands:
  scan    Suggest new ~/.mackup/ssr-*.cfg files (confirm each item).
  list    List existing generated files.
  clean   Remove all generated ssr-*.cfg files.
  unlink  Undo symlink-based Mackup only (`mackup link uninstall`).

Run 'ssr mackup scan' when new dot-dirs appear; commit ~/.mackup/ssr-*.cfg
with your dotfiles if you use them.

If you previously used symlink-based Mackup and apps break, run:
  ssr mackup unlink
Current SSR uses copy-based `mackup backup` / `restore` instead.
EOF
}

# ---------------------------------------------------------------------------
# scan — show each candidate and confirm per-item
# ---------------------------------------------------------------------------
cmd_scan() {
    local mackup_dir
    mackup_dir="$(ssr::cloud::mackup_dir 2>/dev/null)" \
        || mackup_dir=""   # allow offline use; _in_backup will just never match

    local cfg_dir="$HOME/.mackup"
    mkdir -p "$cfg_dir"

    local skip_set=" ${_SSR_MACKUP_SKIP[*]} "

    local declared
    declared="$(_ssr_mackup_declared_paths)"

    # Returns true when <path> is declared exactly OR when any built-in declares
    # a sub-path of <path> (e.g. .byobu/profile covers .byobu, .claude/settings
    # covers .claude, etc.).
    # Uses `command grep` (bypasses colour aliases) and a bash glob for the
    # prefix check (avoids regex escaping issues with dots in paths).
    _is_declared() {
        local p="$1" decl
        command grep -qxF "$p" <<< "$declared" && return 0
        while IFS= read -r decl; do
            [[ "$decl" == "$p/"* ]] && return 0
        done <<< "$declared"
        return 1
    }
    _in_backup() { [[ -n "$mackup_dir" && -e "$mackup_dir/$1" ]]; }

    # -----------------------------------------------------------------------
    # Collect candidate items — same logic as generate_custom_cfg.
    # -----------------------------------------------------------------------
    local -a items=()
    local item
    for item in "${_SSR_MACKUP_CURATED[@]}" ${SSR_MACKUP_EXTRA_PATHS:-}; do
        [[ -e "$HOME/$item" ]] || continue
        _is_declared "$item"   && continue
        _in_backup "$item"     && continue
        items+=("$item")
    done
    local name
    while IFS= read -r dotdir; do
        name="${dotdir##*/}"
        [[ "$skip_set" == *" $name "* ]] && continue
        _is_declared "$name"   && continue
        _in_backup "$name"     && continue
        local dup=false
        for item in "${items[@]:-}"; do [[ "$item" == "$name" ]] && dup=true && break; done
        $dup && continue
        items+=("$name")
    done < <(find "$HOME" -maxdepth 1 -mindepth 1 -name '.*' -type d 2>/dev/null | sort)

    # -----------------------------------------------------------------------
    # Stale ssr-*.cfg files: exist but now covered by a built-in definition.
    # -----------------------------------------------------------------------
    local -a stale_cfgs=()
    local stale_cfg stale_path
    for stale_cfg in "$cfg_dir"/ssr-*.cfg; do
        [[ -f "$stale_cfg" ]] || continue
        stale_path="$(awk '/^\[configuration_files\]/{f=1;next}/^\[/{f=0}f&&/^[^#[:space:]]/{print $1;exit}' "$stale_cfg")"
        _is_declared "$stale_path" && stale_cfgs+=("$stale_cfg")
    done

    # -----------------------------------------------------------------------
    # Filter to NEW items only (user owns existing files).
    # -----------------------------------------------------------------------
    local -a new_items=()
    local -a new_cfgs=()
    local it app_name cfg_file
    for it in "${items[@]:-}"; do
        app_name="$(_ssr_mackup_cfg_name "$it")"
        cfg_file="$cfg_dir/${app_name}.cfg"
        [[ -f "$cfg_file" ]] && continue
        new_items+=("$it")
        new_cfgs+=("$cfg_file")
    done

    if [[ ${#new_items[@]} -eq 0 && ${#stale_cfgs[@]} -eq 0 ]]; then
        ssr::ok "All ~/.mackup/ssr-*.cfg files are up-to-date — nothing to do."
        return 0
    fi

    printf '\n'
    local written=0 removed=0

    # -----------------------------------------------------------------------
    # New files — confirm each individually.
    # -----------------------------------------------------------------------
    if [[ ${#new_items[@]} -gt 0 ]]; then
        printf '\033[1mNew Mackup app definitions\033[0m  (%d candidate(s))\n\n' "${#new_items[@]}"

        local i
        for i in "${!new_items[@]}"; do
            it="${new_items[$i]}"
            cfg_file="${new_cfgs[$i]}"
            local cfg_name="${cfg_file##*/}"

            # Resolved paths preview.
            local resolved; resolved="$(_ssr_mackup_resolve_paths "$it")"
            local line_count; line_count="$(printf '%s\n' "$resolved" | wc -l | tr -d ' ')"

            # Header for this item.
            printf '  \033[32m+\033[0m \033[1m~/.mackup/%s\033[0m\n' "$cfg_name"
            if [[ "$line_count" -eq 1 && "$resolved" == "$it" ]]; then
                printf '      backs up: ~/%s/ (whole directory)\n' "$it"
            else
                printf '      backs up: %d path(s) inside ~/%s/\n' "$line_count" "$it"
                printf '%s\n' "$resolved" | while IFS= read -r p; do
                    printf '        · ~/%s\n' "$p"
                done
            fi
            printf '\n'

            if ssr::confirm "  Create ~/.mackup/$cfg_name?" 30; then
                _ssr_mackup_write_cfg "$cfg_dir" "$it"
                written=$((written + 1))
                printf '    \033[32m✓ written\033[0m\n\n'
            else
                printf '    skipped\n\n'
            fi
        done
    fi

    # -----------------------------------------------------------------------
    # Stale files — confirm each removal individually.
    # -----------------------------------------------------------------------
    if [[ ${#stale_cfgs[@]} -gt 0 ]]; then
        printf '\033[1mStale duplicates\033[0m  (now covered by a Mackup built-in)\n\n'

        local f
        for f in "${stale_cfgs[@]}"; do
            printf '  \033[31m-\033[0m \033[1m%s\033[0m\n' "~/.mackup/${f##*/}"
            # Show which built-in covers it.
            stale_path="$(awk '/^\[configuration_files\]/{f=1;next}/^\[/{f=0}f&&/^[^#[:space:]]/{print $1;exit}' "$f")"
            printf '      path \033[2m%s\033[0m is now declared by a built-in Mackup app\n\n' "$stale_path"

            if ssr::confirm "  Remove ${f##*/}?" 30; then
                rm -f "$f"
                removed=$((removed + 1))
                printf '    \033[31m✓ removed\033[0m\n\n'
            else
                printf '    skipped\n\n'
            fi
        done
    fi

    # -----------------------------------------------------------------------
    # Summary.
    # -----------------------------------------------------------------------
    if [[ $written -gt 0 || $removed -gt 0 ]]; then
        ssr::ok "Done — $written written, $removed removed."
        [[ $written -gt 0 ]] && printf '\nEdit ~/.mackup/ssr-*.cfg directly to adjust paths.\n'
        printf 'Run \033[1mssr sync\033[0m to back up with the updated definitions.\n\n'
    else
        printf 'Nothing applied.\n'
    fi
}

# ---------------------------------------------------------------------------
# list — show existing generated files
# ---------------------------------------------------------------------------
cmd_list() {
    local cfg_dir="$HOME/.mackup"
    local found=0 cfg path

    for cfg in "$cfg_dir"/ssr-*.cfg; do
        [[ -f "$cfg" ]] || continue
        path="$(awk '/^\[configuration_files\]/{f=1;next}/^\[/{f=0}f&&/^[^#[:space:]]/{print $1;exit}' "$cfg")"
        printf '  %-42s  %s\n' "~/.mackup/${cfg##*/}" "$path"
        found=$((found + 1))
    done

    if [[ $found -eq 0 ]]; then
        printf 'No generated ssr-*.cfg files found in ~/.mackup/\n'
        printf 'Run: ssr mackup scan\n'
    else
        printf '\n%d file(s) total.\n' "$found"
    fi
}

# ---------------------------------------------------------------------------
# clean — remove all generated files
# ---------------------------------------------------------------------------
cmd_clean() {
    local cfg_dir="$HOME/.mackup"
    local -a found=()
    local cfg
    for cfg in "$cfg_dir"/ssr-*.cfg; do
        [[ -f "$cfg" ]] && found+=("$cfg")
    done

    if [[ ${#found[@]} -eq 0 ]]; then
        ssr::ok "No generated ssr-*.cfg files to remove."
        return 0
    fi

    printf 'Will remove %d file(s):\n' "${#found[@]}"
    for cfg in "${found[@]}"; do
        printf '  %s\n' "~/.mackup/${cfg##*/}"
    done
    printf '\n'

    ssr::confirm "Remove all generated ssr-*.cfg files?" 60 || { printf 'Aborted.\n'; return 0; }
    for cfg in "${found[@]}"; do rm -f "$cfg"; done
    ssr::ok "Removed ${#found[@]} file(s)."
}

# ---------------------------------------------------------------------------
# unlink — mackup link uninstall (recovery)
# ---------------------------------------------------------------------------
cmd_unlink() {
    cat <<'EOF'
This runs:  mackup -f link uninstall

Mackup copies each managed path from your Mackup storage folder back into
$HOME and removes the symlinks. Use only if you still have symlink-based
Mackup from an older SSR or from manual `mackup link` / `link install`.

After unlink, apps use normal local files again. Ongoing `ssr sync` uses
`mackup backup` (copy to cloud), not symlinks.
EOF
    printf '\n'
    ssr::confirm "Run mackup link uninstall now?" 120 || { printf 'Aborted.\n'; return 0; }
    ssr::mackup::link_uninstall && ssr::ok "mackup link uninstall finished."
}

# ---------------------------------------------------------------------------
# Dispatch
# ---------------------------------------------------------------------------
[[ $# -gt 0 ]] || { usage; exit 1; }

case "$1" in
    scan)   cmd_scan   ;;
    list)   cmd_list   ;;
    clean)  cmd_clean  ;;
    unlink) cmd_unlink ;;
    -h|--help) usage ;;
    *) ssr::err "Unknown subcommand: $1"; usage; exit 1 ;;
esac
