#!/usr/bin/env bash
# Common utilities for gitlink-cli workflow scripts

set -euo pipefail
# Trap SIGPIPE to prevent premature exit when piping through head/truncate
trap '' PIPE

# Ensure jq is available (WinGet installs to non-default PATH on Windows)
if ! command -v jq &>/dev/null; then
    for d in "$LOCALAPPDATA/Microsoft/WinGet/Links" "$HOME/AppData/Local/Microsoft/WinGet/Links"; do
        [[ -d "$d" ]] && export PATH="$d:$PATH"
    done
fi

# Ensure CLAUDE_CODE_GIT_BASH_PATH is set for Windows (needed by claude CLI)
if [[ -z "${CLAUDE_CODE_GIT_BASH_PATH:-}" ]] && command -v cygpath &>/dev/null; then
    export CLAUDE_CODE_GIT_BASH_PATH="$(cygpath -w "$(which bash)")"
fi

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'

# ── Logging ──────────────────────────────────────────────────────────
log_step() { echo -e "${BLUE}[STEP]${NC} $*"; }
log_ok()   { echo -e "${GREEN}[  OK]${NC} $*"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
log_err()  { echo -e "${RED}[ ERR]${NC} $*" >&2; }
log_info() { echo -e "${CYAN}[INFO]${NC} $*"; }
log_title(){ echo -e "\n${BOLD}══════ $* ══════${NC}\n"; }

# ── Auth Check ───────────────────────────────────────────────────────
check_auth() {
    # Check env var first, then try CLI auth status
    if [[ -n "${GITLINK_TOKEN:-}" ]]; then
        log_ok "GITLINK_TOKEN is set"
        return 0
    fi
    local status
    status=$(gitlink-cli auth status 2>&1)
    if echo "$status" | grep -qi "logged in\|✓"; then
        log_ok "Authenticated: $(echo "$status" | sed -n 's/.*as //p' | tr -d '[:space:]')"
        return 0
    fi
    log_err "Not authenticated. Please login first:"
    log_info "  gitlink-cli auth login"
    log_info "  export GITLINK_TOKEN=\"your-private-token\""
    exit 1
}

# ── JSON Helpers ─────────────────────────────────────────────────────
# Extract a field from CLI JSON output (Envelope: {ok, data, ...})
json_ok() {
    echo "$1" | jq -r '.ok // false' 2>/dev/null
}

json_data() {
    echo "$1" | jq -r '.data' 2>/dev/null
}

json_get() {
    echo "$1" | jq -r "$2" 2>/dev/null
}

json_error() {
    echo "$1" | jq -r '.error.message // "unknown error"' 2>/dev/null
}

# ── CLI Wrapper ──────────────────────────────────────────────────────
GL="gitlink-cli"

gl_run() {
    local output
    # Always use JSON format for scripting
    output=$("$GL" "$@" --format json 2>&1) || true
    echo "$output"
}

gl_check() {
    local output
    output=$(gl_run "$@")
    # Check if output is valid JSON (use here-string to avoid SIGPIPE)
    if ! jq empty <<< "$output" 2>/dev/null; then
        log_err "Command failed (non-JSON response): $GL $*"
        log_err "$output"
        return 1
    fi
    if [[ "$(json_ok "$output")" != "true" ]]; then
        log_err "Command failed: $GL $*"
        log_err "$(json_error "$output")"
        return 1
    fi
    echo "$output"
}

# ── Owner/Repo Detection ────────────────────────────────────────────
detect_owner_repo() {
    local remote_url
    remote_url=$(git remote get-url origin 2>/dev/null || echo "")
    if [[ -z "$remote_url" ]]; then
        log_err "No git remote 'origin' found. Use --owner and --repo flags."
        exit 1
    fi
    # Parse gitlink URL patterns
    # https://gitlink.org.cn/owner/repo.git or git@gitlink.org.cn:owner/repo.git
    if [[ "$remote_url" =~ gitlink\.org\.cn[:/]([^/]+)/([^/.]+) ]]; then
        DETECTED_OWNER="${BASH_REMATCH[1]}"
        DETECTED_REPO="${BASH_REMATCH[2]}"
    else
        log_err "Cannot parse owner/repo from remote: $remote_url"
        exit 1
    fi
}

require_owner_repo() {
    if [[ -z "${OWNER:-}" || -z "${REPO:-}" ]]; then
        detect_owner_repo
        OWNER="${OWNER:-$DETECTED_OWNER}"
        REPO="${REPO:-$DETECTED_REPO}"
    fi
    log_info "Using: ${OWNER}/${REPO}"
}

# ── Confirmation ─────────────────────────────────────────────────────
confirm() {
    local msg="${1:-Proceed?}"
    if [[ "${DRY_RUN:-false}" == "true" ]]; then
        log_warn "[DRY RUN] Would execute: $msg"
        return 1
    fi
    read -rp "$(echo -e "${YELLOW}$msg [y/N]${NC} ")" answer
    [[ "$answer" =~ ^[Yy] ]]
}

# ── Date Helpers ─────────────────────────────────────────────────────
date_today() {
    date +%Y-%m-%d
}

date_week_ago() {
    date -d "7 days ago" +%Y-%m-%d 2>/dev/null || date -v-7d +%Y-%m-%d 2>/dev/null
}

date_month_ago() {
    date -d "30 days ago" +%Y-%m-%d 2>/dev/null || date -v-30d +%Y-%m-%d 2>/dev/null
}

# ── Parameter Parsing ────────────────────────────────────────────────
parse_common_args() {
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --owner)   OWNER="$2"; shift 2 ;;
            --repo)    REPO="$2"; shift 2 ;;
            --dry-run) DRY_RUN="true"; shift ;;
            --help|-h) usage; exit 0 ;;
            *)         break ;;
        esac
    done
}

# ── Section Divider ──────────────────────────────────────────────────
divider() {
    echo -e "${CYAN}────────────────────────────────────────────────${NC}"
}

# ══════════════════════════════════════════════════════════════════════
# Scientific Research Helpers
# ══════════════════════════════════════════════════════════════════════

# Normalize value against max (returns 0-1, 0 if max is 0)
normalize() {
    awk -v val="$1" -v max="$2" 'BEGIN { printf "%.4f", (max > 0) ? val / max : 0 }'
}

# Clamp value between lo and hi
clamp() {
    awk -v val="$1" -v lo="$2" -v hi="$3" 'BEGIN { printf "%.4f", (val < lo) ? lo : ((val > hi) ? hi : val) }'
}

# Weighted sum: pass pairs of "value weight" as arguments
weighted_sum() {
    awk 'BEGIN { sum=0; for(i=1;i<ARGC;i+=2) sum+=(ARGV[i]*ARGV[i+1]); printf "%.4f", sum }' "$@"
}

# Jaccard similarity between two space-separated strings
jaccard() {
    echo "$1 $2" | awk '{
        n1 = split($1, a1, " "); n2 = split($2, a2, " ");
        delete seen; inter = 0;
        for (i in a1) seen[a1[i]] = 1;
        for (i in a2) if (seen[a2[i]] == 1) { inter++; seen[a2[i]] = 2; }
        union = 0;
        for (i in seen) union++;
        printf "%.4f", (union > 0) ? inter / union : 0;
    }'
}

# Detect programming language from file extension
detect_lang_from_ext() {
    local ext="${1##*.}"
    case "$ext" in
        go)       echo "Go" ;;
        py|pyx)   echo "Python" ;;
        js|ts|jsx|tsx|mjs|cjs) echo "JavaScript/TypeScript" ;;
        rs)       echo "Rust" ;;
        java)     echo "Java" ;;
        kt|kts)   echo "Kotlin" ;;
        c|cpp|cxx|h|hpp|hxx) echo "C/C++" ;;
        r|R)      echo "R" ;;
        jl)       echo "Julia" ;;
        m|mm)     echo "MATLAB/Objective-C" ;;
        swift)    echo "Swift" ;;
        rb)       echo "Ruby" ;;
        php)      echo "PHP" ;;
        scala)    echo "Scala" ;;
        dart)     echo "Dart" ;;
        lua)      echo "Lua" ;;
        ipynb)    echo "Jupyter Notebook" ;;
        sh|bash|zsh) echo "Shell" ;;
        ps1|psm1|psd1) echo "PowerShell" ;;
        *)        echo "Other" ;;
    esac
}

# Cross-platform days between two dates (YYYY-MM-DD format)
days_between() {
    local d1 d2 diff
    d1=$(date -d "$1" +%s 2>/dev/null || date -jf "%Y-%m-%d" "$1" +%s 2>/dev/null || echo "0")
    d2=$(date -d "$2" +%s 2>/dev/null || date -jf "%Y-%m-%d" "$2" +%s 2>/dev/null || echo "0")
    diff=$(( (d2 - d1) / 86400 ))
    echo "${diff#-}"
}

# Get today, N days ago (cross-platform)
date_days_ago() {
    local n="$1"
    date -d "$n days ago" +%Y-%m-%d 2>/dev/null || date -v-"$n"d +%Y-%m-%d 2>/dev/null
}

# Extract first N space-separated authors into BibTeX format
# Input: "First Last" "First2 Last2" ...
format_authors_bibtex() {
    local names="$1" count=0 result=""
    for name in $names; do
        count=$((count + 1))
        if [[ $count -gt 10 ]]; then
            result="${result} and others"
            break
        fi
        local last="${name##* }" first="${name%% *}"
        [[ $count -gt 1 ]] && result="${result} and "
        result="${result}${last}, ${first}"
    done
    echo "$result"
}

# Extract organization name from login (try git remote or repo +info)
org_from_owner() {
    local owner="$1"
    # check if it's an org or user by listing repos
    local out
    out=$(gl_run repo +list --user "$owner" --limit 1)
    if [[ "$(json_ok "$out")" == "true" ]]; then
        echo "$owner"
    else
        echo ""
    fi
}

# Min and max helpers for awk
min_val() { awk -v a="$1" -v b="$2" 'BEGIN { print (a < b) ? a : b }'; }
max_val() { awk -v a="$1" -v b="$2" 'BEGIN { print (a > b) ? a : b }'; }

# ── Knowledge Graph Helpers ──────────────────────────────────────────

# Generate a unique node ID
kg_node_id() { echo "${1}:${2}" | tr '/' '_' | tr ' ' '_'; }

# URL-encode a string (basic)
url_encode() {
    local str="$1"
    echo "$str" | jq -sRr @uri 2>/dev/null || echo "$str"
}

# Escape JSON string value
json_escape() {
    echo "$1" | jq -Rsa . 2>/dev/null || echo "\"$1\""
}

# ── Color-coded Severity ──────────────────────────────────────────────

severity_color() {
    case "$1" in
        Critical|critical|CRITICAL) echo -e "${RED}$1${NC}" ;;
        Warning|warning|WARNING)    echo -e "${YELLOW}$1${NC}" ;;
        Info|info|INFO)             echo -e "${CYAN}$1${NC}" ;;
        OK|ok|CLEAN)                echo -e "${GREEN}$1${NC}" ;;
        *)                          echo "$1" ;;
    esac
}
