#!/usr/bin/env bash
# omarchy-report-laptop — collect Linux laptop hardware + functional outcomes
# and upload to rig-observer (https://github.com/rig-observer).
#
# Usage:
#   omarchy-report-laptop                 # collect + upload probe, then print attestation link
#   omarchy-report-laptop --dry-run       # print the JSON payload, upload nothing
#   omarchy-report-laptop --report        # after upload, interactively attest component outcomes
#   omarchy-report-laptop --suspend-test  # consent-gated: run one suspend cycle and report the wake outcome
#   omarchy-report-laptop --url URL       # override API base (default https://rig.observer)
#
# Set LLDB_TOKEN to reuse a specific anonymous reporter token.

set -euo pipefail

API_URL="${LLDB_URL:-https://rig.observer}"
DRY_RUN=0
DO_REPORT=0
SUSPEND_TEST=0
TOKEN="${LLDB_TOKEN:-}"

while [ $# -gt 0 ]; do
	case "$1" in
	--dry-run) DRY_RUN=1 ;;
	--report) DO_REPORT=1 ;;
	--suspend-test) SUSPEND_TEST=1 ;;
	--url) API_URL="$2"; shift ;;
	*) echo "unknown option: $1" >&2; exit 64 ;;
	esac
	shift
done

# ---- helpers -----------------------------------------------------------------

jesc() {
	# escape for JSON string: backslash, quote, newline/tab
	printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' | tr -d '\n\r\t'
}

have() { command -v "$1" >/dev/null 2>&1; }

# ---- identity ----------------------------------------------------------------

DMI=/sys/class/dmi/id
BOARD_VENDOR=$(cat "$DMI/board_vendor" 2>/dev/null || echo unknown)
BOARD_NAME=$(cat "$DMI/board_name" 2>/dev/null || echo unknown)
PRODUCT_NAME=$(cat "$DMI/product_name" 2>/dev/null || echo)
BIOS_VERSION=$(cat "$DMI/bios_version" 2>/dev/null || echo)

CHASSIS=Notebook
case "$(cat "$DMI/chassis_type" 2>/dev/null || echo 9)" in
1|2|3) CHASSIS=Desktop ;;
8|9|10|14) CHASSIS=Notebook ;;
31|32) CHASSIS=Server ;;
*) CHASSIS=Other ;;
esac

# distro detection (Omarchy first-class)
DISTRO=""
DISTRO_VERSION=""
OMARCHY=0
if have omarchy 2>/dev/null || pacman -Q omarchy >/dev/null 2>&1; then
	OMARCHY=1
	DISTRO=omarchy
	DISTRO_VERSION=$(pacman -Q omarchy 2>/dev/null | awk '{print $2}')
fi
if [ -z "$DISTRO" ] && [ -r /etc/os-release ]; then
	# shellcheck disable=SC1091
	. /etc/os-release
	DISTRO="${ID:-}"
	DISTRO_VERSION="${VERSION_ID:-}"
fi

KERNEL=$(uname -r)
DE="${XDG_CURRENT_DESKTOP:-}"
if [ "$OMARCHY" = 1 ] && have hyprctl; then DE="Hyprland"; fi

# known kernel flavors: which non-stock kernel packages are installed
KERNEL_FORK=""
if have pacman; then
	for pkg in linux-surface linux-tuxedo linux-cachyos linux-zen linux-lts linux-hardened; do
		if pacman -Q "$pkg" >/dev/null 2>&1; then
			KERNEL_FORK="$KERNEL_FORK $pkg"
		fi
	done
	KERNEL_FORK="${KERNEL_FORK# }"
fi

CMDLINE=$(cat /proc/cmdline 2>/dev/null || echo)

CPU=""
if have lscpu; then
	CPU=$(lscpu 2>/dev/null | grep "Model name" | sed -E 's/^[^:]+:\s*//' | sed -E 's/\s+-\s+.*//' || true)
fi
if [ -z "$CPU" ]; then
	CPU=$(grep -m1 "model name" /proc/cpuinfo 2>/dev/null | sed 's/.*: //' || true)
fi

# ---- sleep capabilities --------------------------------------------------------

MEM_SLEEP=$(cat /sys/power/mem_sleep 2>/dev/null || echo)
POWER_STATES=$(cat /sys/power/state 2>/dev/null || echo)
S3=0
case "$MEM_SLEEP" in *deep*) S3=1 ;; esac
S4=0
case "$POWER_STATES" in *disk*) S4=1 ;; esac
S0IX=""  # detection requires acpidump; left for the web report if known

# ---- components ----------------------------------------------------------------

declare -a COMPONENTS_JSON=()

add_component() { # type bus vendor device subsystem name driver
	local json="{\"type\":\"$(jesc "$1")\""
	[ -n "${2:-}" ] && json+=",\"bus\":\"$(jesc "$2")\""
	[ -n "${3:-}" ] && json+=",\"vendor_id\":\"$(jesc "$3")\""
	[ -n "${4:-}" ] && json+=",\"device_id\":\"$(jesc "$4")\""
	[ -n "${5:-}" ] && json+=",\"subsystem_id\":\"$(jesc "$5")\""
	[ -n "${6:-}" ] && json+=",\"name\":\"$(jesc "$6")\""
	[ -n "${7:-}" ] && json+=",\"driver\":\"$(jesc "$7")\""
	json+="}"
	COMPONENTS_JSON+=("$json")
}

if have lspci; then
	while IFS= read -r line; do
		addr="${line%% *}"
		rest="${line#* }"
		cls="$(printf '%s' "$rest" | sed -E 's/^([^[]+) \[[0-9a-f]{4}\]: .*/\1/')"
		ids="$(printf '%s' "$rest" | grep -oE '\[[0-9a-f]{4}:[0-9a-f]{4}\]' | tr -d '[]' | tr ':' ' ')"
		vendor=$(printf '%s' "$ids" | awk '{print $1}')
		device=$(printf '%s' "$ids" | awk '{print $2}')
		name="$(printf '%s' "$rest" | sed -E 's/^[^]]*\]: //' | sed -E 's/\[[0-9a-f]{4}:[0-9a-f]{4}\]//; s/\s*\(rev [^)]*\)//; s/\s+$//')"
		driver=$(lspci -k -s "$addr" 2>/dev/null | grep "Kernel driver in use" | sed 's/.*: //' || true)
		subsys=$(lspci -k -s "$addr" 2>/dev/null | grep -oE 'Subsystem: .*\[[0-9a-f]{4}:[0-9a-f]{4}\]' | grep -oE '\[[0-9a-f]{4}:[0-9a-f]{4}\]$' | tr -d '[]' || true)
		case "$cls" in
		*"VGA compatible"* | *"3D controller"* | *"Display controller"*) type=graphics ;;
		*"Network controller"*) type=wifi ;;
		*"Ethernet controller"*) type=ethernet ;;
		*"Audio"*) type=audio ;;
		*"SD Host"* | *"SD/MMC"*) type=sd_reader ;;
		*) continue ;;
		esac
		add_component "$type" "pci" "$vendor" "$device" "${subsys:-}" "$name" "$driver"
	done < <(lspci -nn 2>/dev/null | grep -E "VGA compatible|3D controller|Display controller|Network controller|Ethernet controller|Audio|SD Host|SD/MMC")
fi

if have lsusb; then
	while IFS= read -r line; do
		ids="$(printf '%s' "$line" | grep -oE '^[0-9a-f]{4}:[0-9a-f]{4}' || true)"
		vendor="${ids%%:*}"
		device="${ids##*:}"
		name="$(printf '%s' "$line" | sed -E 's/^[0-9a-f]{4}:[0-9a-f]{4} //' | sed -E 's/^[0-9a-f]+ [0-9a-f]+ //')"
		case "$name" in
		*Webcam* | *Camera* | *"HD Video"*) type=webcam ;;
		*Fingerprint*) type=fingerprint ;;
		*Bluetooth*) type=bluetooth ;;
		*Wireless* | *Wi-Fi*) type=wifi ;;
		*Touchpad*) type=touchpad ;;
		*Keyboard*) type=keyboard ;;
		*Audio* | *Sound* | *Speaker*) type=audio ;;
		*) continue ;;
		esac
		add_component "$type" "usb" "$vendor" "$device" "" "$name" ""
	done < <(lsusb 2>/dev/null)
fi

if have bluetoothctl; then
	bluetoothctl list 2>/dev/null | grep -q Controller || true
fi

# dedupe identical type+bus+vendor+device entries
mapfile -t COMPONENTS_JSON < <(printf '%s\n' "${COMPONENTS_JSON[@]}" | awk '!seen[$0]++')

# ---- payload -------------------------------------------------------------------

COMPONENTS=$(printf '%s,' "${COMPONENTS_JSON[@]}" | sed 's/,$//')
ALIASES=""
[ -n "$PRODUCT_NAME" ] && [ "$PRODUCT_NAME" != "$BOARD_NAME" ] && ALIASES=",\"aliases\":[\"$(jesc "$PRODUCT_NAME")\"]"

SLEEP_JSON=""
if [ -n "$MEM_SLEEP" ] || [ "$S3" = 1 ] || [ "$S4" = 1 ]; then
	SLEEP_JSON=",\"sleep\":{\"mem_sleep\":\"$(jesc "$MEM_SLEEP")\""
	[ -n "$S0IX" ] && SLEEP_JSON+=",\"s0ix\":$S0IX"
	SLEEP_JSON+=",\"s3\":$S3,\"s4\":$S4}"
fi

PAYLOAD="{
  \"schema\": 1,
  \"collected_at\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",
  \"laptop\": {
    \"board_vendor\": \"$(jesc "$BOARD_VENDOR")\",
    \"board_name\": \"$(jesc "$BOARD_NAME")\",
    \"product_name\": \"$(jesc "$PRODUCT_NAME")\"${ALIASES},
    \"chassis\": \"$(jesc "$CHASSIS")\"
  },
  \"system\": {
    \"distro\": \"$(jesc "$DISTRO")\",
    \"distro_version\": \"$(jesc "$DISTRO_VERSION")\",
    \"kernel\": \"$(jesc "$KERNEL")\",
    \"de\": \"$(jesc "$DE")\",
    \"bios\": \"$(jesc "$BIOS_VERSION")\",
    \"cpu\": \"$(jesc "$CPU")\",
    \"cmdline\": \"$(jesc "$CMDLINE")\"
  }${SLEEP_JSON},
  \"components\": [$COMPONENTS]
}"

if [ "$DRY_RUN" = 1 ]; then
	printf '%s\n' "$PAYLOAD"
	exit 0
fi

# ---- token ---------------------------------------------------------------------

TOKEN_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/omarchy-report-laptop"
mkdir -p "$TOKEN_DIR"
TOKEN_FILE="$TOKEN_DIR/token"

if [ -z "$TOKEN" ] && [ -r "$TOKEN_FILE" ]; then
	TOKEN=$(cat "$TOKEN_FILE")
fi
if [ -z "$TOKEN" ]; then
	TOKEN=$(curl -fsS -X POST -H "Content-Type: application/json" "$API_URL/api/reporters" | sed -E 's/.*"token":"([^"]+)".*/\1/')
	if [ -z "$TOKEN" ]; then
		echo "error: could not obtain reporter token" >&2
		exit 1
	fi
	printf '%s' "$TOKEN" > "$TOKEN_FILE"
fi

# ---- upload ---------------------------------------------------------------------

echo "Collected:"
echo "  laptop:  $BOARD_VENDOR $BOARD_NAME${PRODUCT_NAME:+ ($PRODUCT_NAME)}"
echo "  distro:  $DISTRO ${DISTRO_VERSION} (kernel $KERNEL)"
[ -n "$CPU" ] && echo "  cpu:     $CPU"
echo "  sleep:   mem_sleep=${MEM_SLEEP:-n/a} s3=$S3 s4=$S4"
echo "  parts:   ${#COMPONENTS_JSON[@]} components"
echo

echo "Uploading probe to $API_URL …"
RESPONSE=$(curl -fsS -X POST "$API_URL/api/probes" \
	-H "Authorization: Bearer $TOKEN" \
	-H "Content-Type: application/json" \
	-d "$PAYLOAD")

LAPTOP_ID=$(printf '%s' "$RESPONSE" | sed -E 's/.*"laptop_id":([0-9]+).*/\1/')
echo "Uploaded: laptop #$LAPTOP_ID"
echo
echo "Now attest what actually works:"
echo "  $API_URL/submit   (or run with --report)"

# ---- interactive attestation ------------------------------------------------------

if [ "$DO_REPORT" = 1 ]; then
	echo
	echo "Report outcomes (Enter = skip, w = works, p = partial, b = broken):"
	OUT_URL="$API_URL"
	# ids come from the probe response, not the local payload
	while IFS=' ' read -r cid ctype; do
		[ -z "$cid" ] && continue
		printf '%-15s ' "$ctype"
		read -r ans </dev/tty || ans=""
		case "$ans" in
		w | p | b) outcome=$([ "$ans" = w ] && echo works || { [ "$ans" = p ] && echo partial || echo broken; }) ;;
		*) continue ;;
		esac
		printf '  note: '
		read -r note </dev/tty || note=""
		curl -fsS -X POST "$OUT_URL/api/reports" \
			-H "Authorization: Bearer $TOKEN" \
			-H "Content-Type: application/json" \
			-H "X-Source: tool" \
			-d "{\"laptop_id\":$LAPTOP_ID,\"component_id\":$cid,\"outcome\":\"$outcome\",\"context\":\"normal\",\"kernel\":\"$(jesc "$KERNEL")\",\"distro\":\"$(jesc "$DISTRO")\",\"distro_version\":\"$(jesc "$DISTRO_VERSION")\",\"de\":\"$(jesc "$DE")\",\"bios\":\"$(jesc "$BIOS_VERSION")\",\"note\":\"$(jesc "$note")\"}" \
			>/dev/null && echo "  → reported $ctype: $outcome"
	done < <(printf '%s' "$RESPONSE" | grep -oE '"id":[0-9]+,"type":"[^"]*"' | sed -E 's/"id":([0-9]+),"type":"([^"]*)"/\1 \2/')
fi

# ---- verified suspend test ----------------------------------------------------------

if [ "$SUSPEND_TEST" = 1 ]; then
	echo
	echo "This will suspend the machine for ~10 seconds. Close work first!"
	printf 'Proceed? [y/N] '
	read -r ans </dev/tty || ans=y
	case "$ans" in
	y | Y) ;;
	*) exit 0 ;;
	esac

	DRIVERS_BEFORE=$(lspci -k 2>/dev/null | grep "Kernel driver in use" | sort)
	echo "Suspending…"
	systemctl suspend || echo "suspend command failed"

	# give wifi/bluetooth a moment to reassociate after wake
	sleep 5
	DRIVERS_AFTER=$(lspci -k 2>/dev/null | grep "Kernel driver in use" | sort)
	RESUME_ERRORS=$(journalctl -k --since "-2 minutes" 2>/dev/null | grep -iE "resume|suspend|failed|error" | tail -5 || true)

	LOST=$(comm -23 <(printf '%s\n' "$DRIVERS_BEFORE") <(printf '%s\n' "$DRIVERS_AFTER") | tr '\n' ';' | sed 's/;$//')
	if [ -n "$LOST" ]; then
		SUS_OUTCOME=broken
		SUS_NOTE="drivers lost after resume: $LOST"
	elif [ -n "$RESUME_ERRORS" ]; then
		SUS_OUTCOME=partial
		SUS_NOTE="resume completed with kernel errors (see journal)"
	else
		SUS_OUTCOME=works
		SUS_NOTE="suspend + resume clean; all drivers rebound"
	fi
	echo "suspend test: $SUS_OUTCOME — $SUS_NOTE"

	curl -fsS -X POST "$API_URL/api/reports" \
		-H "Authorization: Bearer $TOKEN" \
		-H "Content-Type: application/json" \
		-H "X-Source: tool" \
		-d "{\"laptop_id\":$LAPTOP_ID,\"outcome\":\"$SUS_OUTCOME\",\"context\":\"after_suspend\",\"kernel\":\"$(jesc "$KERNEL")\",\"distro\":\"$(jesc "$DISTRO")\",\"de\":\"$(jesc "$DE")\",\"bios\":\"$(jesc "$BIOS_VERSION")\",\"note\":\"$(jesc "$SUS_NOTE")\"}" \
		>/dev/null && echo "reported suspend result (source: verified tool run)"
fi
