#!/usr/bin/env bash
# lib/common.sh — shared helpers for shell scripts and hooks in this repo.
#
# Source it from any script:
#   #!/usr/bin/env bash
#   set -euo pipefail
#   HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
#   # shellcheck source=lib/common.sh
#   source "$HERE/lib/common.sh"   # or the appropriate relative path
#
# This library exists to drive a known cross-project inconsistency to zero:
# every repo used to re-paste the ANSI color block, redefine log_* helpers,
# pick one of three incompatible shebangs, and roll its own docker wrapper.
# Import this once instead.

# Do NOT set -euo pipefail here unconditionally — some callers (git hooks)
# source this file and rely on controlling their own shell options. We only
# guarantee the functions below are defined.

###############################################################################
# Config — override via environment before sourcing if needed
###############################################################################
: "${TEMPLATE_ROOT:=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
export TEMPLATE_ROOT

###############################################################################
# ANSI colors (defined once, used everywhere)
###############################################################################
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
    RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
    BLUE='\033[0;34m'; BOLD='\033[1m'; NC='\033[0m'
else
    RED=''; GREEN=''; YELLOW=''; BLUE=''; BOLD=''; NC=''
fi
export RED GREEN YELLOW BLUE BOLD NC

###############################################################################
# Logging
###############################################################################
log_info()  { printf "${BLUE}›${NC} %s\n" "$*"; }
log_ok()    { printf "${GREEN}✓${NC} %s\n" "$*"; }
log_warn()  { printf "${YELLOW}⚠${NC} %s\n" "$*" >&2; }
log_error() { printf "${RED}✗${NC} %s\n" "$*" >&2; }
log_step()  { printf "\n${BOLD}== %s ==${NC}\n" "$*"; }

die() { log_error "$*"; exit 1; }

###############################################################################
# Predicates
###############################################################################
# have <cmd> — return 0 if <cmd> is on PATH
have() { command -v "$1" >/dev/null 2>&1; }

###############################################################################
# Path helpers
###############################################################################
repo_root() {
    # Prefer git's notion of the repo root, fall back to $TEMPLATE_ROOT, then pwd.
    if git rev-parse --show-toplevel >/dev/null 2>&1; then
        git rev-parse --show-toplevel
    else
        printf '%s\n' "${TEMPLATE_ROOT:-$(pwd)}"
    fi
}

###############################################################################
# Privilege helpers
###############################################################################
# as_root — run the remaining args as root via sudo, or directly if already root.
as_root() {
    if [ "$(id -u)" -eq 0 ]; then "$@"; else sudo "$@"; fi
}

###############################################################################
# Docker wrapper
###############################################################################
# docker_run <image> <args...>
# Ephemeral container, host-uid ownership, repo mounted at /data, cwd /data.
# Drives the "host stays clean; everything runs in containers" policy and
# ensures output files are owned by the invoking user, not root.
docker_run() {
    [ "$#" -ge 1 ] || die "docker_run: image required"
    local image="$1"; shift
    have docker || die "docker not found on PATH"
    local root
    root="$(repo_root)"
    docker run --rm \
        --user "$(id -u):$(id -g)" \
        -e HOME=/tmp \
        -v "$root:/data" \
        -w /data \
        "$image" "$@"
}

###############################################################################
# Rule-audit accumulator (used by scripts/check-rules.sh)
# Globals read/written: RULE_PASS RULE_WARN RULE_FAIL
###############################################################################
init_counters() { RULE_PASS=0; RULE_WARN=0; RULE_FAIL=0; }

# check <description> <pass|warn|fail>
check() {
    local desc="$1" result="$2"
    case "$result" in
        pass)
            RULE_PASS=$((RULE_PASS + 1))
            if [ "${RULE_VERBOSE:-true}" = true ]; then printf "  ${GREEN}PASS${NC}  %s\n" "$desc"; fi
            ;;
        warn)
            RULE_WARN=$((RULE_WARN + 1))
            if [ "${RULE_VERBOSE:-true}" = true ]; then printf "  ${YELLOW}WARN${NC}  %s\n" "$desc"; fi
            ;;
        fail)
            RULE_FAIL=$((RULE_FAIL + 1))
            printf "  ${RED}FAIL${NC}  %s\n" "$desc"
            ;;
        *)
            die "check(): invalid result '$result' (use pass|warn|fail)"
            ;;
    esac
}

# print_summary_and_exit
print_summary_and_exit() {
    if [ "${RULE_VERBOSE:-true}" = true ]; then
        printf "\n=== Summary ===\n  PASS: %s\n  WARN: %s\n  FAIL: %s\n\n" \
            "$RULE_PASS" "$RULE_WARN" "$RULE_FAIL"
    fi
    if [ "$RULE_FAIL" -gt 0 ]; then
        if [ "${RULE_VERBOSE:-true}" = true ]; then
            printf "AUDIT FAILED — %s rule(s) violated.\n" "$RULE_FAIL"
        fi
        exit 1
    fi
    if [ "${RULE_VERBOSE:-true}" = true ]; then printf "AUDIT PASSED.\n"; fi
    exit 0
}
