#!/usr/bin/env bash

# Every compositor-owned preference must be read at config time, not only
# applied live.
#
# A schema entry with `hypr:` metadata gets two delivery paths by design
# (stated in services/SystemSettings.qml): hyprctl eval reaches the running
# compositor, and a prefs.get() in the Lua is what survives `hyprctl reload`
# and the moment before the shell starts. Twenty-one keys once shipped with
# only the live half -- the entire Mouse & Touchpad page quietly reverted on
# every reload, and the schema's touchpad natural-scroll default disagreed
# with Hyprland's, so the page misreported the hardware until the shell's
# startup replay ran.
#
# Two properties, then: every hypr-backed key has a prefs.get() somewhere in
# config/dot/hypr/, and the Lua fallback equals the schema default -- a
# different fallback is the same lie on a different day.

set -uo pipefail

repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"

python3 - "$repo_dir" <<'PY'
import re, sys, pathlib

repo = pathlib.Path(sys.argv[1])
schema = (repo / "config/dot/quickshell/config/PreferenceSchema.qml").read_text()

# Pair each key with its entry body (up to the next key) and keep the ones
# carrying hypr: metadata, along with their declared default.
keys = [(m.group(1), m.start()) for m in re.finditer(r'key: "([A-Za-z]+)"', schema)]
failures = []
backed = {}
for i, (key, pos) in enumerate(keys):
    end = keys[i + 1][1] if i + 1 < len(keys) else len(schema)
    body = schema[pos:end]
    if "hypr: {" not in body:
        continue
    m = re.search(r'def: (\([^)]*\)|"[^"]*"|[-\d.]+|true|false)', body)
    if not m:
        failures.append(f"{key}: hypr-backed but its default could not be read")
        continue
    backed[key] = m.group(1).strip('"')

lua = ""
for path in (repo / "config/dot/hypr").glob("*.lua"):
    lua += path.read_text()

def normalize(value):
    # A bool schema key can back an int compositor option (autoHdr -> render:
    # cm_auto_hdr), read through prefs.getInt; true/1 and false/0 are the
    # same declared default on the two sides.
    v = {"true": "1", "false": "0"}.get(str(value), str(value))
    try:
        return repr(float(v))
    except ValueError:
        return v

for key, default in sorted(backed.items()):
    reads = re.findall(
        r'prefs\.get(?:Int)?\(\s*"' + key + r'"\s*,\s*("[^"]*"|[-\d.]+|true|false)\s*\)', lua)
    if not reads:
        failures.append(f"{key}: no prefs.get() in config/dot/hypr -- reverts on hyprctl reload")
        continue
    for fallback in reads:
        if normalize(fallback.strip('"')) != normalize(default):
            failures.append(
                f"{key}: Lua fallback {fallback} disagrees with schema default {default!r}")

if failures:
    print(f"hypr prefs contract: {len(failures)} finding(s)")
    for failure in failures:
        print(f"  - {failure}")
    sys.exit(1)
print(f"hypr prefs contract: ok ({len(backed)} compositor-owned keys read at config time)")
PY
