#!/usr/bin/env bash

# Every compositor-backed setting must actually change the compositor.
#
# settings-hyprland-write-contract proves this for five settings. There are
# sixty-six, and the ones it does not reach are exactly where a dead setting
# hides: the write path no-ops, nothing fails, nothing logs, and the row simply
# does not do anything. That is what a user reports as "the settings app does
# not work", and it is not visible from any shape-checking test.
#
# Each setting is driven through SystemSettings.commitPreference -- the entry
# point a settings row uses -- flipped to a value it does not hold, read back
# from the live compositor, and put straight back before the next one is
# touched.
#
# This one talks to the running compositor on purpose. Preferences are written
# to an isolated config home so nothing lands in the real settings.json.

set -uo pipefail

repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
harness="$repo_dir/config/dot/quickshell/settings-system-harness.qml"
sweep="$repo_dir/tests/quickshell/settings_write_sweep.py"

fail() {
    printf 'settings write sweep contract: %s\n' "$1" >&2
    exit 1
}

[[ -r "$harness" ]] || fail 'the settings system harness is missing'
[[ -r "$sweep" ]] || fail 'the sweep is missing'

command -v qs >/dev/null 2>&1 || { printf 'settings write sweep contract: SKIP (no quickshell)\n'; exit 0; }
command -v hyprctl >/dev/null 2>&1 || { printf 'settings write sweep contract: SKIP (no compositor)\n'; exit 0; }
command -v jq >/dev/null 2>&1 || { printf 'settings write sweep contract: SKIP (no jq)\n'; exit 0; }
hyprctl -j getoption decoration:rounding >/dev/null 2>&1 \
    || { printf 'settings write sweep contract: SKIP (compositor not answering)\n'; exit 0; }

config_home="$(mktemp -d /tmp/panama-write-sweep.XXXXXX)"

qs_for_harness() {
    XDG_CONFIG_HOME="$config_home" qs -p "$harness" "$@"
}

cleanup() {
    qs_for_harness kill >/dev/null 2>&1 || true
    rm -rf "$config_home"
}
trap cleanup EXIT

# The sibling contract drives the SAME harness file with the compositor seam
# stubbed. Quickshell identifies an instance by config path, so an instance left
# over from that run would answer here and every write would miss the compositor
# entirely -- passing for the worst possible reason.
if qs_for_harness ipc show 2>/dev/null | grep -q '^target settings-system-test$'; then
    fail 'a harness instance is already running; a stale one would answer these writes instead of the compositor'
fi

XDG_CONFIG_HOME="$config_home" PANAMA_SETTINGS_TEST_ISOLATE_COMPOSITOR=0 \
    qs -p "$harness" --daemonize >/dev/null 2>&1

for _ in $(seq 1 40); do
    qs_for_harness ipc show 2>/dev/null | grep -q '^target settings-system-test$' && break
    sleep 0.25
done
qs_for_harness ipc show 2>/dev/null | grep -q '^target settings-system-test$' \
    || fail 'the harness did not start'

result="$(cd "$repo_dir" && XDG_CONFIG_HOME="$config_home" \
    timeout 600 python3 "$sweep" "$config_home" "$harness")" \
    || fail 'the sweep did not finish'

verified="$(jq -r '.verified | length' <<<"$result")"
skipped="$(jq -r '.skipped | length' <<<"$result")"
failed="$(jq -r '.failures | length' <<<"$result")"
total="$(jq -r '.total' <<<"$result")"

if [[ "$failed" != "0" ]]; then
    printf 'settings write sweep contract: %s setting(s) did not reach the compositor:\n' "$failed" >&2
    jq -r '.failures[] | "    \(.[0]): \(.[1])"' <<<"$result" >&2
    exit 1
fi

# Skipping is legitimate for a shape with no scalar to compare, but a sweep that
# skips most of what it was pointed at is not evidence of anything.
if (( verified * 2 < total )); then
    jq -r '.skipped[] | "    \(.[0]): \(.[1])"' <<<"$result" >&2
    fail "only $verified of $total settings were actually exercised"
fi

# ── Settings Panama stores itself ───────────────────────────────────────────
# No compositor to ask, so the question is whether the value comes back out of
# the store. A setting that silently fails to persist is the same dead row.
local_failed="$(jq -r '.localFailures | length' <<<"$result")"
local_verified="$(jq -r '.localVerified | length' <<<"$result")"
local_skipped="$(jq -r '.localSkipped | length' <<<"$result")"

if [[ "$local_failed" != "0" ]]; then
    printf 'settings write sweep contract: %s stored setting(s) did not round-trip:\n' "$local_failed" >&2
    jq -r '.localFailures[] | "    \(.[0]): \(.[1])"' <<<"$result" >&2
    exit 1
fi

printf 'settings write sweep contract: PASS (%d of %d compositor settings verified live, %d skipped; %d stored settings round-tripped, %d skipped)\n' \
    "$verified" "$total" "$skipped" "$local_verified" "$local_skipped"
