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

# ============================================================================
# Kubernetes Controller for REMOTE JAVA (JaCoCo) COVERAGE
# Token + Tokenless (managed identity / default kubeconfig) compatible
#
# START:
#   ./k8s_controller_coverage.sh start <api> <token> <ns> <pod|pod-regex|label> <value> \
#       <mode:auto|manual|explicit> <processNameOrPid> <jacocoExecPath> <startCmd> <stopCmd> [container]
#
# STOP:
#   ./k8s_controller_coverage.sh stop <api> <token> <ns> <pod|pod-regex|label> <value> \
#       <mode:auto|manual|explicit> <processNameOrPid> <jacocoExecPath> <includeCSV> <uuid> <stopCmd> \
#       [sourceCSV] [container]
#
# Notes:
# - If <token> is empty ("") => uses current kubectl auth context (managed identity / kubeconfig)
# - Reports fetched per pod to:  $NS_WDIR/logs/coverage/<uuid>/<pod>/
# - Exec files fetched per pod to: $NS_WDIR/logs/codeAnalyzer/<uuid>/<uuid>_<pod>.exec
# ============================================================================

ACTION="${1:-}"
shift 2>/dev/null || true

NS_WDIR="${NS_WDIR:-$(pwd)}"
KUBECTL_BIN="${KUBECTL_BIN:-kubectl}"
COVERAGE_BASE_PATH="${COVERAGE_BASE_PATH:-}"

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

AGENT_LOCAL="${AGENT_LOCAL:-$SCRIPT_DIR/remote_covk8s_agent.sh}"
JACOCOCLI_SRC="${JACOCOCLI_SRC:-$NS_WDIR/thirdparty/gui_signed_jars/jacococli.jar}"

KUBECFG_OUT="${KUBECFG_OUT:-$SCRIPT_DIR/kubeconfig_token.yaml}"
CA_OUT="${CA_OUT:-$SCRIPT_DIR/apiserver-ca.crt}"

# ---------------- logging (similar to your .NET controller) ----------------
LOG_DIR="$NS_WDIR/logs/coverage/Code_coverage_logs"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/java_code_coverage_$(date +%Y%m%d).log"

ts(){ date +"%Y-%m-%d %H:%M:%S"; }
_log_write(){ local lvl="$1"; shift; printf "%s|%s|UUID=%s|%s\n" "$(ts)" "$lvl" "${UUID:-NA}" "$*" >>"$LOG_FILE"; }
log_info(){ _log_write "INFO" "$*"; }
log_warn(){ _log_write "WARN" "$*"; }
log_error(){ _log_write "ERROR" "$*"; }

die(){ log_error "$*"; echo "[ERROR] $*" >&2; exit 1; }
say(){ echo -e "$*"; }
need(){ command -v "$1" >/dev/null 2>&1 || die "'$1' is required"; }

need "$KUBECTL_BIN"
need openssl
need gzip

USE_TOKEN=0

# ---------------- kubeconfig (token + tokenless) ----------------
setup_kubeconfig() {
  local api="$1" token="$2" ns="$3"
  local hostport="${api#https://}"

  if [[ -z "${token:-}" ]]; then
    USE_TOKEN=0
    log_info "AuthenticationMethod=ManagedIdentity Namespace=$ns API=$api"
    return 0
  fi

  USE_TOKEN=1
  log_info "AuthenticationMethod=Token-based Namespace=$ns API=$api"

  # best effort CA fetch (same style as your .NET controller; still skip verify)
  openssl s_client -connect "$hostport" -showcerts </dev/null 2>/dev/null \
    | openssl x509 -outform PEM > "$CA_OUT" || die "Failed to fetch API cert from $hostport"

  cat > "$KUBECFG_OUT" <<EOF
apiVersion: v1
kind: Config
clusters:
- name: remote
  cluster:
    server: $api
    insecure-skip-tls-verify: true
users:
- name: sa-user
  user:
    token: $token
contexts:
- name: ctx
  context:
    cluster: remote
    user: sa-user
    namespace: $ns
current-context: ctx
EOF
}

k() {
  if [[ "${USE_TOKEN:-0}" -eq 1 ]]; then
    "$KUBECTL_BIN" --kubeconfig="$KUBECFG_OUT" "$@"
  else
    "$KUBECTL_BIN" "$@"
  fi
}

# ---------------- pods ----------------
get_pods() {
  local ns="$1" kind="$2" value="$3"
  log_info "GetPods|Namespace=$ns SelectorType=$kind SelectorValue=$value"

  case "$kind" in
    pod|pod-regex)
      if [[ "$value" =~ [\.\*\^\$\[\]\(\)\|\+\?] ]]; then
        say "[INFO] Using pod regex: $value"
        k -n "$ns" get pods --no-headers -o custom-columns=":metadata.name" | grep -E "^${value}$" || true
      else
        k -n "$ns" get pod "$value" -o name 2>/dev/null | awk -F/ '{print $2}' || true
      fi
      ;;
    label)
      k -n "$ns" get pods -l "$value" -o jsonpath='{.items[*].metadata.name}' || true
      ;;
    *)
      die "Invalid pod selector: $kind (use pod|pod-regex|label)"
      ;;
  esac
}

detect_container() {
  local ns="$1" pod="$2"
  local names
  names="$(k -n "$ns" get pod "$pod" -o jsonpath='{.spec.containers[*].name}')"
  # Prefer non-sidecar-ish container if multiple
  local arr=()
  read -r -a arr <<< "$names"
  if [[ "${#arr[@]}" -eq 1 ]]; then echo "${arr[0]}"; return 0; fi
  for n in "${arr[@]}"; do
    [[ "$n" != *proxy* && "$n" != *sidecar* && "$n" != *istio* ]] && echo "$n" && return 0
  done
  echo "${arr[0]}"
}

# ---------------- agent install ----------------
install_agent() {
  local ns="$1" pod="$2" container="$3"

  [[ -f "$AGENT_LOCAL" ]] || die "Missing remote agent: $AGENT_LOCAL"
  [[ -f "$JACOCOCLI_SRC" ]] || die "Missing jacococli.jar: $JACOCOCLI_SRC"

  log_info "InstallAgent|pod=$pod container=$container"

  # Find a writable base dir (same logic style as .NET controller)
  local TOOLS_BASE
  TOOLS_BASE="$(k -n "$ns" exec "$pod" -c "$container" -- sh -lc '
    for d in "'"$COVERAGE_BASE_PATH"'" "$HOME" /tmp /var/tmp; do
      [ -n "$d" ] || continue
      mkdir -p "$d/.coverage_tools" 2>/dev/null && echo "$d" && exit 0
    done
    exit 1
  ')" || die "No writable directory found in pod=$pod"

  local TOOLS_DIR="$TOOLS_BASE/.coverage_tools"
  log_info "InstallAgent|tools_base=$TOOLS_BASE tools_dir=$TOOLS_DIR pod=$pod"

  k -n "$ns" cp "$AGENT_LOCAL" "$pod:$TOOLS_DIR/remote_covk8s_agent.sh" -c "$container" \
    || die "Failed to copy remote agent to pod=$pod"
  k -n "$ns" exec "$pod" -c "$container" -- sh -lc "chmod +x '$TOOLS_DIR/remote_covk8s_agent.sh'" \
    || die "Failed to chmod agent in pod=$pod"

  k -n "$ns" cp "$JACOCOCLI_SRC" "$pod:$TOOLS_DIR/jacococli.jar" -c "$container" \
    || die "Failed to copy jacococli.jar to pod=$pod"

  echo "$TOOLS_DIR"
}

detect_tools_dir() {
  local ns="$1" pod="$2" container="$3"
  k -n "$ns" exec "$pod" -c "$container" -- sh -lc '
    for d in "'"$COVERAGE_BASE_PATH"'" "$HOME" /tmp /var/tmp; do
      [ -n "$d" ] || continue
      if [ -x "$d/.coverage_tools/remote_covk8s_agent.sh" ]; then
        echo "$d/.coverage_tools"
        exit 0
      fi
    done
    exit 1
  '
}

# ---------------- fetch HTML report per pod ----------------
fetch_html_report() {
  local ns="$1" pod="$2" uuid="$3" container="$4" tools_dir="$5"

  local dest="$NS_WDIR/logs/coverage/$uuid/$pod"
  mkdir -p "$dest"

  local remote_tar="$tools_dir/report-$uuid/html/report_${uuid}.tar.gz"
  local local_tar="$dest/report_${uuid}.tar.gz"

  log_info "FetchHTML|pod=$pod uuid=$uuid remote_tar=$remote_tar local_tar=$local_tar"

  k -n "$ns" cp -c "$container" "$pod:$remote_tar" "$local_tar" \
    || { log_warn "FetchHTML|TarNotFound|pod=$pod uuid=$uuid"; return 1; }

  ( cd "$dest" && tar -xzf "$(basename "$local_tar")" ) || die "Failed to untar HTML for pod=$pod"
  rm -f "$local_tar"
  say "✅ HTML extracted: $dest/index.html"
  return 0
}

# ---------------- copy exec (binary) per pod with retries ----------------
copy_exec_from_pod() {
  local ns="$1" pod="$2" uuid="$3" container="$4" tools_dir="$5"

  local out_dir="$NS_WDIR/logs/codeAnalyzer/$uuid"
  mkdir -p "$out_dir"

  local remote_exec="$tools_dir/report_${uuid}.exec"
  local local_exec="$out_dir/${uuid}_${pod}.exec"
  local tmp_gz="$local_exec.tmp.gz"
  local tmp_bin="$local_exec.tmp"

  log_info "CopyExec|pod=$pod uuid=$uuid remote_exec=$remote_exec local_exec=$local_exec"

  local i=1
  local retries="${RETRIES:-5}"
  while [[ $i -le $retries ]]; do
    rm -f "$tmp_gz" "$tmp_bin" 2>/dev/null || true

    # stream gzip from pod (more reliable than kubectl cp for big binaries)
    if k -n "$ns" exec "$pod" -c "$container" -- sh -lc "test -f '$remote_exec' && gzip -c '$remote_exec'" \
        >"$tmp_gz" 2>/dev/null \
      && gzip -dc "$tmp_gz" >"$tmp_bin" 2>/dev/null; then

      # basic sanity: file not empty
      if [[ -s "$tmp_bin" ]]; then
        mv "$tmp_bin" "$local_exec"
        rm -f "$tmp_gz" 2>/dev/null || true
        say "✅ Exec copied: $local_exec"
        log_info "CopyExec|Success|pod=$pod file=$local_exec"
        return 0
      fi
    fi

    log_warn "CopyExec|Retry|pod=$pod attempt=$i/$retries"
    sleep $((2*i))
    i=$((i+1))
  done

  log_warn "CopyExec|Failed|pod=$pod uuid=$uuid (exec may not exist)"
  return 1
}

# ============================================================================
# START
# ============================================================================
if [[ "$ACTION" == "start" ]]; then
  [[ $# -ge 10 ]] || die "Usage: start <api> <token> <ns> <pod|pod-regex|label> <value> <mode> <process> <exec> <startCmd> <stopCmd> [container]"

  API="$1"; TOKEN="$2"; NS="$3"; KIND="$4"; VAL="$5"
  MODE="$6"; PROCESS="$7"
  JACOCO_EXEC_PATH="$8"
  START_CMD="$9"
  STOP_CMD="${10}"
  CONTAINER="${11:-}"

  UUID="NA"
  export UUID

  setup_kubeconfig "$API" "$TOKEN" "$NS" || die "setup_kubeconfig failed"

  log_info "START|Namespace=$NS SelectorType=$KIND SelectorValue=$VAL Mode=$MODE Process=$PROCESS Exec=$JACOCO_EXEC_PATH"

  PODS="$(get_pods "$NS" "$KIND" "$VAL")"
  [[ -n "${PODS// }" ]] || die "No pods found for kind=$KIND value=$VAL in ns=$NS"

  ok=0; fail=0
  for P in $PODS; do
    say "---- START pod=$P ----"
    log_info "START|Pod=$P"

    C="$CONTAINER"; [[ -z "$C" ]] && C="$(detect_container "$NS" "$P")"
    tools_dir="$(install_agent "$NS" "$P" "$C")" || { fail=$((fail+1)); continue; }

    # Run remote agent start (POSIX sh agent)
    set +e
    k -n "$NS" exec "$P" -c "$C" -- sh -lc \
      "TOOLS_DIR='$tools_dir' JACOCOCLI='$tools_dir/jacococli.jar' '$tools_dir/remote_covk8s_agent.sh' start '$MODE' '$PROCESS' '$JACOCO_EXEC_PATH' '$START_CMD' '$STOP_CMD'"
    rc=$?
    set -e

    if [[ $rc -ne 0 ]]; then
      say "[ERROR] Start failed in pod=$P (rc=$rc)"
      log_error "START|Failed|pod=$P rc=$rc"
      fail=$((fail+1))
      continue
    fi

    say "✅ Coverage start triggered in pod=$P"
    log_info "START|Success|pod=$P"
    ok=$((ok+1))
  done

  say "[DONE] Start completed. Success=$ok Failed=$fail"
  log_info "START|Completed Success=$ok Failed=$fail"
  [[ $ok -gt 0 ]] || exit 1
  exit 0
fi

# ============================================================================
# STOP
# ============================================================================
if [[ "$ACTION" == "stop" ]]; then
  [[ $# -ge 11 ]] || die "Usage: stop <api> <token> <ns> <pod|pod-regex|label> <value> <mode> <process> <exec> <includeCSV> <uuid> <stopCmd> [sourceCSV] [container]"

  API="$1"; TOKEN="$2"; NS="$3"; KIND="$4"; VAL="$5"
  MODE="$6"; PROCESS="$7"
  JACOCO_EXEC_PATH="$8"
  INCLUDE_CSV="$9"
  UUID="${10}"
  STOP_CMD="${11}"
  SOURCE_CSV="${12:-}"
  CONTAINER="${13:-}"

  export UUID

  setup_kubeconfig "$API" "$TOKEN" "$NS" || die "setup_kubeconfig failed"

  log_info "STOP|Namespace=$NS SelectorType=$KIND SelectorValue=$VAL Mode=$MODE Process=$PROCESS UUID=$UUID"

  PODS="$(get_pods "$NS" "$KIND" "$VAL")"
  [[ -n "${PODS// }" ]] || die "No pods found for kind=$KIND value=$VAL in ns=$NS"

  ok=0; fail=0
  for P in $PODS; do
    say "---- STOP pod=$P ----"
    log_info "STOP|Pod=$P"

    C="$CONTAINER"; [[ -z "$C" ]] && C="$(detect_container "$NS" "$P")"

    tools_dir="$(detect_tools_dir "$NS" "$P" "$C")" || {
      say "[ERROR] Coverage agent not found in pod=$P"
      log_error "DetectToolsDir|NotFound|pod=$P"
      fail=$((fail+1))
      continue
    }

    # Run remote agent stop (always pass SOURCE_CSV to keep arg positions stable)
    set +e
    k -n "$NS" exec "$P" -c "$C" -- sh -lc \
      "TOOLS_DIR='$tools_dir' JACOCOCLI='$tools_dir/jacococli.jar' '$tools_dir/remote_covk8s_agent.sh' stop '$MODE' '$PROCESS' '$JACOCO_EXEC_PATH' '$INCLUDE_CSV' '$UUID' '$STOP_CMD' '$SOURCE_CSV'"
    rc=$?
    set -e

    if [[ $rc -ne 0 && $rc -ne 143 ]]; then
      say "[ERROR] Stop failed in pod=$P (rc=$rc)"
      log_error "STOP|Failed|pod=$P rc=$rc"
      fail=$((fail+1))
      continue
    fi

    # Fetch exec + HTML per pod (no overwrites)
    copy_exec_from_pod "$NS" "$P" "$UUID" "$C" "$tools_dir" || true
    fetch_html_report "$NS" "$P" "$UUID" "$C" "$tools_dir" || true

    say "✅ Coverage collected from pod=$P"
    log_info "STOP|Success|pod=$P"
    ok=$((ok+1))
  done

  say "[DONE] Stop completed. Success=$ok Failed=$fail"
  say "[OK] HTML root : $NS_WDIR/logs/coverage/$UUID/"
  say "[OK] EXEC root : $NS_WDIR/logs/codeAnalyzer/$UUID/"
  log_info "STOP|Completed Success=$ok Failed=$fail UUID=$UUID"
  [[ $ok -gt 0 ]] || exit 1
  exit 0
fi

die "Invalid action (use start|stop)"

