#!/usr/bin/env bash
set -euo pipefail

# ─────────────────────────────────────────────────────────────────────────────
# 10-prune-cleanup.sh — SecureGit Repository Hygiene
#
# Clean up stale/merged branches, remote tracking refs, worktrees, and show
# repo size stats with large file analysis. Produces a summary report.
#
# Usage:
#   ./10-prune-cleanup.sh [options]
#   ./10-prune-cleanup.sh --dry-run
#   ./10-prune-cleanup.sh --all --force
#
# Options:
#   --dry-run         Show what would be cleaned without doing it
#   --all             Run all cleanup steps non-interactively
#   --stale-days <n>  Override SG_STALE_BRANCH_DAYS (default: 30)
#   --force           Skip confirmations
#   --help            Show this help message
# ─────────────────────────────────────────────────────────────────────────────

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
source "$SCRIPT_DIR/lib/securegit-common.sh"

DRY_RUN=false RUN_ALL=false FORCE=false
STALE_DAYS="${SG_STALE_BRANCH_DAYS:-30}"
BRANCHES_DELETED=0 REMOTE_REFS_PRUNED=0 WORKTREES_CLEANED=0
CLEANUP_ACTIONS=()

show_help() { sed -n '/^# Usage:/,/^# ──/{ /^# ──/d; s/^# \?//; p }' "$0"; exit 0; }

while [[ $# -gt 0 ]]; do
    case "$1" in
        --dry-run)     DRY_RUN=true; shift ;;
        --all)         RUN_ALL=true; shift ;;
        --force)       FORCE=true; shift ;;
        --stale-days)  STALE_DAYS="${2:-$STALE_DAYS}"; shift 2 ;;
        --help|-h)     show_help ;;
        *)             sg_die "Unknown option: $1. Use --help for usage." ;;
    esac
done

sg_require_repo
CURRENT_BRANCH="$(sg_current_branch)"
DEFAULT_BRANCH="$(sg_default_branch)"
REPO_ROOT="$(git rev-parse --show-toplevel)"

should_proceed() {
    [[ "$RUN_ALL" == true || "$FORCE" == true ]] && return 0
    sg_confirm "$1" "${2:-y}"
}

add_action() { CLEANUP_ACTIONS+=("$1"); }

# ─── Merged branches ────────────────────────────────────────────────────────
cleanup_merged_branches() {
    sg_header "Merged Branches"
    local merged=()
    while IFS= read -r b; do
        b="$(echo "$b" | xargs)"; [[ -z "$b" || "$b" == "$CURRENT_BRANCH" ]] && continue
        sg_is_protected "$b" && continue
        merged+=("$b")
    done < <(git branch --merged "$DEFAULT_BRANCH" --format='%(refname:short)' 2>/dev/null)

    if [[ ${#merged[@]} -eq 0 ]]; then sg_success "No merged branches to clean up."; return; fi
    sg_info "${#merged[@]} branch(es) fully merged into '$DEFAULT_BRANCH':"
    echo
    for i in "${!merged[@]}"; do
        local age; age="$(git log -1 --format='%cr' "${merged[$i]}" 2>/dev/null || echo "unknown")"
        printf "  ${_C_DIM}%2d)${_C_RESET} %-40s ${_C_DIM}%s${_C_RESET}\n" "$((i+1))" "${merged[$i]}" "$age"
    done; echo

    if [[ "$DRY_RUN" == true ]]; then
        printf "${_C_YELLOW}[DRY RUN]${_C_RESET} Would delete %d merged branch(es).\n" "${#merged[@]}"
        add_action "Would delete ${#merged[@]} merged branch(es)"; return
    fi
    if should_proceed "Delete all ${#merged[@]} merged branches?"; then
        for b in "${merged[@]}"; do
            _sg_cmd branch -d "$b" 2>&1 && ((BRANCHES_DELETED++)) || sg_warn "Failed: $b"
        done
        sg_success "Deleted $BRANCHES_DELETED merged branch(es)."
        add_action "Deleted $BRANCHES_DELETED merged branch(es)"
    else sg_info "Skipped."; fi
}

# ─── Stale branches ─────────────────────────────────────────────────────────
cleanup_stale_branches() {
    sg_header "Stale Branches (>${STALE_DAYS} days)"
    local cutoff; cutoff="$(date -d "$STALE_DAYS days ago" +%s 2>/dev/null || date -v-"${STALE_DAYS}"d +%s 2>/dev/null || echo 0)"
    [[ "$cutoff" -eq 0 ]] && { sg_warn "Cannot compute date cutoff. Skipping."; return; }

    local stale=() ages=()
    while IFS= read -r b; do
        b="$(echo "$b" | xargs)"; [[ -z "$b" || "$b" == "$CURRENT_BRANCH" ]] && continue
        sg_is_protected "$b" && continue
        local epoch; epoch="$(git log -1 --format='%ct' "$b" 2>/dev/null || echo 0)"
        if [[ "$epoch" -lt "$cutoff" && "$epoch" -gt 0 ]]; then
            stale+=("$b"); ages+=("$(git log -1 --format='%cr' "$b" 2>/dev/null)")
        fi
    done < <(git branch --format='%(refname:short)' 2>/dev/null)

    if [[ ${#stale[@]} -eq 0 ]]; then sg_success "No stale branches."; return; fi
    sg_info "${#stale[@]} stale branch(es):"
    echo
    for i in "${!stale[@]}"; do
        local merged_flag="${_C_YELLOW}[unmerged]${_C_RESET}"
        git branch --merged "$DEFAULT_BRANCH" 2>/dev/null | grep -q "^\s*${stale[$i]}$" && merged_flag="${_C_GREEN}[merged]${_C_RESET}"
        printf "  ${_C_DIM}%2d)${_C_RESET} %-35s ${_C_DIM}%-18s${_C_RESET} %b\n" "$((i+1))" "${stale[$i]}" "${ages[$i]}" "$merged_flag"
    done; echo

    if [[ "$DRY_RUN" == true ]]; then
        printf "${_C_YELLOW}[DRY RUN]${_C_RESET} Would offer to delete %d stale branch(es).\n" "${#stale[@]}"
        add_action "Would delete up to ${#stale[@]} stale branch(es)"; return
    fi

    if [[ "$RUN_ALL" == true || "$FORCE" == true ]]; then
        for b in "${stale[@]}"; do
            if git branch --merged "$DEFAULT_BRANCH" 2>/dev/null | grep -q "^\s*${b}$"; then
                _sg_cmd branch -d "$b" 2>&1 && ((BRANCHES_DELETED++)) || true
            else sg_warn "Skipping unmerged: $b"; fi
        done
    else
        sg_info "Enter numbers (space-separated), 'all' for all merged, or 'none':"
        local sel; read -r sel
        [[ "$sel" == "none" ]] && return
        local targets=()
        if [[ "$sel" == "all" ]]; then targets=("${stale[@]}")
        else for n in $sel; do [[ "$n" =~ ^[0-9]+$ ]] && (( n >= 1 && n <= ${#stale[@]} )) && targets+=("${stale[$((n-1))]}"); done; fi
        for b in "${targets[@]}"; do
            if git branch --merged "$DEFAULT_BRANCH" 2>/dev/null | grep -q "^\s*${b}$"; then
                _sg_cmd branch -d "$b" 2>&1 && ((BRANCHES_DELETED++)) || true
            else
                sg_warn "'$b' is NOT merged."
                sg_confirm "  Force delete (data loss risk)?" "n" && { _sg_cmd branch -D "$b" 2>&1 && ((BRANCHES_DELETED++)) || true; }
            fi
        done
    fi
    add_action "Deleted stale branches"
}

# ─── Remote tracking prune ──────────────────────────────────────────────────
prune_remote_tracking() {
    sg_header "Remote Tracking References"
    local remotes; remotes="$(git remote 2>/dev/null)"
    [[ -z "$remotes" ]] && { sg_info "No remotes configured."; return; }
    local stale_refs=()
    while IFS= read -r remote; do
        [[ -z "$remote" ]] && continue
        while IFS= read -r ref; do [[ -n "$ref" ]] && stale_refs+=("$ref"); done \
            < <(git remote prune "$remote" --dry-run 2>/dev/null | grep '\[would prune\]' | awk '{print $NF}')
    done <<< "$remotes"

    if [[ ${#stale_refs[@]} -eq 0 ]]; then sg_success "No stale remote refs."; return; fi
    sg_info "${#stale_refs[@]} stale ref(s):"; for ref in "${stale_refs[@]}"; do printf "  - %s\n" "$ref"; done; echo
    if [[ "$DRY_RUN" == true ]]; then
        printf "${_C_YELLOW}[DRY RUN]${_C_RESET} Would prune %d ref(s).\n" "${#stale_refs[@]}"
        add_action "Would prune ${#stale_refs[@]} ref(s)"; return
    fi
    if should_proceed "Prune stale remote refs?"; then
        while IFS= read -r r; do [[ -n "$r" ]] && _sg_cmd remote prune "$r" 2>&1; done <<< "$remotes"
        REMOTE_REFS_PRUNED=${#stale_refs[@]}
        sg_success "Pruned $REMOTE_REFS_PRUNED ref(s)."
        add_action "Pruned $REMOTE_REFS_PRUNED remote ref(s)"
    fi
}

# ─── Worktree cleanup ───────────────────────────────────────────────────────
cleanup_worktrees() {
    sg_header "Worktree Cleanup"
    sg_info "Active worktrees:"; git worktree list 2>/dev/null | while IFS= read -r l; do printf "  %s\n" "$l"; done; echo
    local prune_out; prune_out="$(git worktree prune --dry-run 2>&1 || true)"
    if [[ -z "$prune_out" ]] || echo "$prune_out" | grep -q "^$"; then
        sg_success "No stale worktrees."; return
    fi
    sg_info "Stale worktrees detected:"; printf "  %s\n" "$prune_out"; echo
    if [[ "$DRY_RUN" == true ]]; then
        printf "${_C_YELLOW}[DRY RUN]${_C_RESET} Would prune stale worktrees.\n"
        add_action "Would prune stale worktrees"; return
    fi
    if should_proceed "Prune stale worktrees?"; then
        git worktree prune 2>&1; ((WORKTREES_CLEANED++)) || true
        sg_success "Worktrees pruned."; add_action "Pruned stale worktrees"
    fi
}

# ─── Repo size stats ────────────────────────────────────────────────────────
show_repo_stats() {
    sg_header "Repository Size Statistics"
    printf "  .git size:     ${_C_BOLD}%s${_C_RESET}\n" "$(du -sh "$REPO_ROOT/.git" 2>/dev/null | cut -f1 || echo "?")"
    printf "  Tree size:     ${_C_BOLD}%s${_C_RESET}\n" "$(du -sh --exclude='.git' "$REPO_ROOT" 2>/dev/null | cut -f1 || echo "?")"
    printf "  Branches:      ${_C_BOLD}%s${_C_RESET}\n" "$(git branch | wc -l)"
    printf "  Total commits: ${_C_BOLD}%s${_C_RESET}\n" "$(git rev-list --all --count 2>/dev/null || echo "?")"
    local obj; obj="$(git count-objects -v 2>/dev/null || true)"
    if [[ -n "$obj" ]]; then
        printf "  Loose objects: ${_C_BOLD}%s${_C_RESET}  Packed: ${_C_BOLD}%s${_C_RESET}\n" \
            "$(echo "$obj" | grep '^count:' | awk '{print $2}')" "$(echo "$obj" | grep '^in-pack:' | awk '{print $2}')"
    fi
    echo; sg_info "Largest files in history:"
    git rev-list --objects --all 2>/dev/null | \
        git cat-file --batch-check='%(objecttype) %(objectsize) %(objectname) %(rest)' 2>/dev/null | \
        grep '^blob ' | sort -t' ' -k2 -n -r | head -10 | while IFS= read -r line; do
        local sz nm; sz="$(echo "$line" | awk '{print $2}')"; nm="$(echo "$line" | awk '{$1=$2=$3=""; print}' | xargs)"
        if [[ "$sz" -gt 1048576 ]] 2>/dev/null; then printf "  %6.1f MB  %s\n" "$(echo "scale=1; $sz/1048576" | bc 2>/dev/null || echo "$sz")" "$nm"
        elif [[ "$sz" -gt 1024 ]] 2>/dev/null; then printf "  %6.1f KB  %s\n" "$(echo "scale=1; $sz/1024" | bc 2>/dev/null || echo "$sz")" "$nm"
        else printf "  %6d  B  %s\n" "$sz" "$nm"; fi
    done; echo
}

# ─── SecureGit stats ────────────────────────────────────────────────────────
show_securegit_stats() {
    sg_header "SecureGit Statistics"
    if command -v securegit &>/dev/null; then
        securegit scan --min-severity low 2>&1 | tail -5 || sg_info "No scan data."
    else sg_info "securegit not installed. Install for security scanning."; fi
}

# ─── Summary ─────────────────────────────────────────────────────────────────
show_summary() {
    sg_header "Cleanup Summary"
    [[ "$DRY_RUN" == true ]] && printf "  ${_C_YELLOW}DRY RUN — no changes made${_C_RESET}\n\n"
    printf "  Branches deleted:   ${_C_BOLD}%d${_C_RESET}\n" "$BRANCHES_DELETED"
    printf "  Remote refs pruned: ${_C_BOLD}%d${_C_RESET}\n" "$REMOTE_REFS_PRUNED"
    printf "  Worktrees cleaned:  ${_C_BOLD}%d${_C_RESET}\n\n" "$WORKTREES_CLEANED"
    if [[ ${#CLEANUP_ACTIONS[@]} -gt 0 ]]; then
        sg_info "Actions:"; for a in "${CLEANUP_ACTIONS[@]}"; do printf "  - %s\n" "$a"; done; echo
    fi
    [[ "$DRY_RUN" == true ]] && sg_info "Run without --dry-run to apply."
    [[ "$BRANCHES_DELETED" -gt 0 ]] && sg_undo_available
}

# ─── Main ────────────────────────────────────────────────────────────────────
main() {
    sg_header "SecureGit Repository Hygiene"
    [[ "$DRY_RUN" == true ]] && printf "  ${_C_YELLOW}DRY RUN MODE${_C_RESET}\n\n"
    sg_info "Repository: $REPO_ROOT"
    sg_info "Branch: $CURRENT_BRANCH  Default: $DEFAULT_BRANCH  Stale: >${STALE_DAYS}d"
    echo
    cleanup_merged_branches
    cleanup_stale_branches
    prune_remote_tracking
    cleanup_worktrees
    show_repo_stats
    show_securegit_stats
    show_summary
}

main
