#!/usr/bin/env bash
# securegit-common.sh — Shared library for SecureGit workflow scripts
# Source this file from any workflow: source "$(dirname "$0")/lib/securegit-common.sh"
set -euo pipefail

# ─── Guard against double-sourcing ──────────────────────────────────────────
[[ -n "${_SG_COMMON_LOADED:-}" ]] && return 0
_SG_COMMON_LOADED=1

# ─── Resolve paths ──────────────────────────────────────────────────────────
SG_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SG_WORKFLOWS_DIR="$(cd "$SG_LIB_DIR/.." && pwd)"

# ─── Color / formatting ────────────────────────────────────────────────────
if [[ -t 1 ]] && [[ "${NO_COLOR:-}" != "1" ]]; then
    _C_RED='\033[0;31m'    _C_GREEN='\033[0;32m'  _C_YELLOW='\033[0;33m'
    _C_BLUE='\033[0;34m'   _C_CYAN='\033[0;36m'   _C_BOLD='\033[1m'
    _C_DIM='\033[2m'       _C_RESET='\033[0m'
else
    _C_RED='' _C_GREEN='' _C_YELLOW='' _C_BLUE='' _C_CYAN='' _C_BOLD='' _C_DIM='' _C_RESET=''
fi

# ─── Output helpers ─────────────────────────────────────────────────────────
sg_success() { printf "${_C_GREEN}✓${_C_RESET} %s\n" "$*"; }
sg_error()   { printf "${_C_RED}✗${_C_RESET} %s\n" "$*" >&2; }
sg_warn()    { printf "${_C_YELLOW}!${_C_RESET} %s\n" "$*" >&2; }
sg_info()    { printf "${_C_CYAN}→${_C_RESET} %s\n" "$*"; }
sg_header()  { printf "\n${_C_BOLD}${_C_BLUE}━━━ %s ━━━${_C_RESET}\n\n" "$*"; }
sg_divider() { printf "${_C_DIM}────────────────────────────────────────${_C_RESET}\n"; }

sg_die() {
    sg_error "$@"
    exit 1
}

# ─── Config loading ─────────────────────────────────────────────────────────
# Loading order (later overrides earlier):
#   1. workflows/securegit-workflows.conf  (shipped defaults)
#   2. ~/.config/securegit/workflows.conf  (user global)
#   3. .securegit/workflows.conf           (per-project)
#   4. Environment variables               (runtime override)
sg_load_config() {
    local repo_root
    repo_root="$(git rev-parse --show-toplevel 2>/dev/null || echo ".")"

    # 1. Shipped defaults
    local shipped="$SG_WORKFLOWS_DIR/securegit-workflows.conf"
    [[ -f "$shipped" ]] && source "$shipped"

    # 2. User global
    local user_global="${XDG_CONFIG_HOME:-$HOME/.config}/securegit/workflows.conf"
    [[ -f "$user_global" ]] && source "$user_global"

    # 3. Per-project
    local project="$repo_root/.securegit/workflows.conf"
    [[ -f "$project" ]] && source "$project"

    # 4. Environment variables override everything (already in env)
}

# ─── Project / language detection ───────────────────────────────────────────
sg_detect_language() {
    # If configured, use that
    [[ -n "${SG_LANGUAGE:-}" ]] && { echo "$SG_LANGUAGE"; return; }

    local root
    root="$(git rev-parse --show-toplevel 2>/dev/null || echo ".")"

    if [[ -f "$root/Cargo.toml" ]]; then echo "rust"
    elif [[ -f "$root/go.mod" ]]; then echo "go"
    elif [[ -f "$root/pyproject.toml" ]] || [[ -f "$root/setup.py" ]] || [[ -f "$root/requirements.txt" ]]; then echo "python"
    elif [[ -f "$root/package.json" ]]; then echo "js"
    elif [[ -f "$root/pom.xml" ]] || [[ -f "$root/build.gradle" ]] || [[ -f "$root/build.gradle.kts" ]]; then echo "java"
    elif [[ -f "$root/Gemfile" ]]; then echo "ruby"
    elif [[ -f "$root/composer.json" ]]; then echo "php"
    elif [[ -f "$root/CMakeLists.txt" ]] || [[ -f "$root/Makefile" ]] && ls "$root"/*.c "$root"/**/*.c 2>/dev/null | head -1 >/dev/null; then echo "c"
    else echo "generic"
    fi
}

sg_detect_test_cmd() {
    [[ -n "${SG_TEST_CMD:-}" ]] && { echo "$SG_TEST_CMD"; return; }
    case "$(sg_detect_language)" in
        rust)    echo "cargo test" ;;
        go)      echo "go test ./..." ;;
        python)  if command -v pytest &>/dev/null; then echo "pytest"; else echo "python -m unittest discover"; fi ;;
        js)
            local root; root="$(git rev-parse --show-toplevel 2>/dev/null || echo ".")"
            if [[ -f "$root/vitest.config.ts" ]] || [[ -f "$root/vitest.config.js" ]]; then echo "npx vitest run"
            elif grep -q '"jest"' "$root/package.json" 2>/dev/null; then echo "npx jest"
            elif grep -q '"mocha"' "$root/package.json" 2>/dev/null; then echo "npx mocha"
            else echo "npm test"
            fi ;;
        java)
            local root; root="$(git rev-parse --show-toplevel 2>/dev/null || echo ".")"
            if [[ -f "$root/gradlew" ]]; then echo "./gradlew test"
            elif [[ -f "$root/mvnw" ]]; then echo "./mvnw test"
            elif [[ -f "$root/pom.xml" ]]; then echo "mvn test"
            else echo "gradle test"
            fi ;;
        ruby)    echo "bundle exec rspec" ;;
        php)     echo "vendor/bin/phpunit" ;;
        c)       echo "make test" ;;
        *)       echo "echo 'No test command detected'" ;;
    esac
}

sg_detect_lint_cmd() {
    [[ -n "${SG_LINT_CMD:-}" ]] && { echo "$SG_LINT_CMD"; return; }
    case "$(sg_detect_language)" in
        rust)    echo "cargo clippy -- -D warnings" ;;
        go)      echo "golangci-lint run" ;;
        python)  if command -v ruff &>/dev/null; then echo "ruff check ."; else echo "flake8"; fi ;;
        js)
            local root; root="$(git rev-parse --show-toplevel 2>/dev/null || echo ".")"
            if [[ -f "$root/biome.json" ]] || [[ -f "$root/biome.jsonc" ]]; then echo "npx biome check"
            elif compgen -G "$root/.eslintrc*" &>/dev/null || compgen -G "$root/eslint.config*" &>/dev/null; then echo "npx eslint ."
            else echo "echo 'No linter detected'"
            fi ;;
        java)    echo "echo 'No linter detected'" ;;
        ruby)    echo "bundle exec rubocop" ;;
        php)     echo "vendor/bin/phpstan analyse" ;;
        c)       echo "echo 'No linter detected'" ;;
        *)       echo "echo 'No linter detected'" ;;
    esac
}

sg_detect_fmt_cmd() {
    [[ -n "${SG_FMT_CMD:-}" ]] && { echo "$SG_FMT_CMD"; return; }
    case "$(sg_detect_language)" in
        rust)    echo "cargo fmt -- --check" ;;
        go)      echo "gofmt -l ." ;;
        python)  if command -v ruff &>/dev/null; then echo "ruff format --check ."; else echo "black --check ."; fi ;;
        js)
            local root; root="$(git rev-parse --show-toplevel 2>/dev/null || echo ".")"
            if [[ -f "$root/biome.json" ]] || [[ -f "$root/biome.jsonc" ]]; then echo "npx biome format --check ."
            elif compgen -G "$root/.prettierrc*" &>/dev/null || compgen -G "$root/prettier.config*" &>/dev/null; then echo "npx prettier --check ."
            else echo "echo 'No formatter detected'"
            fi ;;
        java)    echo "echo 'No formatter detected'" ;;
        ruby)    echo "bundle exec rubocop --format simple" ;;
        php)     echo "vendor/bin/php-cs-fixer fix --dry-run --diff" ;;
        c)       echo "echo 'No formatter detected'" ;;
        *)       echo "echo 'No formatter detected'" ;;
    esac
}

sg_detect_build_cmd() {
    [[ -n "${SG_BUILD_CMD:-}" ]] && { echo "$SG_BUILD_CMD"; return; }
    case "$(sg_detect_language)" in
        rust)    echo "cargo build" ;;
        go)      echo "go build ./..." ;;
        python)  echo "echo 'No build step required'" ;;
        js)
            local root; root="$(git rev-parse --show-toplevel 2>/dev/null || echo ".")"
            if grep -q '"build"' "$root/package.json" 2>/dev/null; then echo "npm run build"
            else echo "echo 'No build step required'"
            fi ;;
        java)
            local root; root="$(git rev-parse --show-toplevel 2>/dev/null || echo ".")"
            if [[ -f "$root/gradlew" ]]; then echo "./gradlew build"
            elif [[ -f "$root/mvnw" ]]; then echo "./mvnw package"
            elif [[ -f "$root/pom.xml" ]]; then echo "mvn package"
            else echo "gradle build"
            fi ;;
        ruby)    echo "echo 'No build step required'" ;;
        php)     echo "echo 'No build step required'" ;;
        c)       echo "make" ;;
        *)       echo "echo 'No build step required'" ;;
    esac
}

# ─── Git helpers ────────────────────────────────────────────────────────────
sg_require_repo() {
    git rev-parse --is-inside-work-tree &>/dev/null \
        || sg_die "Not inside a git repository."
}

sg_require_clean() {
    local status
    status="$(git status --porcelain 2>/dev/null)"
    if [[ -n "$status" ]]; then
        sg_die "Working tree has uncommitted changes. Commit or stash first."
    fi
}

sg_current_branch() {
    git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD
}

sg_default_branch() {
    if [[ -n "${SG_DEFAULT_BRANCH:-}" ]]; then
        echo "$SG_DEFAULT_BRANCH"
        return
    fi
    # Try to detect from remote
    local remote="${SG_PRIMARY_REMOTE:-origin}"
    local ref
    ref="$(git symbolic-ref "refs/remotes/$remote/HEAD" 2>/dev/null || true)"
    if [[ -n "$ref" ]]; then
        echo "${ref##*/}"
    else
        # Fallback: check common names
        for b in main master; do
            if git show-ref --verify --quiet "refs/heads/$b" 2>/dev/null; then
                echo "$b"
                return
            fi
        done
        echo "main"
    fi
}

sg_is_protected() {
    local branch="$1"
    local protected="${SG_PROTECTED_BRANCHES:-main master develop staging production}"
    local b
    for b in $protected; do
        [[ "$branch" == "$b" ]] && return 0
    done
    return 1
}

# ─── Interactive helpers ────────────────────────────────────────────────────
sg_confirm() {
    local prompt="${1:-Continue?}"
    local default="${2:-y}"
    local yn

    if [[ "$default" == "y" ]]; then
        printf "%s [Y/n] " "$prompt"
    else
        printf "%s [y/N] " "$prompt"
    fi

    read -r yn
    yn="${yn:-$default}"
    [[ "$yn" =~ ^[Yy] ]]
}

sg_prompt() {
    local prompt="$1"
    local default="${2:-}"
    local result

    if [[ -n "$default" ]]; then
        printf "%s [%s]: " "$prompt" "$default"
    else
        printf "%s: " "$prompt"
    fi

    read -r result
    echo "${result:-$default}"
}

sg_menu() {
    local title="$1"
    shift
    local options=("$@")
    local i

    sg_header "$title"
    for i in "${!options[@]}"; do
        printf "  ${_C_BOLD}%d)${_C_RESET} %s\n" "$((i + 1))" "${options[$i]}"
    done
    echo

    local choice
    while true; do
        printf "Select [1-%d]: " "${#options[@]}"
        read -r choice
        if [[ "$choice" =~ ^[0-9]+$ ]] && (( choice >= 1 && choice <= ${#options[@]} )); then
            echo "$choice"
            return
        fi
        sg_warn "Invalid selection. Try again."
    done
}

# ─── SecureGit integration ──────────────────────────────────────────────────
_sg_cmd() {
    # Use securegit if available, otherwise fall back to git
    if command -v securegit &>/dev/null; then
        securegit "$@"
    else
        # For git-native commands, fall back; for securegit-specific, warn
        case "${1:-}" in
            scan|snapshot|hook|backup|acquire|pre-commit|pre-push)
                sg_warn "securegit not found — '${1}' requires securegit. Install from: https://github.com/jfrog/securegit"
                return 1
                ;;
            *)
                git "$@"
                ;;
        esac
    fi
}

sg_scan_staged() {
    sg_info "Scanning staged changes for security issues..."
    if _sg_cmd pre-commit 2>/dev/null; then
        sg_success "Security scan passed."
        return 0
    else
        sg_error "Security scan found issues. Review findings before committing."
        return 1
    fi
}

sg_snapshot_create() {
    local msg="${1:-auto-snapshot}"
    if command -v securegit &>/dev/null; then
        sg_info "Creating snapshot: $msg"
        securegit stash save "$msg" 2>/dev/null && securegit stash pop 2>/dev/null || true
    fi
}

sg_undo_available() {
    if command -v securegit &>/dev/null; then
        sg_info "Tip: Run 'securegit undo' to reverse the last operation."
    fi
}

# ─── Utility ────────────────────────────────────────────────────────────────
sg_count_lines() {
    git diff --cached --stat 2>/dev/null | tail -1 | grep -oP '\d+ insertion' | grep -oP '\d+' || echo 0
}

sg_staged_files() {
    git diff --cached --name-only 2>/dev/null
}

sg_branch_exists() {
    git show-ref --verify --quiet "refs/heads/$1" 2>/dev/null
}

sg_remote_exists() {
    git remote | grep -qx "$1" 2>/dev/null
}

sg_has_remote() {
    [[ -n "$(git remote 2>/dev/null)" ]]
}

sg_ticket_from_branch() {
    local branch pattern
    branch="$(sg_current_branch)"
    pattern="${SG_TICKET_PATTERN:-[A-Z]+-[0-9]+}"
    echo "$branch" | grep -oP "$pattern" | head -1 || true
}

sg_conventional_types() {
    echo "${SG_COMMIT_TYPES:-feat fix docs style refactor perf test build ci chore revert}"
}

sg_branch_prefixes() {
    echo "${SG_BRANCH_PREFIXES:-feature bugfix hotfix release experiment refactor docs test chore}"
}

# ─── Init: load config on source ────────────────────────────────────────────
sg_load_config
