#!/bin/sh
set -eu

# ============================================================================
# Kubernetes Controller for REMOTE .NET COVERAGE
# Alpine + Ubuntu compatible (POSIX sh)
#
# Usage:
#   start <api> <token> <ns> <pod|label> <value> <appPath> [outdir] [container] [pattern]
#   stop  <api> <token> <ns> <pod|label> <value> <uuid>    [outdir] [container]
# ============================================================================

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

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

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
. "$SCRIPT_DIR/strip_reportgenerator_promos.sh"
COVERAGE_CONFIG_LOCAL="$SCRIPT_DIR/coverage.config"
AGENT_LOCAL="$SCRIPT_DIR/remote_dotnet_cov_agent.sh"
KUBECFG_OUT="$SCRIPT_DIR/kubeconfig_token.yaml"
CA_OUT="$SCRIPT_DIR/apiserver-ca.crt"

# ---------------- logging (ADDED, does not modify existing lines) ----------------
# Create log directory and basic logging helpers (lines added, original content unchanged)
LOG_DIR="$NS_WDIR/logs/coverage/Code_coverage_logs"
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/code_coverage_$(date +%Y%m%d).log"

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

# ---------------- helpers ----------------
die(){ echo "[ERROR] $*" >&2; exit 1; }
say(){ echo "$*"; }
need(){ command -v "$1" >/dev/null 2>&1 || die "'$1' is required"; }
sq(){ printf "%s" "$1" | sed "s/'/'\\\\''/g"; }

need "$KUBECTL_BIN"
need openssl

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

  # ADDED: log auth method discovery (inserted, original lines below left intact)
  if [ -z "$token" ]; then
    log_info "AuthenticationMethod=ManagedIdentity Namespace=$ns API=$api"
    USE_TOKEN=0

    return 0

  fi

  USE_TOKEN=1

  # ADDED: log token-based auth
  log_info "AuthenticationMethod=Token-based Auth Namespace=$ns API=$api"

  # best-effort CA fetch (kept from your script)
  openssl s_client -connect "$hostport" -showcerts </dev/null 2>/dev/null | \
    openssl x509 -outform PEM > "$CA_OUT" || die "Failed to fetch CA"

  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(){ "$KUBECTL_BIN" --kubeconfig="$KUBECFG_OUT" "$@"; }
k(){
  if [ "${USE_TOKEN:-0}" -eq 1 ]; then
    "$KUBECTL_BIN" --kubeconfig="$KUBECFG_OUT" "$@"
  else
    "$KUBECTL_BIN" "$@"
  fi
}
# ---------------- pods ----------------
get_pods() {
  ns="$1"; kind="$2"; value="$3"

  # ADDED: log pod fetch request
  log_info "GetPods|Namespace=$ns SelectorType=$kind SelectorValue=$value"

  case "$kind" in
    pod|pod-regex)
      case "$value" in
        *[\.\*\^\$\[\]\(\)\|\+\?]*)
          echo "[INFO] Using pod regex: $value" >&2
          k -n "$ns" get pods --no-headers \
            -o custom-columns=":metadata.name" | \
            grep -E "^$value$" || true
          ;;
        *)
          k -n "$ns" get pod "$value" -o name 2>/dev/null | \
            awk -F/ '{print $2}'
          ;;
      esac
      ;;
    label)
      k -n "$ns" get pods -l "$value" \
        -o jsonpath='{.items[*].metadata.name}'
      ;;
    *)
      die "Invalid pod selector: $kind (use pod|label)"
      ;;
  esac
}
# ADDED: after get_pods we cannot change logic above; log results where get_pods is called

detect_container() {
  ns="$1"; pod="$2"
  names="$(k -n "$ns" get pod "$pod" -o jsonpath='{.spec.containers[*].name}')"
  set -- $names
  echo "$1"
}


install_agent() {
  ns="$1"; pod="$2"; container="$3"

  # ADDED: log agent install attempt
  log_info "InstallAgent|pod=$pod container=$container"

  if [ ! -f "$AGENT_LOCAL" ]; then
    say "[ERROR] Missing remote_dotnet_cov_agent.sh"
    return 1
  fi

  # Ask pod to find a writable base directory
  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
  ')" || {
    say "[ERROR] No writable directory found in pod=$pod"
    return 1
  }

  say "[INFO] Using writable base path=$TOOLS_BASE for pod=$pod"
  TOOLS_DIR="$TOOLS_BASE/.coverage_tools"

  # Copy agent
  k -n "$ns" cp "$AGENT_LOCAL" \
    "$pod:$TOOLS_DIR/remote_dotnet_cov_agent.sh" \
    -c "$container" || return 1

  # Make executable
  k -n "$ns" exec "$pod" -c "$container" -- sh -lc \
    "chmod +x '$TOOLS_DIR/remote_dotnet_cov_agent.sh'" || return 1
  # Copy coverage config (auto)
  if [ -f "$COVERAGE_CONFIG_LOCAL" ]; then
    k -n "$ns" cp "$COVERAGE_CONFIG_LOCAL" \
      "$pod:$TOOLS_DIR/coverage.config" \
      -c "$container" || return 1
    log_info "InstallAgent|coverage.config copied to pod=$pod path=$TOOLS_DIR/coverage.config"
  else
    log_warn "InstallAgent|coverage.config not found at $COVERAGE_CONFIG_LOCAL (continuing without it)"
  fi

  POD_TOOLS_BASE="$TOOLS_BASE"
  export POD_TOOLS_BASE
}


detect_tools_base() {
  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_dotnet_cov_agent.sh" ]; then
        echo "$d"
        exit 0
      fi
    done
    exit 1
  '
}


# ---------------- detached start + verify ----------------
verify_started() {
  ns="$1"; pod="$2"; container="$3"; outdir="${4:-/tmp/coverage}"

  # ADDED: log verify action
  log_info "VerifyStarted|pod=$pod container=$container outdir=$outdir"

  k -n "$ns" exec "$pod" -c "$container" -- sh -lc "
    out='$(sq "$outdir")'
    [ -f \"\$out/collector.pid\" ] || exit 1
    cp=\$(cat \"\$out/collector.pid\" 2>/dev/null || true)
    [ -n \"\$cp\" ] || exit 1
    kill -0 \"\$cp\" 2>/dev/null || exit 1

    # app pid is best-effort
    if [ -f \"\$out/app.pid\" ]; then
      ap=\$(cat \"\$out/app.pid\" 2>/dev/null || true)
      [ -n \"\$ap\" ] && kill -0 \"\$ap\" 2>/dev/null || true
    fi
  " >/dev/null 2>&1
}

start_detached() {
  # Signature (matches your call):
  # start_detached ns pod container tools_dir app_path mode start_cmd stop_cmd app_pattern outdir
  ns="$1"; pod="$2"; container="$3"; tools_dir="$4"
  app_path="$5"; mode="${6:-auto}"
  start_cmd="${7:-}"; stop_cmd="${8:-}"
  app_pattern="${9:-}"
  outdir="${10:-/tmp/coverage}"

  [ -n "$outdir" ] || outdir="/tmp/coverage"

  # ADDED: log start_detached invocation
  log_info "StartDetached|pod=$pod container=$container app_path=$app_path mode=$mode outdir=$outdir"

  k -n "$ns" exec "$pod" -c "$container" -- sh -lc "
    out='$(sq "$outdir")'
    mkdir -p \"\$out/logs\" \"\$out/html\" >/dev/null 2>&1 || true

    APP_PATH='$(sq "$app_path")' \
    MODE='$(sq "$mode")' \
    START_CMD='$(sq "$start_cmd")' \
    STOP_CMD='$(sq "$stop_cmd")' \
    APP_PATTERN='$(sq "$app_pattern")' \
    COVERAGE_OUTDIR='$(sq "$outdir")' \
    COVERAGE_SETTINGS_FILE='$(sq "$tools_dir/coverage.config")' \
      nohup '$(sq "$tools_dir")/remote_dotnet_cov_agent.sh' start \
        >\"\$out/logs/controller_start.log\" 2>&1 </dev/null &
  " >/dev/null 2>&1
}

# ---------------- copy HTML report ----------------
fetch_report() {
  ns="$1"; pod="$2"; uuid="$3"; container="$4"; outdir="$5"

  # ADDED: log fetch_report attempt
  log_info "FetchReport|pod=$pod uuid=$uuid container=$container outdir=$outdir"

  DEST="$NS_WDIR/logs/coverage"
  mkdir -p "$DEST"

  TAR_NAME="$uuid.tar.gz"

  # Copy tar from pod
  k -n "$ns" cp -c "$container" \
    "$pod:$outdir/html/$TAR_NAME" \
    "$DEST/$TAR_NAME" || die "Failed to copy report tar"

  # Untar locally
  tar -xzf "$DEST/$TAR_NAME" -C "$DEST"

  # Optional cleanup
  rm -f "$DEST/$TAR_NAME"
}

# ---------------- copy cobertura.xml ----------------
fetch_xml() {
  ns="$1"; pod="$2"; uuid="$3"; container="$4"; outdir="$5"
  DEST="$NS_WDIR/logs/codeAnalyzer"
  mkdir -p "$DEST"

  # ADDED: log fetch_xml attempt
  log_info "FetchXML|pod=$pod uuid=$uuid container=$container outdir=$outdir"

  k -n "$ns" cp -c "$container" \
    "$pod:$outdir/coverage.cobertura.xml" \
    "$DEST/report_$uuid.xml" 2>/dev/null || \
    say "[WARN] coverage.cobertura.xml not found in pod=$pod"
}

copy_xml_from_pod() {
  ns="$1"; pod="$2"; uuid="$3"; container="$4"; outdir="$5"

  # ADDED: log copy_xml start
  log_info "CopyXMLFromPod|pod=$pod uuid=$uuid container=$container outdir=$outdir"

  DEST="$NS_WDIR/logs/codeAnalyzer/$uuid"
  mkdir -p "$DEST"
  OUT="$DEST/${uuid}_${pod}.xml"
  TMP="$OUT.tmp"
  TMPGZ="$OUT.tmp.gz"

  # Get expected size (best-effort)
  expected="$(
    k -n "$ns" exec "$pod" -c "$container" -- sh -lc \
      "wc -c < '$outdir/coverage.cobertura.xml' 2>/dev/null | tr -d ' ' || true" \
      2>/dev/null || true
  )"

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

    # gzip stream (smaller => fewer resets)
    if k -n "$ns" exec "$pod" -c "$container" -- sh -lc \
        "gzip -c '$outdir/coverage.cobertura.xml'" > "$TMPGZ" 2>/dev/null \
      && gzip -dc "$TMPGZ" > "$TMP" 2>/dev/null; then

      actual="$(wc -c < "$TMP" | tr -d ' ')"

      # size check if we have it
      if [ -n "${expected:-}" ] && [ "$expected" -gt 0 ] 2>/dev/null; then
        if [ "$actual" -ne "$expected" ]; then
          echo "[WARN] XML size mismatch (expected=$expected got=$actual), retry..."
          i=$((i+1)); sleep $((2*i)); continue
        fi
      fi

      # sanity check
      if grep -q "<coverage" "$TMP" && tail -n 2 "$TMP" | grep -q "</coverage>"; then
        mv "$TMP" "$OUT"
        rm -f "$TMPGZ" 2>/dev/null || true
        echo "[OK] XML copied: $OUT"
        # ADDED: log successful copy
        log_info "CopyXMLFromPod|Success|pod=$pod out=$OUT"
        return 0
      fi

      echo "[WARN] XML sanity check failed, retry..."
    else
      echo "[WARN] Streaming failed, retry..."
    fi

    i=$((i+1))
    sleep $((2*i))
  done

  echo "[ERROR] Failed to copy XML from pod=$pod"
  # ADDED: log failure to copy XML
  log_error "CopyXMLFromPod|Failed|pod=$pod uuid=$uuid"
  return 1
}


finalize_xml() {
  uuid="$1"

  # ADDED: log finalize start
  log_info "FinalizeXML|uuid=$uuid"

  SRC_DIR="$NS_WDIR/logs/codeAnalyzer/$uuid"
  FINAL_XML="$NS_WDIR/logs/codeAnalyzer/report_$uuid.xml"

  FILE_COUNT="$(ls "$SRC_DIR"/*.xml 2>/dev/null | wc -l)"

  [ "$FILE_COUNT" -ge 1 ] || die "No XML files found for UUID=$uuid"

  if [ "$FILE_COUNT" -eq 1 ]; then
    cp "$SRC_DIR"/*.xml "$FINAL_XML"
  else
    need reportgenerator
    reportgenerator \
      -reports:"$SRC_DIR/*.xml" \
      -targetdir:"$SRC_DIR/merged_tmp" \
      -reporttypes:Cobertura \
      >/dev/null 2>&1 || die "XML merge failed"

    cp "$SRC_DIR/merged_tmp/Cobertura.xml" "$FINAL_XML"
    rm -rf "$SRC_DIR/merged_tmp"
  fi
}



generate_html_controller() {
  uuid="$1"
  source_path="$2"
  use_source="$3"

  # ADDED: log HTML generation start
  log_info "GenerateHTML|uuid=$uuid use_source=$use_source source_path=$source_path"

  need reportgenerator

  SRC_XML="$NS_WDIR/logs/codeAnalyzer/report_$uuid.xml"
  DEST="$NS_WDIR/logs/coverage/$uuid"

  mkdir -p "$DEST"

  if [ "$use_source" = true ]; then
    reportgenerator \
      -reports:"$SRC_XML" \
      -targetdir:"$DEST" \
      -sourcedirs:"$source_path" \
      -title:"Coverage Report - $uuid" \
      >/dev/null 2>&1 || die "HTML report generation failed (with source)"
  else
    reportgenerator \
      -reports:"$SRC_XML" \
      -targetdir:"$DEST" \
      -title:"Coverage Report - $uuid" \
      >/dev/null 2>&1 || die "HTML report generation failed (summary)"
  fi

  [ -f "$DEST/index.html" ] || die "index.html not generated"
  # ADDED: log HTML generated
  log_info "GenerateHTML|Completed|uuid=$uuid index=$DEST/index.html"
}

# ============================================================================
# START
# ============================================================================
if [ "$cmd" = "start" ]; then
  [ $# -ge 6 ] || die "Usage: start <api> <token> <ns> <pod|label> <value> <appPath> [outdir] [container] [pattern]"

  API="$1"; TOKEN="$2"; NS="$3"; KIND="$4"; VAL="$5"
  APP_PATH="$6"
  MODE="$7"
  START_CMD="${8:-}"
  STOP_CMD="${9:-}"
  # ✅ Correct mapping: [outdir] [container] [pattern]
  REMOTE_OUTDIR="${10:-/tmp/coverage}"
  CONTAINER="${11:-}"
  APP_PATTERN="${12:-}"
  setup_kubeconfig "$API" "$TOKEN" "$NS"

  # ADDED: log start high-level
  log_info "START|Application=$APP_PATH ServerType=k8s AppType=Dotnet AuthenticationMethod=${AUTH_METHOD:-Unknown} Namespace=$NS SelectorType=$KIND SelectorValue=$VAL Mode=${MODE:-auto} RemoteOutDir=$REMOTE_OUTDIR"

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

START_OK=0
START_FAIL=0

for P in $PODS; do
  say "---- START pod=$P ----"
  # ADDED: log per-pod start attempt
  log_info "START|Pod=$P"

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

  if ! install_agent "$NS" "$P" "$C"; then
    say "[ERROR] Agent install failed in pod=$P"
    log_error "InstallAgent|Failed|pod=$P"
    START_FAIL=$((START_FAIL+1))
    continue
  fi

  TOOLS_DIR="$POD_TOOLS_BASE/.coverage_tools"

  if ! start_detached "$NS" "$P" "$C" "$TOOLS_DIR" "$APP_PATH" "$MODE" "$START_CMD" "$STOP_CMD" "$APP_PATTERN" "$REMOTE_OUTDIR"; then
    say "[ERROR] Failed to launch start in pod=$P"
    log_error "StartDetached|Failed|pod=$P"
    START_FAIL=$((START_FAIL+1))
    continue
  fi

  i=0
  while [ $i -lt 20 ]; do
    if verify_started "$NS" "$P" "$C" "$REMOTE_OUTDIR"; then
      say "[OK] Started in pod=$P"
      log_info "VerifyStarted|Success|pod=$P"
      START_OK=$((START_OK+1))
      break
    fi
    sleep 1
    i=$((i + 1))
  done

  if [ $i -ge 20 ]; then
    say "[ERROR] Collector not running in pod=$P"
    log_error "VerifyStarted|Timeout|pod=$P"
    START_FAIL=$((START_FAIL+1))
  fi
done

if [ "$START_OK" -gt 0 ]; then
  say "[SUCCESS] Start completed. Success=$START_OK Failed=$START_FAIL"
  log_info "START|Completed Success=$START_OK Failed=$START_FAIL"
  exit 0
else
  die "Start failed in all pods"
fi
fi


# ============================================================================
# STOP
# ============================================================================
if [ "$cmd" = "stop" ]; then
  [ $# -ge 6 ] || die "Usage: stop <api> <token> <ns> <pod|label> <value> <uuid> [outdir] [container]"

  API="$1"; TOKEN="$2"; NS="$3"; KIND="$4"; VAL="$5"; UUID="$6"
  MODE="$7"
  STOP_CMD="${8:-}"
  REMOTE_OUTDIR="${9:-/tmp/coverage}"
  CONTAINER="${10:-}"
  SOURCE_CODE_PATH="${11:-}"
  USE_SOURCE=false
if [ -n "$SOURCE_CODE_PATH" ]; then
  if [ -d "$SOURCE_CODE_PATH" ]; then
    USE_SOURCE=true
    say "[INFO] Source code path enabled: $SOURCE_CODE_PATH"
  else
    say "[WARN] Source code path not found: $SOURCE_CODE_PATH"
    say "[WARN] Generating summary-only coverage"
  fi
else
  say "[INFO] No source code path provided"
  say "[INFO] Generating summary-only coverage"
fi

  setup_kubeconfig "$API" "$TOKEN" "$NS"

  # ADDED: log stop high-level
  log_info "STOP|UUID=$UUID AuthenticationMethod=${AUTH_METHOD:-Unknown} Namespace=$NS SelectorType=$KIND SelectorValue=$VAL Mode=${MODE:-}"

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


  STOP_OK=0
STOP_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_BASE="$(detect_tools_base "$NS" "$P" "$C")" || {
  say "[ERROR] Coverage agent not found in pod=$P"
  log_error "DetectToolsBase|NotFound|pod=$P"
  STOP_FAIL=$((STOP_FAIL+1))
  continue
  }

  TOOLS_DIR="$TOOLS_BASE/.coverage_tools"

  set +e
  k -n "$NS" exec "$P" -c "$C" -- sh -lc "
    UUID='$UUID' COVERAGE_OUTDIR='$REMOTE_OUTDIR' MODE='$MODE' STOP_CMD='$STOP_CMD'\
      '$TOOLS_DIR/remote_dotnet_cov_agent.sh' stop
  "
  rc=$?
  set -e

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

  if copy_xml_from_pod "$NS" "$P" "$UUID" "$C" "$REMOTE_OUTDIR"; then
    log_info "CopyXMLFromPod|Success|pod=$P"
    STOP_OK=$((STOP_OK+1))
  else
    say "[ERROR] XML copy failed for pod=$P"
    log_error "CopyXMLFromPod|Failed|pod=$P"
    STOP_FAIL=$((STOP_FAIL+1))
    continue
  fi

  k -n "$NS" delete pod "$P" --wait=false || \
    say "[WARN] Pod delete failed: $P"
done

  if [ "$STOP_OK" -gt 0 ]; then
  finalize_xml "$UUID"
  generate_html_controller "$UUID" "$SOURCE_CODE_PATH" "$USE_SOURCE"
  strip_reportgenerator_promos_generic "$NS_WDIR/logs/coverage/$UUID"
  add_space_before_risk_hotspots "$NS_WDIR/logs/coverage/$UUID"
  say "[SUCCESS] Stop completed. Success=$STOP_OK Failed=$STOP_FAIL"
  say "[OK] XML  : $NS_WDIR/logs/codeAnalyzer/report_$UUID.xml"
  say "[OK] HTML : $NS_WDIR/logs/coverage/$UUID/index.html"
  log_info "STOP|Completed Success=$STOP_OK Failed=$STOP_FAIL UUID=$UUID XML=$NS_WDIR/logs/codeAnalyzer/report_$UUID.xml HTML=$NS_WDIR/logs/coverage/$UUID/index.html"
  exit 0
else
  die "Stop failed in all pods"
fi

fi

die "Invalid action (use start|stop)"

