#!/usr/bin/env bash
# Filter one MAKO Renderer test run into a focused, shareable diagnostic trace.
set -euo pipefail

default_plugin_log="${HOME}/.config/mako-render/present-diagnostics.log"
default_steam_logs=(
  "${HOME}/.steam/steam/logs/console-linux.txt"
  "${HOME}/.var/app/com.valvesoftware.Steam/.steam/steam/logs/console-linux.txt"
)
log_path="${MAKO_PRESENT_DIAGNOSTICS_LOG:-}"
retained_session_count=5
session=latest
line_count=800
presets=()

usage() {
  cat <<'EOF'
Usage: mako-diagnostics [options] [PRESET ...]

Filters retained MAKO Renderer diagnostics sessions. Multiple presets may be combined.
The MAKO Decky private log is preferred. Otherwise, the newest native or
Flatpak Steam console log is used.

Presets:
  hdr          HDR/scRGB selection, transport packing, passthrough, and initialization
  config       Decky reloads and pending/applied safe runtime transitions
  scaling      spatial extent, format, queue, activation, and scaling-only policy
  adaptive     Adaptive policy, stabilization, ramp, rescue, and cadence
  recovery     generated-image fallback, history warm-up, and in-place recovery
  performance  fixed-rate telemetry plus slow presentation and GPU operations
  lifecycle    swapchain creation/destruction and colour-pipeline selection
  startup      layer discovery, context creation, HDR selection, and failures
  layers       Vulkan-loader, Gamescope WSI, and MAKO layer discovery
  errors       failures, timeouts, fallbacks, passthrough, and Vulkan errors
  all          every relevant MAKO, Vulkan-loader, and Gamescope WSI line
               (alias: everything)

Options:
  --log PATH       Use PATH as the latest-session log instead of auto-selection
  --session NAME   Select latest, previous, oldest, previous-two, or all
                   (default: latest)
  --lines N        Return at most the last N matching lines per session (default: 800)
  --list           List presets
  -h, --help       Show this help

Examples:
  mako-diagnostics hdr
  mako-diagnostics scaling
  mako-diagnostics adaptive recovery
  mako-diagnostics --lines 2000 all > mako-report.txt
  mako-diagnostics --session previous --lines 2000 all > previous-report.txt
  mako-diagnostics --session previous-two --lines 2000 all > earlier-report.txt
  mako-diagnostics --session all --lines 2000 all > five-session-report.txt
EOF
}

list_presets() {
  printf '%s\n' hdr config scaling adaptive recovery performance lifecycle startup layers errors all
}

while (($#)); do
  case "$1" in
    --log)
      if (($# < 2)); then
        echo "--log requires a path" >&2
        exit 2
      fi
      log_path="$2"
      shift 2
      ;;
    --lines)
      if (($# < 2)); then
        echo "--lines requires a positive integer" >&2
        exit 2
      fi
      line_count="$2"
      shift 2
      ;;
    --session)
      if (($# < 2)); then
        echo "--session requires latest, previous, oldest, previous-two, or all" >&2
        exit 2
      fi
      session="$2"
      shift 2
      ;;
    --list)
      list_presets
      exit 0
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    --*)
      echo "Unknown option: $1" >&2
      usage >&2
      exit 2
      ;;
    *)
      presets+=("$1")
      shift
      ;;
  esac
done

if [[ ! "$line_count" =~ ^[1-9][0-9]*$ ]]; then
  echo "--lines must be a positive integer" >&2
  exit 2
fi

case "$session" in
  latest|previous|oldest|previous-two|all) ;;
  *)
    echo "--session must be latest, previous, oldest, previous-two, or all" >&2
    exit 2
    ;;
esac

if ((${#presets[@]} == 0)); then
  presets=(all)
fi

if [[ -z "$log_path" ]]; then
  if [[ -f "$default_plugin_log" ]]; then
    log_path="$default_plugin_log"
  else
    for candidate in "${default_steam_logs[@]}"; do
      if [[ -f "$candidate" &&
            ( -z "$log_path" || "$candidate" -nt "$log_path" ) ]]; then
        log_path="$candidate"
      fi
    done
    if [[ -z "$log_path" ]]; then
      log_path="${default_steam_logs[0]}"
    fi
  fi
fi

base_log_path="$log_path"
oldest_session_index=$((retained_session_count - 1))
log_paths=()
session_labels=()

add_session_log() {
  log_paths+=("$1")
  session_labels+=("$2")
}

case "$session" in
  latest)
    add_session_log "$base_log_path" latest
    ;;
  previous)
    add_session_log "${base_log_path}.1" previous
    ;;
  oldest)
    add_session_log "${base_log_path}.${oldest_session_index}" oldest
    ;;
  previous-two)
    if [[ -f "${base_log_path}.2" ]]; then
      add_session_log "${base_log_path}.2" previous-2
    fi
    if [[ -f "${base_log_path}.1" ]]; then
      add_session_log "${base_log_path}.1" previous
    fi
    ;;
  all)
    for ((session_index = oldest_session_index; session_index >= 1; session_index--)); do
      selected_log_path="${base_log_path}.${session_index}"
      selected_session="previous-${session_index}"
      if ((session_index == oldest_session_index)); then
        selected_session=oldest
      elif ((session_index == 1)); then
        selected_session=previous
      fi
      if [[ -f "$selected_log_path" ]]; then
        add_session_log "$selected_log_path" "$selected_session"
      fi
    done
    if [[ -f "$base_log_path" ]]; then
      add_session_log "$base_log_path" latest
    fi
    ;;
esac

if ((${#log_paths[@]} == 0)); then
  echo "Diagnostics log not found: $base_log_path" >&2
  echo "Enable MAKO_PRESENT_DIAGNOSTICS=1, reproduce the issue, and quit the game first." >&2
  exit 1
fi

for selected_log_path in "${log_paths[@]}"; do
  if [[ ! -f "$selected_log_path" ]]; then
    echo "Diagnostics log not found for session '$session': $selected_log_path" >&2
    echo "Only the latest $retained_session_count diagnostics-enabled MAKO Decky launches are retained." >&2
    exit 1
  fi
done

pattern=""
add_pattern() {
  if [[ -n "$pattern" ]]; then
    pattern+="|"
  fi
  pattern+="($1)"
}

# Every report should identify the exact engine that produced it. This makes a
# missing render layer visible even when the user selects only a focused
# preset such as HDR or performance. Retain the former prefix in the filter so
# reports collected immediately after an upgrade can still include old logs.
renderer_prefix='(MAKO Renderer:|mako:)'
add_pattern "$renderer_prefix render layer active; identity=VK_LAYER_MAKO_render; build="
# Interleaved Vulkan processes are common on SteamOS. Keep their identity and
# swapchain metadata in every focused report so policy and slow-operation
# records can be attributed without expanding the report to unrelated output.
add_pattern "$renderer_prefix present diagnostics: operation=(process-identity|swapchain-context-create)"
# Wrapper session admission failures are deliberately log-only. Retain this
# line in every focused report so a Desktop-session fallback remains visible
# without reproducing Gamescope WSI's modal error path.
add_pattern 'MAKO Decky: Gamescope WSI skipped:'
# Spatial-scaling and live resource policy records are low-frequency
# swapchain/startup metadata. Keep them in every focused report so combined
# Fixed/Adaptive failures cannot be misread as frame-generation-only sessions.
# The focused scaling preset adds related colour, runtime-transition, and
# generic swapchain failures.
spatial_scaling_record="$renderer_prefix (spatial scaling|live spatial scaling change|live profile resource change|LS1 scaling unavailable|standalone spatial scaling|multi-swapchain spatial-scaling|frame generation was enabled after the application device was created without external interop|frame-generation backend initialization failed; independent spatial scaling retained)"
add_pattern "$spatial_scaling_record"
live_recreation_record="$renderer_prefix present diagnostics: operation=(generated-present-recreation-drain|original-present-recreation-propagate|swapchain-recreation-observed|swapchain-create-observed|replacement-wsi-prime|replacement-backend-stabilization)"
add_pattern "$live_recreation_record"

for preset in "${presets[@]}"; do
  case "$preset" in
    hdr)
      add_pattern "$renderer_prefix (swapchain colour pipeline|HDR10 transport|frame generation disabled for this swapchain|frame generation is off for this swapchain|frame-generation initialization failed|MAKO frame-generation initialization failed)"
      add_pattern "$renderer_prefix Gamescope application HDR feedback"
      add_pattern "$renderer_prefix present diagnostics: operation=(runtime-transition-pending|runtime-transition-prepared|runtime-transition-failed|runtime-transition-applied|runtime-state-applied|gamescope-refresh-rate-applied)"
      ;;
    config)
      add_pattern "$renderer_prefix (updated configuration in place|updated mako configuration in place|configuration changes requiring GPU resource reconstruction|process-static configuration changes|global backend construction changed)"
      add_pattern "$renderer_prefix renderer-memory operation=private-context-(prepared|applied)"
      add_pattern "$renderer_prefix present diagnostics: operation=(runtime-transition-pending|runtime-transition-prepared|runtime-transition-failed|runtime-transition-applied|runtime-transition-recreation-requested|runtime-state-applied)"
      ;;
    scaling)
      add_pattern "$renderer_prefix (swapchain colour pipeline|swapchain creation failed|swapchain presentation failed)"
      add_pattern "$renderer_prefix Gamescope application HDR feedback"
      add_pattern "$renderer_prefix present diagnostics: operation=(runtime-transition-pending|runtime-transition-prepared|runtime-transition-failed|runtime-transition-applied|runtime-transition-recreation-requested|runtime-state-applied|gamescope-refresh-rate-applied)"
      add_pattern '(fixed-surface )?spatial scaling (context initialization failed|requires|source and presentation extents are identical)'
      ;;
    adaptive)
      add_pattern "$renderer_prefix present diagnostics: operation=adaptive-"
      add_pattern "$renderer_prefix present diagnostics: operation=(runtime-transition-pending|runtime-transition-prepared|runtime-transition-failed|runtime-transition-applied|runtime-state-applied|generated-delivery-miss|gamescope-refresh-rate-applied)"
      ;;
    recovery)
      add_pattern "$renderer_prefix present diagnostics: operation=(adaptive-wsi-headroom-limit|skip-generated-frames|resume-generated-frames|generated-image-recovered|generated-delivery-miss|generated-admission-pressure|generated-admission-recovered|fixed-cadence-collapse-(probe-start|probe-rejected|probe-recovered|recovery-unstable|recovery-verified)|ordered-acquire-policy|ordered-acquire-classification-split|ordered-acquire-budget-exhausted|ordered-acquire-guard|ordered-acquire-guard-bypass|ordered-acquire-native-saturation|ordered-acquire-quarantine|ordered-acquire-retry|ordered-acquire-recovered|ordered-acquire-stabilized|pipeline-busy-bypass|pipeline-busy-recovered|render-fence-budget-missed|history-warmup|swapchain-recreation-suppressed|runtime-transition-pending|runtime-transition-prepared|runtime-transition-failed|runtime-transition-applied)"
      ;;
    performance)
      add_pattern "$renderer_prefix (backend-memory|renderer-memory) operation="
      add_pattern "$renderer_prefix present diagnostics: operation=(adaptive-wsi-headroom-limit|fixed-plan|fixed-cadence-collapse-(probe-start|probe-rejected|probe-recovered|recovery-unstable|recovery-verified)|runtime-transition-pending|runtime-transition-prepared|runtime-transition-failed|runtime-transition-applied|runtime-transition-recreation-requested|runtime-state-applied|generated-delivery-miss|generated-admission-pressure|generated-admission-recovered|ordered-acquire-policy|ordered-acquire-classification-split|ordered-acquire-budget-exhausted|ordered-acquire-guard|ordered-acquire-guard-bypass|ordered-acquire-native-saturation|ordered-acquire-quarantine|ordered-acquire-retry|ordered-acquire-recovered|ordered-acquire-stabilized|pipeline-busy-recovered|pipeline-busy-bypass|gamescope-refresh-rate-applied|present-|schedule-|submit-|acquire-|wait-|copy-|render-)"
      ;;
    lifecycle)
      add_pattern "$renderer_prefix present diagnostics: operation=swapchain-context-(create|destroy)"
      add_pattern "$renderer_prefix present diagnostics: operation=(runtime-transition-pending|runtime-transition-prepared|runtime-transition-failed|runtime-transition-applied|runtime-state-applied)"
      add_pattern "$renderer_prefix (swapchain colour pipeline|HDR10 transport|frame generation disabled for this swapchain|frame generation is off for this swapchain|frame-generation initialization failed|MAKO frame-generation initialization failed)"
      ;;
    startup)
      add_pattern '(VK_LAYER_MAKO_render|VkLayer_MAKO_render|Gamescope WSI|Vulkan Loader|<Loader>|<Device>)'
      add_pattern "$renderer_prefix present diagnostics: operation=swapchain-context-create"
      add_pattern "$renderer_prefix Gamescope application HDR feedback"
      add_pattern "$renderer_prefix present diagnostics: operation=(runtime-transition-pending|runtime-transition-prepared|runtime-transition-failed|runtime-transition-applied|runtime-state-applied)"
      add_pattern "$renderer_prefix (swapchain colour pipeline|HDR10 transport|frame generation disabled for this swapchain|frame generation is off for this swapchain|frame-generation initialization failed|MAKO frame-generation initialization failed)"
      ;;
    layers)
      add_pattern '(VK_LAYER_MAKO_render|VkLayer_MAKO_render|Gamescope WSI|Vulkan Loader|<Loader>|<Device>)'
      ;;
    errors)
      add_pattern "$renderer_prefix.*(failed|failure|error|timeout|fallback|passthrough|disabled|VK_ERROR|VK_TIMEOUT|VK_NOT_READY|device lost)"
      add_pattern '(Vulkan Loader|Gamescope WSI).*(ERROR|Error|error|WARNING|Warning|warning)'
      ;;
    all|everything)
      add_pattern '(MAKO Renderer:|mako:|VK_LAYER_MAKO_render|VkLayer_MAKO_render|Gamescope WSI|Vulkan Loader|<Loader>|<Device>)'
      ;;
    *)
      echo "Unknown preset: $preset" >&2
      echo "Available presets:" >&2
      list_presets >&2
      exit 2
      ;;
  esac
done

for index in "${!log_paths[@]}"; do
  selected_log_path="${log_paths[$index]}"
  selected_session="${session_labels[$index]}"
  echo "MAKO Renderer diagnostics source: $selected_log_path" >&2
  echo "session: $selected_session; retained sessions: $retained_session_count; presets: ${presets[*]}; last matching lines: $line_count" >&2
  { grep -aE "$pattern" "$selected_log_path" || true; } | tail -n "$line_count"
done
