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

# ─────────────────────────────────────────────────────────────────────────────
# 08-cherry-pick-hotfix.sh — Surgical Cherry-Pick & Hotfix Workflow
#
# Description:
#   Cherry-pick and hotfix management with safety snapshots, conflict
#   handling, security scanning, push, and PR creation support.
#
# Usage:
#   ./08-cherry-pick-hotfix.sh [COMMAND] [OPTIONS] [COMMITS...]
#   Commands: hotfix pick list status abort
#
# Options:
#   --source BRANCH   Source branch for commits
#   --target BRANCH   Target branch for hotfix base
#   --push            Push after cherry-pick
#   --pr              Create pull request
#   --no-scan         Skip post-pick security scan
#   -h, --help        Show this help
# ─────────────────────────────────────────────────────────────────────────────

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

# ─── Argument parsing ───────────────────────────────────────────────────────
OPT_SOURCE="" ; OPT_TARGET="" ; OPT_PUSH=false ; OPT_PR=false ; OPT_NO_SCAN=false
COMMAND="" ; POSITIONAL=()
while [[ $# -gt 0 ]]; do
    case "$1" in
        hotfix|pick|list|status|abort) COMMAND="$1" ;;
        --source) shift; OPT_SOURCE="${1:-}" ;; --target) shift; OPT_TARGET="${1:-}" ;;
        --push) OPT_PUSH=true ;; --pr) OPT_PR=true ;; --no-scan) OPT_NO_SCAN=true ;;
        -h|--help)
            echo "Usage: $0 [hotfix|pick|list|status|abort] [OPTIONS] [COMMITS...]"
            echo "Options: --source BRANCH --target BRANCH --push --pr --no-scan"
            exit 0 ;;
        *) POSITIONAL+=("$1") ;;
    esac; shift
done

sg_require_repo
REPO_ROOT="$(git rev-parse --show-toplevel)"

# ─── Helpers ─────────────────────────────────────────────────────────────────
_find_release_branches() {
    local pat="${SG_RELEASE_BRANCH_PATTERN:-^release/}" candidates=()
    for b in production main master; do sg_branch_exists "$b" && candidates+=("$b"); done
    while IFS= read -r b; do
        [[ -z "$b" ]] && continue
        echo "$b" | grep -qP "$pat" && candidates+=("$b")
    done < <(git branch --format='%(refname:short)' 2>/dev/null)
    printf '%s\n' "${candidates[@]}"
}

_create_snapshot() {
    sg_snapshot_create "pre-cherry-pick: $1"
    sg_info "Recovery point: $(git rev-parse HEAD)"
}

_select_commits() {
    local branch="$1"
    sg_header "Select Commits from $branch"
    local hashes=() subjects=()
    while IFS='|' read -r h s _ _; do
        [[ -z "$h" ]] && continue; hashes+=("$h"); subjects+=("$s")
    done < <(git log --format='%h|%s|%as|%an' -n 30 "$branch" 2>/dev/null)
    (( ${#hashes[@]} == 0 )) && { sg_error "No commits on: $branch"; return 1; }
    for i in "${!hashes[@]}"; do
        printf "  ${_C_BOLD}%2d)${_C_RESET} ${_C_YELLOW}%s${_C_RESET} %-45s ${_C_DIM}%s %s${_C_RESET}\n" \
            "$((i+1))" "${hashes[$i]}" "${subjects[$i]:0:45}" \
            "$(git log -1 --format='%as' "${hashes[$i]}" 2>/dev/null)" \
            "$(git log -1 --format='%an' "${hashes[$i]}" 2>/dev/null)"
    done; echo
    sg_info "Enter numbers separated by spaces (e.g., '1 3 5')."
    local sel; sel="$(sg_prompt "Commits to cherry-pick")"
    [[ -z "$sel" ]] && { sg_warn "None selected."; return 1; }
    SELECTED_HASHES=()
    for n in $sel; do
        if [[ "$n" =~ ^[0-9]+$ ]] && (( n >= 1 && n <= ${#hashes[@]} )); then
            SELECTED_HASHES+=("${hashes[$((n-1))]}")
        elif git rev-parse --verify "$n" &>/dev/null; then
            SELECTED_HASHES+=("$n")
        else
            sg_warn "Skipping invalid: $n"
        fi
    done
    (( ${#SELECTED_HASHES[@]} == 0 )) && { sg_error "No valid commits."; return 1; }
    sg_info "Selected ${#SELECTED_HASHES[@]} commit(s):"
    for h in "${SELECTED_HASHES[@]}"; do
        printf "    ${_C_YELLOW}%s${_C_RESET} %s\n" "$h" "$(git log -1 --format='%s' "$h" 2>/dev/null)"
    done; echo
}

_handle_conflict() {
    local hash="$1" subj; subj="$(git log -1 --format='%s' "$hash" 2>/dev/null)"
    sg_error "Conflict cherry-picking: $hash ($subj)"; echo
    sg_info "Conflicting files:"
    git diff --name-only --diff-filter=U 2>/dev/null | while IFS= read -r f; do printf "    ${_C_RED}!${_C_RESET} %s\n" "$f"; done; echo
    echo "  1) Resolve in subshell  2) Accept theirs  3) Accept ours  4) Skip  5) Abort all"
    while true; do
        printf "  [1-5]: "; read -r c
        case "$c" in
            1)  sg_info "Type 'exit' when done resolving."
                ( export PS1="(conflict) \w \$ "; exec bash --norc --noprofile )
                git diff --name-only --diff-filter=U 2>/dev/null | grep -q . && { sg_warn "Still unresolved."; continue; }
                git cherry-pick --continue --no-edit 2>/dev/null || true; return 0 ;;
            2)  git checkout --theirs . 2>/dev/null; git add -A 2>/dev/null
                git cherry-pick --continue --no-edit 2>/dev/null || true
                sg_success "Resolved (theirs)."; return 0 ;;
            3)  git checkout --ours . 2>/dev/null; git add -A 2>/dev/null
                git cherry-pick --continue --no-edit 2>/dev/null || true
                sg_success "Resolved (ours)."; return 0 ;;
            4)  git cherry-pick --abort 2>/dev/null; sg_warn "Skipped."; return 1 ;;
            5)  git cherry-pick --abort 2>/dev/null; sg_warn "Aborted."; sg_undo_available; exit 1 ;;
            *)  sg_warn "Invalid." ;;
        esac
    done
}

_post_scan() {
    $OPT_NO_SCAN && return 0
    [[ "${SG_PRE_COMMIT_SCAN:-true}" != "true" ]] && return 0
    command -v securegit &>/dev/null || { sg_info "securegit not available; skipping scan."; return 0; }
    sg_info "Running post-pick security scan..."
    if _sg_cmd scan . 2>/dev/null; then sg_success "Scan passed."
    else sg_warn "Scan reported findings."; sg_confirm "Continue?" "n" || return 1; fi
}

_push_branch() {
    local br="$1" remote="${SG_PRIMARY_REMOTE:-origin}"
    sg_has_remote || { sg_warn "No remote."; return 0; }
    sg_info "Pushing $br to $remote..."
    _sg_cmd push -u "$remote" "$br" 2>/dev/null && sg_success "Pushed." || sg_error "Push failed."
}

_create_pr() {
    local br="$1" target="$2"
    command -v gh &>/dev/null || { sg_warn "'gh' not found. Create PR manually: $br -> $target"; return 0; }
    local title; title="$(sg_prompt "PR title" "hotfix: $br")"
    sg_info "Body (empty line to finish, Enter to skip):"; local body="" line
    while true; do read -r line; [[ -z "$line" ]] && break; [[ -n "$body" ]] && body+=$'\n'; body+="$line"; done
    [[ -z "$body" ]] && body="Hotfix cherry-pick: $br -> $target"
    gh pr create --base "$target" --head "$br" --title "$title" --body "$body" 2>/dev/null \
        && sg_success "PR created." || sg_error "PR creation failed."
}

# ─── Cherry-pick engine (shared by hotfix and pick) ─────────────────────────
_do_cherry_picks() {
    local picked=0 failed=0
    for hash in "${SELECTED_HASHES[@]}"; do
        local subj; subj="$(git log -1 --format='%s' "$hash" 2>/dev/null)"
        sg_info "Cherry-picking: $hash ($subj)"
        if git cherry-pick --no-edit "$hash" 2>/dev/null; then
            sg_success "Applied: $hash"; (( picked++ )) || true
        elif _handle_conflict "$hash"; then
            (( picked++ )) || true
        else
            (( failed++ )) || true
        fi
    done
    sg_header "Cherry-Pick Summary"
    printf "  ${_C_GREEN}Applied:${_C_RESET} %d\n" "$picked"
    (( failed > 0 )) && printf "  ${_C_RED}Failed: ${_C_RESET} %d\n" "$failed"
    echo
    (( picked == 0 )) && { sg_error "No commits applied."; sg_undo_available; return 1; }
    _post_scan || sg_warn "Continuing despite scan."
    return 0
}

# ─── Command: hotfix ─────────────────────────────────────────────────────────
_cmd_hotfix() {
    sg_header "Hotfix Workflow"
    # Select target branch
    local target="$OPT_TARGET"
    if [[ -z "$target" ]]; then
        local cands; cands="$(_find_release_branches)"
        [[ -n "$cands" ]] && { sg_info "Candidates:"; echo "$cands" | while IFS= read -r b; do printf "    %s\n" "$b"; done; echo; }
        target="$(sg_prompt "Target branch" "$(echo "$cands" | head -1)")"
    fi
    [[ -z "$target" ]] && sg_die "No target branch."
    sg_branch_exists "$target" || {
        local remote="${SG_PRIMARY_REMOTE:-origin}"
        git ls-remote --heads "$remote" "$target" 2>/dev/null | grep -q . || sg_die "Branch not found: $target"
        sg_info "Fetching $target..."; git fetch "$remote" "$target" 2>/dev/null
        git branch "$target" "$remote/$target" 2>/dev/null || true
    }
    # Select source and commits
    local source="${OPT_SOURCE:-$(sg_prompt "Source branch" "$(sg_current_branch)")}"
    SELECTED_HASHES=()
    if (( ${#POSITIONAL[@]} > 0 )); then
        for a in "${POSITIONAL[@]}"; do git rev-parse --verify "$a" &>/dev/null && SELECTED_HASHES+=("$a") || sg_warn "Invalid: $a"; done
    else
        _select_commits "$source" || sg_die "No commits."
    fi
    (( ${#SELECTED_HASHES[@]} == 0 )) && sg_die "No valid commits."
    # Hotfix branch
    local dflt="hotfix/$(date +%Y%m%d)-$(echo "${SELECTED_HASHES[0]}" | head -c 7)"
    local hfb; hfb="$(sg_prompt "Hotfix branch name" "$dflt")"
    # Confirm
    sg_divider
    printf "  ${_C_BOLD}Target:${_C_RESET} %s  ${_C_BOLD}Branch:${_C_RESET} %s  ${_C_BOLD}Commits:${_C_RESET} %d\n" "$target" "$hfb" "${#SELECTED_HASHES[@]}"
    for h in "${SELECTED_HASHES[@]}"; do printf "    ${_C_YELLOW}%s${_C_RESET} %s\n" "$h" "$(git log -1 --format='%s' "$h" 2>/dev/null)"; done; echo
    sg_confirm "Proceed?" || { sg_info "Aborted."; exit 0; }
    # Execute
    _create_snapshot "hotfix $source -> $target"
    if sg_branch_exists "$hfb"; then _sg_cmd checkout "$hfb" 2>/dev/null || git checkout "$hfb"
    else _sg_cmd checkout -b "$hfb" "$target" 2>/dev/null || git checkout -b "$hfb" "$target"; fi
    sg_success "On branch: $hfb"; echo
    _do_cherry_picks || { sg_undo_available; exit 1; }
    # Push / PR
    if $OPT_PUSH; then _push_branch "$hfb"
    elif sg_has_remote && sg_confirm "Push $hfb?"; then _push_branch "$hfb"; fi
    if $OPT_PR; then _push_branch "$hfb" 2>/dev/null; _create_pr "$hfb" "$target"
    elif sg_has_remote && sg_confirm "Create PR?" "n"; then _push_branch "$hfb" 2>/dev/null; _create_pr "$hfb" "$target"; fi
    sg_success "Hotfix workflow complete."; sg_undo_available
}

# ─── Command: pick ───────────────────────────────────────────────────────────
_cmd_pick() {
    sg_header "Cherry-Pick Commits"
    local current; current="$(sg_current_branch)"
    sg_is_protected "$current" && { sg_warn "Protected branch: $current"; sg_confirm "Pick onto $current?" "n" || exit 0; }
    local source="${OPT_SOURCE:-}"
    [[ -z "$source" ]] && {
        sg_info "Branches:"; git branch --format='%(refname:short)' 2>/dev/null | grep -v "^${current}$" | head -15 | while IFS= read -r b; do printf "    %s\n" "$b"; done; echo
        source="$(sg_prompt "Source branch")"
    }
    [[ -z "$source" ]] && sg_die "No source."
    SELECTED_HASHES=()
    if (( ${#POSITIONAL[@]} > 0 )); then
        for a in "${POSITIONAL[@]}"; do git rev-parse --verify "$a" &>/dev/null && SELECTED_HASHES+=("$a"); done
    else
        _select_commits "$source" || sg_die "No commits."
    fi
    (( ${#SELECTED_HASHES[@]} == 0 )) && sg_die "No valid commits."
    sg_confirm "Cherry-pick ${#SELECTED_HASHES[@]} commit(s) onto $current?" || exit 0
    _create_snapshot "cherry-pick onto $current"
    _do_cherry_picks; sg_undo_available
}

# ─── Command: list ───────────────────────────────────────────────────────────
_cmd_list() {
    local br="${OPT_SOURCE:-$(sg_prompt "Branch" "$(sg_current_branch)")}"
    sg_header "Commits on $br"
    local c=0
    while IFS='|' read -r h s d a; do
        [[ -z "$h" ]] && continue; (( c++ )) || true
        printf "  ${_C_BOLD}%2d)${_C_RESET} ${_C_YELLOW}%s${_C_RESET} %-45s ${_C_DIM}%s %s${_C_RESET}\n" "$c" "$h" "${s:0:45}" "$d" "$a"
    done < <(git log --format='%h|%s|%as|%an' -n 30 "$br" 2>/dev/null)
    echo; sg_info "$c commit(s). Use 'pick' or 'hotfix' to cherry-pick."
}

# ─── Command: status ─────────────────────────────────────────────────────────
_cmd_status() {
    sg_header "Cherry-Pick Status"
    local gd="$REPO_ROOT/.git"; [[ -f "$gd" ]] && gd="$(< "$gd")"; gd="${gd#gitdir: }"
    if [[ -f "$gd/CHERRY_PICK_HEAD" ]]; then
        local ph; ph="$(< "$gd/CHERRY_PICK_HEAD")"
        sg_warn "Cherry-pick in progress: $(echo "$ph" | head -c 7) $(git log -1 --format='%s' "$ph" 2>/dev/null)"
        local cf; cf="$(git diff --name-only --diff-filter=U 2>/dev/null)"
        [[ -n "$cf" ]] && { sg_info "Conflicts:"; echo "$cf" | while IFS= read -r f; do printf "    ${_C_RED}!${_C_RESET} %s\n" "$f"; done; } \
            || sg_info "Conflicts resolved. Run: git cherry-pick --continue"
    else
        sg_success "No cherry-pick in progress."
    fi
    sg_info "Branch: $(sg_current_branch)"
}

# ─── Command: abort ──────────────────────────────────────────────────────────
_cmd_abort() {
    sg_header "Abort Cherry-Pick"
    local gd="$REPO_ROOT/.git"; [[ -f "$gd" ]] && gd="$(< "$gd")"; gd="${gd#gitdir: }"
    [[ ! -f "$gd/CHERRY_PICK_HEAD" ]] && { sg_info "No cherry-pick in progress."; return 0; }
    sg_confirm "Abort?" && { git cherry-pick --abort 2>/dev/null; sg_success "Aborted."; sg_undo_available; }
}

# ─── Dispatch ────────────────────────────────────────────────────────────────
case "${COMMAND:-}" in
    hotfix) _cmd_hotfix ;; pick) _cmd_pick ;; list) _cmd_list ;;
    status) _cmd_status ;; abort) _cmd_abort ;;
    "")
        c="$(sg_menu "Cherry-Pick & Hotfix" \
            "Create hotfix (branch + pick + PR)" "Cherry-pick onto current branch" \
            "List commits from branch" "Cherry-pick status" "Abort cherry-pick" "Quit")"
        case "$c" in 1) _cmd_hotfix ;; 2) _cmd_pick ;; 3) _cmd_list ;; 4) _cmd_status ;; 5) _cmd_abort ;; 6) exit 0 ;; esac ;;
    *) sg_die "Unknown: $COMMAND. Use --help." ;;
esac
