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

# discover.sh — Environment detection script for tunectl
# Outputs a JSON report to stdout with system information.
# Strictly read-only: no system modifications.
# Works without root privileges.

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

# -------------------------------------------------------
# Helper: safe file read (returns empty string if missing)
# -------------------------------------------------------
safe_read() {
  local file="$1"
  if [[ -r "$file" ]]; then
    cat "$file" 2>/dev/null || echo ""
  else
    echo ""
  fi
}

# -------------------------------------------------------
# Detect OS version from /etc/os-release
# -------------------------------------------------------
detect_os_version() {
  if [[ -r /etc/os-release ]]; then
    # shellcheck disable=SC1091
    . /etc/os-release 2>/dev/null
    echo "${PRETTY_NAME:-unknown}"
  else
    echo "unknown"
  fi
}

# -------------------------------------------------------
# Detect kernel version via uname -r
# -------------------------------------------------------
detect_kernel_version() {
  uname -r 2>/dev/null || echo "unknown"
}

# -------------------------------------------------------
# Detect RAM in MB from /proc/meminfo
# -------------------------------------------------------
detect_ram_mb() {
  local mem_kb
  mem_kb=$(awk '/^MemTotal:/ {print $2}' /proc/meminfo 2>/dev/null || echo "0")
  echo $(( mem_kb / 1024 ))
}

# -------------------------------------------------------
# Detect CPU count via nproc
# -------------------------------------------------------
detect_cpu_count() {
  nproc 2>/dev/null || grep -c '^processor' /proc/cpuinfo 2>/dev/null || echo "1"
}

# -------------------------------------------------------
# Detect disk type: check /sys/block/*/queue/rotational
# and look for virtio driver
# -------------------------------------------------------
detect_disk_type() {
  local disk_type="unknown"
  # Find the primary block device (skip loop, zram, dm, sr, fd devices)
  local primary_disk=""
  for dev in /sys/block/*; do
    local devname
    devname="$(basename "$dev")"
    # Skip non-physical devices
    case "$devname" in
      loop*|zram*|dm-*|sr*|fd*|ram*) continue ;;
    esac
    primary_disk="$devname"
    break
  done

  if [[ -z "$primary_disk" ]]; then
    echo "unknown"
    return
  fi

  # Check for virtio driver
  local driver_path="/sys/block/${primary_disk}/device/driver"
  if [[ -L "$driver_path" ]]; then
    local driver_target
    driver_target="$(readlink "$driver_path" 2>/dev/null || echo "")"
    if [[ "$driver_target" == *"virtio"* ]]; then
      disk_type="virtio"
      echo "$disk_type"
      return
    fi
  fi

  # Check modalias for virtio
  local modalias
  modalias="$(safe_read "/sys/block/${primary_disk}/device/modalias")"
  if [[ "$modalias" == *"virtio"* ]]; then
    disk_type="virtio"
    echo "$disk_type"
    return
  fi

  # Check rotational flag
  local rotational
  rotational="$(safe_read "/sys/block/${primary_disk}/queue/rotational")"
  rotational="$(echo "$rotational" | tr -d '[:space:]')"
  if [[ "$rotational" == "0" ]]; then
    disk_type="ssd"
  elif [[ "$rotational" == "1" ]]; then
    # On cloud VPS, rotational=1 may still be SSD/virtio — check for common cloud patterns
    # If we have virtio in the device name, it's likely a cloud SSD
    if [[ "$primary_disk" == vd* ]]; then
      disk_type="virtio"
    else
      disk_type="hdd"
    fi
  fi

  echo "$disk_type"
}

# -------------------------------------------------------
# Detect virtualization type
# -------------------------------------------------------
detect_virt_type() {
  # Try systemd-detect-virt first (most reliable)
  if command -v systemd-detect-virt &>/dev/null; then
    local virt
    virt="$(systemd-detect-virt 2>/dev/null || echo "")"
    if [[ -n "$virt" && "$virt" != "none" ]]; then
      echo "$virt"
      return
    fi
  fi

  # Fallback: check /proc/cpuinfo for hypervisor flag
  if grep -qi "hypervisor" /proc/cpuinfo 2>/dev/null; then
    # Try to determine type from DMI
    local product_name
    product_name="$(safe_read /sys/class/dmi/id/product_name)"
    case "$product_name" in
      *"KVM"*|*"QEMU"*) echo "kvm"; return ;;
      *"VMware"*) echo "vmware"; return ;;
      *"VirtualBox"*) echo "oracle"; return ;;
      *"Xen"*) echo "xen"; return ;;
    esac
    echo "vm"
    return
  fi

  echo "none"
}

# -------------------------------------------------------
# Detect swap state
# -------------------------------------------------------
detect_swap() {
  local swap_total_kb=0
  local has_zram=false
  local has_file=false
  local has_partition=false

  # Read /proc/swaps
  if [[ -r /proc/swaps ]]; then
    while IFS= read -r line; do
      # Skip header
      [[ "$line" == Filename* ]] && continue

      local filename type size
      filename="$(echo "$line" | awk '{print $1}')"
      type="$(echo "$line" | awk '{print $2}')"
      size="$(echo "$line" | awk '{print $3}')"

      swap_total_kb=$((swap_total_kb + size))

      if [[ "$filename" == *"zram"* ]]; then
        has_zram=true
      elif [[ "$type" == "file" ]]; then
        has_file=true
      elif [[ "$type" == "partition" ]]; then
        # zram shows as partition type too
        if [[ "$filename" == *"zram"* ]]; then
          has_zram=true
        else
          has_partition=true
        fi
      fi
    done < /proc/swaps
  fi

  local swap_configured=false
  local swap_type="none"
  local swap_total_mb=0

  if [[ "$swap_total_kb" -gt 0 ]]; then
    swap_configured=true
    swap_total_mb=$((swap_total_kb / 1024))

    if $has_zram; then
      swap_type="zram"
    elif $has_file || $has_partition; then
      swap_type="file"
    fi
  fi

  echo "${swap_configured}|${swap_type}|${swap_total_mb}"
}

# -------------------------------------------------------
# Collect current sysctl values for key parameters
# -------------------------------------------------------
collect_sysctl_values() {
  # Key sysctl parameters to check — extracted from manifest categories
  local params=(
    "vm.swappiness"
    "vm.page-cluster"
    "vm.dirty_ratio"
    "vm.dirty_background_ratio"
    "vm.dirty_expire_centisecs"
    "vm.dirty_writeback_centisecs"
    "vm.vfs_cache_pressure"
    "vm.min_free_kbytes"
    "vm.overcommit_memory"
    "vm.max_map_count"
    "vm.compaction_proactiveness"
    "vm.numa_stat"
    "vm.stat_interval"
    "vm.watermark_boost_factor"
    "vm.watermark_scale_factor"
    "net.core.rmem_max"
    "net.core.wmem_max"
    "net.core.rmem_default"
    "net.core.wmem_default"
    "net.core.netdev_max_backlog"
    "net.core.somaxconn"
    "net.core.default_qdisc"
    "net.ipv4.tcp_max_syn_backlog"
    "net.ipv4.tcp_max_tw_buckets"
    "net.ipv4.tcp_fin_timeout"
    "net.ipv4.tcp_keepalive_time"
    "net.ipv4.tcp_keepalive_intvl"
    "net.ipv4.tcp_keepalive_probes"
    "net.ipv4.tcp_slow_start_after_idle"
    "net.ipv4.tcp_tw_reuse"
    "net.ipv4.tcp_fastopen"
    "net.ipv4.ip_local_port_range"
    "net.ipv4.tcp_rmem"
    "net.ipv4.tcp_wmem"
    "net.ipv4.tcp_congestion_control"
    "net.ipv4.tcp_mtu_probing"
    "fs.inotify.max_user_watches"
    "fs.inotify.max_user_instances"
    "fs.aio-max-nr"
    "kernel.sched_autogroup_enabled"
    "kernel.sched_cfs_bandwidth_slice_us"
  )

  # Build JSON object using jq for safe escaping
  local json_obj="{}"
  for param in "${params[@]}"; do
    local value=""
    # Try sysctl command first (handles name→path translation correctly)
    value="$(sysctl -n "$param" 2>/dev/null || echo "")"
    if [[ -z "$value" ]]; then
      # Fallback: read from /proc/sys (convert dots to slashes)
      local proc_path="/proc/sys/$(echo "$param" | tr '.' '/')"
      if [[ -r "$proc_path" ]]; then
        value="$(cat "$proc_path" 2>/dev/null | tr '\t' ' ')"
      fi
    fi

    if [[ -n "$value" ]]; then
      # Trim trailing whitespace
      value="$(echo "$value" | sed 's/[[:space:]]*$//')"
      json_obj="$(echo "$json_obj" | jq --arg k "$param" --arg v "$value" '. + {($k): $v}')"
    fi
  done

  echo "$json_obj"
}

# -------------------------------------------------------
# Detect mount options for root partition
# -------------------------------------------------------
detect_mount_options() {
  local mount_line
  mount_line="$(mount 2>/dev/null | grep ' / ' | grep -v '//' | head -1)"
  if [[ -n "$mount_line" ]]; then
    # Extract options between parentheses
    local opts
    opts="$(echo "$mount_line" | sed 's/.*(\(.*\))/\1/')"
    echo "$opts"
  else
    echo "unknown"
  fi
}

# -------------------------------------------------------
# Detect active services
# -------------------------------------------------------
detect_active_services() {
  if command -v systemctl &>/dev/null; then
    systemctl list-units --type=service --state=active --no-legend 2>/dev/null \
      | awk '{print $1}' \
      | jq -R -s 'split("\n") | map(select(length > 0))'
  else
    echo "[]"
  fi
}

# -------------------------------------------------------
# Main: collect all data and output JSON
# -------------------------------------------------------
main() {
  local os_version kernel_version ram_mb cpu_count disk_type virt_type
  local swap_info swap_configured swap_type swap_total_mb
  local sysctl_values mount_options active_services

  os_version="$(detect_os_version)"
  kernel_version="$(detect_kernel_version)"
  ram_mb="$(detect_ram_mb)"
  cpu_count="$(detect_cpu_count)"
  disk_type="$(detect_disk_type)"
  virt_type="$(detect_virt_type)"

  # Parse swap info (pipe-delimited)
  swap_info="$(detect_swap)"
  swap_configured="$(echo "$swap_info" | cut -d'|' -f1)"
  swap_type="$(echo "$swap_info" | cut -d'|' -f2)"
  swap_total_mb="$(echo "$swap_info" | cut -d'|' -f3)"

  sysctl_values="$(collect_sysctl_values)"
  mount_options="$(detect_mount_options)"
  active_services="$(detect_active_services)"

  # Build JSON output using jq for proper formatting and escaping
  jq -n \
    --arg os_version "$os_version" \
    --arg kernel_version "$kernel_version" \
    --argjson ram_mb "$ram_mb" \
    --argjson cpu_count "$cpu_count" \
    --arg disk_type "$disk_type" \
    --arg virt_type "$virt_type" \
    --argjson swap_configured "$swap_configured" \
    --arg swap_type "$swap_type" \
    --argjson swap_total_mb "$swap_total_mb" \
    --argjson sysctl_values "$sysctl_values" \
    --arg mount_options "$mount_options" \
    --argjson active_services "$active_services" \
    '{
      os_version: $os_version,
      kernel_version: $kernel_version,
      ram_mb: $ram_mb,
      cpu_count: $cpu_count,
      disk_type: $disk_type,
      virt_type: $virt_type,
      swap_configured: $swap_configured,
      swap_type: $swap_type,
      swap_total_mb: $swap_total_mb,
      sysctl_values: $sysctl_values,
      mount_options: $mount_options,
      active_services: $active_services
    }'
}

main
