#!/usr/bin/env bash
# JSONL log writer for hooks/log.sh.

vg_append_log_line() {
  local file="$1"
  local line="$2"
  local lock_dir="${file}.lock.d"
  local acquired="false"
  local attempts=0
  local status=0

  while [[ "$attempts" -lt 100 ]]; do
    if mkdir "$lock_dir" 2>/dev/null; then
      acquired="true"
      break
    fi
    attempts=$((attempts + 1))
    sleep 0.01 2>/dev/null || true
  done

  if printf '%s\n' "$line" >> "$file"; then
    status=0
  else
    status=$?
  fi

  if [[ "$acquired" == "true" ]]; then
    rmdir "$lock_dir" 2>/dev/null || true
  fi

  return "$status"
}

vg_private_log_file() {
  local file="$1"
  [[ -e "$file" ]] || return 0
  chmod 600 "$file" 2>/dev/null || true
}

vg_log_clean_json_text() {
  local text="$1"
  if [[ "$text" =~ ^[A-Za-z0-9_./:\ \(\),-]*$ ]]; then
    printf '%s' "$text"
  else
    printf '%s' "$text" | tr -d '\000-\007\013\016-\037\177'
  fi
}

vg_log_json_escape() {
  local text
  text="$(vg_log_clean_json_text "$1")"
  text="${text//\\/\\\\}"
  text="${text//\"/\\\"}"
  text="${text//$'\n'/\\n}"
  text="${text//$'\r'/\\r}"
  text="${text//$'\t'/\\t}"
  text="${text//$'\b'/\\b}"
  text="${text//$'\f'/\\f}"
  printf '%s' "$text"
}

vg_log_append_string_field() {
  local json="$1"
  local field="$2"
  local value="$3"
  [[ -n "$value" ]] || { printf '%s' "$json"; return 0; }
  printf '%s, "%s": "%s"' "$json" "$field" "$(vg_log_json_escape "$value")"
}

vg_log() {
  local hook="$1"
  local tool="$2"
  local decision="$3"
  local reason="${4:-}"
  local detail="${5:-}"

  reason="$(vg_redact_sensitive "$reason")"
  detail="$(vg_redact_sensitive "$detail")"

  # Truncate detail to 200 chars after redaction so partial secrets do not leak.
  detail="$(vg_truncate_utf8 "$detail" 200)"

  # Calculation time
  local duration_ms=""
  if [[ -n "$_VG_START_MS" ]]; then
    local end_ms
    if command -v perl &>/dev/null; then
      end_ms=$(perl -MTime::HiRes=time -e 'printf "%.0f", time*1000')
    else
      end_ms=$(date +%s)000
    fi
    duration_ms=$(( end_ms - _VG_START_MS ))
    _VG_START_MS=""
  fi

  if [[ ! -d "$VIBEGUARD_LOG_DIR" ]]; then
    mkdir -p "$VIBEGUARD_LOG_DIR" 2>/dev/null
  fi
  chmod 700 "$VIBEGUARD_LOG_DIR" 2>/dev/null || true

  # Pure bash JSON serialization (eliminates python3 subprocess)
  local ts
  ts=$(date -u '+%Y-%m-%dT%H:%M:%SZ')

  # JSON escape: reason and detail may contain special characters.
  # Strip all C0 control bytes that are NOT JSON-escapable (\b \t \n \f \r).
  # This removes ESC (0x1B) and the orphaned BEL/VT/SO/etc. bytes that OSC/CSI
  # terminal sequences leave behind after ESC is removed (e.g. \x1b]8;;url\x07
  # becomes ]8;;url\x07 with ESC-only stripping; the raw BEL makes JSONL invalid).
  # tr range: 0x00-0x07 (NUL-BEL), 0x0B (VT), 0x0E-0x1F (SO-US incl. ESC), 0x7F (DEL)
  local esc_reason esc_detail
  esc_reason=$(vg_log_json_escape "$reason")
  esc_detail=$(vg_log_json_escape "$detail")

  local json
  json="{\"schema_version\": 1, \"ts\": \"${ts}\", \"session\": \"${VIBEGUARD_SESSION_ID}\", \"hook\": \"${hook}\", \"tool\": \"${tool}\", \"decision\": \"${decision}\", \"reason\": \"${esc_reason}\", \"detail\": \"${esc_detail}\""
  [[ -n "$duration_ms" ]] && json="${json}, \"duration_ms\": ${duration_ms}"
  json="$(vg_log_append_string_field "$json" "cli" "${VIBEGUARD_CLI:-}")"
  json="$(vg_log_append_string_field "$json" "agent" "${VIBEGUARD_AGENT_TYPE:-}")"
  json="$(vg_log_append_string_field "$json" "client" "${VIBEGUARD_CLIENT:-}")"
  json="$(vg_log_append_string_field "$json" "client_variant" "${VIBEGUARD_CLIENT_VARIANT:-}")"
  json="$(vg_log_append_string_field "$json" "wrapper" "${VIBEGUARD_WRAPPER:-}")"
  json="$(vg_log_append_string_field "$json" "source_config" "${VIBEGUARD_SOURCE_CONFIG:-}")"
  json="$(vg_log_append_string_field "$json" "hook_protocol_version" "${VIBEGUARD_HOOK_PROTOCOL_VERSION:-}")"
  json="$(vg_log_append_string_field "$json" "caller_evidence" "${VIBEGUARD_CALLER_EVIDENCE:-}")"
  json="${json}}"

  vg_private_log_file "$VIBEGUARD_LOG_FILE"
  vg_append_log_line "$VIBEGUARD_LOG_FILE" "$json"
  vg_private_log_file "$VIBEGUARD_LOG_FILE"

  # Synchronously write to the global log (for stats.sh aggregation analysis)
  local global_log="${VIBEGUARD_LOG_DIR}/events.jsonl"
  if [[ "$VIBEGUARD_LOG_FILE" != "$global_log" ]]; then
    vg_private_log_file "$global_log"
    vg_append_log_line "$global_log" "$json"
    vg_private_log_file "$global_log"
  fi
}
