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

# ─────────────────────────────────────────────────────────────────────────────
# 05-commit-craft.sh — Interactive Conventional Commit Message Builder
#
# Description:
#   Guides the user through crafting a well-formed conventional commit.
#   Type selection, scope, breaking changes, ticket extraction, security
#   scanning, diff size warnings, and AI-assisted message generation.
#
# Usage:
#   ./05-commit-craft.sh [--ai] [--no-scan] [--dry-run] [-h|--help]
# ─────────────────────────────────────────────────────────────────────────────

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

# ─── Argument parsing ───────────────────────────────────────────────────────
OPT_AI=false; OPT_NO_SCAN=false; OPT_DRY_RUN=false
while [[ $# -gt 0 ]]; do
    case "$1" in
        --ai)       OPT_AI=true ;;
        --no-scan)  OPT_NO_SCAN=true ;;
        --dry-run)  OPT_DRY_RUN=true ;;
        -h|--help)
            echo "Usage: $0 [--ai] [--no-scan] [--dry-run]"
            echo "  --ai        Use securegit commit --ai for AI-generated message"
            echo "  --no-scan   Skip pre-commit security scan"
            echo "  --dry-run   Build message without committing"
            exit 0 ;;
        *) sg_die "Unknown option: $1. Use --help for usage." ;;
    esac; shift
done

# ─── Pre-flight ──────────────────────────────────────────────────────────────
sg_require_repo
staged="$(sg_staged_files)"
[[ -z "$staged" ]] && { sg_error "No files staged. Use: git add <file> ..."; exit 1; }

# ─── Show staged summary ────────────────────────────────────────────────────
sg_header "Staged Changes"
file_count="$(echo "$staged" | wc -l)"
printf "  ${_C_BOLD}Files:${_C_RESET} %d\n" "$file_count"
echo "$staged" | while IFS= read -r f; do printf "    ${_C_GREEN}+${_C_RESET} %s\n" "$f"; done
echo; sg_divider
diff_stat="$(git diff --cached --stat 2>/dev/null | tail -1)"
[[ -n "$diff_stat" ]] && printf "  ${_C_DIM}%s${_C_RESET}\n\n" "$diff_stat"

# ─── Check diff size (sg_count_lines) ────────────────────────────────────────
max_lines="${SG_MAX_DIFF_LINES:-200}"
insertions="$(git diff --cached --numstat 2>/dev/null | awk '{s+=$1} END {print s+0}')"
deletions="$(git diff --cached --numstat 2>/dev/null | awk '{s+=$2} END {print s+0}')"
total_changed=$(( insertions + deletions ))
if (( total_changed > max_lines )); then
    sg_warn "This commit touches $total_changed lines (threshold: $max_lines). Consider splitting."
    sg_confirm "Continue with this large commit?" || { sg_info "Aborted."; exit 0; }
    echo
fi

# ─── Security scan (sg_scan_staged) ──────────────────────────────────────────
if [[ "$OPT_NO_SCAN" != "true" ]] && [[ "${SG_PRE_COMMIT_SCAN:-true}" == "true" ]]; then
    sg_divider
    if ! sg_scan_staged; then
        sg_confirm "Security issues found. Commit anyway?" "n" || { sg_info "Aborted."; exit 1; }
        sg_warn "Proceeding despite security findings."
    fi
    echo
fi

# ─── AI commit path ─────────────────────────────────────────────────────────
if [[ "$OPT_AI" == "true" ]] && sg_confirm "Use AI to generate the commit message?"; then
    if $OPT_DRY_RUN; then sg_info "[dry-run] Would run: securegit commit --ai"; exit 0; fi
    if _sg_cmd commit --ai; then
        sg_success "Commit created with AI-generated message."; sg_undo_available; exit 0
    fi
    sg_warn "AI commit failed. Falling back to manual builder."
    echo
fi

# ─── Step 1: Select commit type (sg_conventional_types) ─────────────────────
declare -A _type_desc=([feat]="A new feature" [fix]="A bug fix" [docs]="Documentation only"
    [style]="Formatting changes" [refactor]="Neither fix nor feature" [perf]="Performance"
    [test]="Adding/correcting tests" [build]="Build system/deps" [ci]="CI config"
    [chore]="Maintenance" [revert]="Revert a commit")
IFS=' ' read -ra _types <<< "$(sg_conventional_types)"

sg_header "Commit Type"
for i in "${!_types[@]}"; do
    printf "  ${_C_BOLD}%2d)${_C_RESET} %-10s ${_C_DIM}%s${_C_RESET}\n" \
        "$((i+1))" "${_types[$i]}" "${_type_desc[${_types[$i]}]:-}"
done; echo

COMMIT_TYPE=""
while [[ -z "$COMMIT_TYPE" ]]; do
    printf "Select type [1-%d] or name: " "${#_types[@]}"; read -r _choice
    if [[ "$_choice" =~ ^[0-9]+$ ]] && (( _choice >= 1 && _choice <= ${#_types[@]} )); then
        COMMIT_TYPE="${_types[$((_choice-1))]}"
    else
        for t in "${_types[@]}"; do [[ "$_choice" == "$t" ]] && COMMIT_TYPE="$t"; done
    fi
    [[ -z "$COMMIT_TYPE" ]] && sg_warn "Invalid selection."
done
sg_success "Type: $COMMIT_TYPE"; echo

# ─── Step 2: Select scope ───────────────────────────────────────────────────
COMMIT_SCOPE=""
if [[ -n "${SG_COMMIT_SCOPES:-}" ]]; then
    IFS=' ' read -ra _scopes <<< "$SG_COMMIT_SCOPES"
    _opts=("(none)" "${_scopes[@]}" "(custom)")
    sg_header "Commit Scope"
    for i in "${!_opts[@]}"; do
        printf "  ${_C_BOLD}%2d)${_C_RESET} %s\n" "$((i+1))" "${_opts[$i]}"
    done; echo
    printf "Select scope [1-%d]: " "${#_opts[@]}"; read -r _c
    if [[ "$_c" =~ ^[0-9]+$ ]] && (( _c >= 1 && _c <= ${#_opts[@]} )); then
        _sel="${_opts[$((_c-1))]}"
        case "$_sel" in
            "(none)")   COMMIT_SCOPE="" ;;
            "(custom)") COMMIT_SCOPE="$(sg_prompt "Enter scope")" ;;
            *)          COMMIT_SCOPE="$_sel" ;;
        esac
    fi
else
    COMMIT_SCOPE="$(sg_prompt "Scope (optional, Enter to skip)")"
fi
[[ -n "$COMMIT_SCOPE" ]] && sg_success "Scope: $COMMIT_SCOPE" || sg_info "Scope: (none)"
echo

# ─── Step 3: Breaking change ────────────────────────────────────────────────
BREAKING=false
sg_confirm "Is this a breaking change?" "n" && BREAKING=true
$BREAKING && sg_warn "Marked as BREAKING CHANGE." || sg_info "Not a breaking change."
echo

# ─── Step 4: Subject ────────────────────────────────────────────────────────
sg_header "Commit Subject"
sg_info "Short imperative description (max ~72 chars). e.g.: add auth middleware"
COMMIT_SUBJECT=""
while [[ -z "$COMMIT_SUBJECT" ]]; do
    COMMIT_SUBJECT="$(sg_prompt "Subject")"
    [[ -z "$COMMIT_SUBJECT" ]] && sg_warn "Subject cannot be empty."
done
# Length check
_pfx_len=$(( ${#COMMIT_TYPE} + 2 ))
[[ -n "$COMMIT_SCOPE" ]] && _pfx_len=$(( _pfx_len + ${#COMMIT_SCOPE} + 2 ))
$BREAKING && _pfx_len=$(( _pfx_len + 1 ))
(( _pfx_len + ${#COMMIT_SUBJECT} > 72 )) && sg_warn "Header is $(( _pfx_len + ${#COMMIT_SUBJECT} )) chars (72 recommended)."
echo

# ─── Step 5: Body (optional) ────────────────────────────────────────────────
sg_info "Body (optional). Empty line to finish, Enter to skip."
COMMIT_BODY=""; _first=true
while true; do
    read -r _line
    [[ -z "$_line" ]] && break
    _first=false
    [[ -n "$COMMIT_BODY" ]] && COMMIT_BODY+=$'\n'
    COMMIT_BODY+="$_line"
done; echo

# ─── Step 6: Ticket (sg_ticket_from_branch) ─────────────────────────────────
COMMIT_TICKET=""
_auto_ticket="$(sg_ticket_from_branch)"
if [[ -n "$_auto_ticket" ]]; then
    sg_info "Detected ticket from branch: $_auto_ticket"
    sg_confirm "Include '$_auto_ticket'?" && COMMIT_TICKET="$_auto_ticket"
fi
if [[ -z "$COMMIT_TICKET" ]]; then
    if [[ "${SG_REQUIRE_TICKET:-false}" == "true" ]]; then
        _pat="${SG_TICKET_PATTERN:-[A-Z]+-[0-9]+}"
        while true; do
            COMMIT_TICKET="$(sg_prompt "Ticket reference (required, e.g. PROJ-123)")"
            [[ -n "$COMMIT_TICKET" ]] && echo "$COMMIT_TICKET" | grep -qP "$_pat" && break
            sg_warn "Ticket must match: $_pat"
        done
    else
        COMMIT_TICKET="$(sg_prompt "Ticket reference (optional, Enter to skip)")"
    fi
fi
echo

# ─── Step 7: Breaking change description ────────────────────────────────────
BREAKING_DESC=""
$BREAKING && { sg_info "Describe the breaking change (optional):"; read -r BREAKING_DESC; }

# ─── Assemble message ───────────────────────────────────────────────────────
_header="$COMMIT_TYPE"
[[ -n "$COMMIT_SCOPE" ]] && _header+="(${COMMIT_SCOPE})"
$BREAKING && _header+="!"
_header+=": $COMMIT_SUBJECT"
FULL_MESSAGE="$_header"
[[ -n "$COMMIT_BODY" ]] && FULL_MESSAGE+=$'\n\n'"$COMMIT_BODY"
_footers=""
$BREAKING && _footers="BREAKING CHANGE: ${BREAKING_DESC:-$COMMIT_SUBJECT}"
[[ -n "$COMMIT_TICKET" ]] && { [[ -n "$_footers" ]] && _footers+=$'\n'; _footers+="Refs: $COMMIT_TICKET"; }
[[ -n "$_footers" ]] && FULL_MESSAGE+=$'\n\n'"$_footers"

# ─── Preview ─────────────────────────────────────────────────────────────────
sg_header "Commit Message Preview"; sg_divider
printf "${_C_BOLD}%s${_C_RESET}\n" "$(echo "$FULL_MESSAGE" | head -1)"
_rest="$(echo "$FULL_MESSAGE" | tail -n +2)"
[[ -n "$_rest" ]] && echo "$_rest"
sg_divider; echo

# Offer AI alternative if securegit available
if command -v securegit &>/dev/null; then
    if sg_confirm "See an AI-generated alternative instead?" "n"; then
        if $OPT_DRY_RUN; then sg_info "[dry-run] Would run: securegit commit --ai"; exit 0; fi
        _sg_cmd commit --ai && { sg_success "Done."; sg_undo_available; exit 0; }
        sg_warn "AI commit failed. Using manual message."
    fi
fi

# ─── Commit ──────────────────────────────────────────────────────────────────
if $OPT_DRY_RUN; then
    sg_info "[dry-run] Would create commit with message above."; exit 0
fi
sg_confirm "Create this commit?" || { sg_info "Aborted."; exit 0; }
sg_info "Creating commit..."
if _sg_cmd commit -m "$FULL_MESSAGE"; then
    sg_success "Commit created: $(git rev-parse --short HEAD) $(git log -1 --format='%s')"
    sg_undo_available
else
    sg_die "Commit failed."
fi
