#!/usr/bin/env bash

# The sidebar search indexes the schema, not the page names.
#
# Before this, the field filtered twelve page labels, so "gaps", "wallpaper",
# and "screenshot" all returned nothing on an app that has all three. That is
# the single clearest way a settings app feels smaller than it is, and it
# regresses invisibly: nothing breaks, results just quietly stop appearing.
#
# This pins that every schema entry is reachable by its own label, that
# shortcuts are searchable by what they do, and that results route to a page
# that actually exists.

set -euo pipefail

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

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

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

cleanup() {
    qs_for_harness kill >/dev/null 2>&1 || true
}
trap cleanup EXIT

qs_for_harness --daemonize >/dev/null
for _ in $(seq 1 40); do
    qs_for_harness ipc show 2>/dev/null | rg -q '^target settings-search-test$' && break
    sleep 0.1
done
qs_for_harness ipc show 2>/dev/null | rg -q '^target settings-search-test$' || fail 'test IPC target did not start'

find_top() {
    qs_for_harness ipc call settings-search-test find "$1"
}

# ── An empty query is navigation, not a search ───────────────────────────────
[[ "$(find_top '' | jq -r .count)" == "0" ]] || fail 'an empty query returned results'

# ── Real settings are findable by what they are ──────────────────────────────
while IFS='|' read -r query expect_label expect_page; do
    result="$(find_top "$query")"
    got_label="$(jq -r .top <<<"$result")"
    got_page="$(jq -r .topPage <<<"$result")"
    [[ "$got_label" == "$expect_label" ]] \
        || fail "searching '$query' put '$got_label' first, expected '$expect_label'"
    [[ "$got_page" == "$expect_page" ]] \
        || fail "searching '$query' routes to '$got_page', expected '$expect_page'"
done <<'CASES'
wallpaper|Wallpaper|appearance
blur|Blur|appearance
timezone|Timezone|datetime
repeat delay|Repeat delay|shortcuts
system health|System Health|services
doctor|Copy health report|services
lock screen background|Lock screen background|appearance
password field|Password field|appearance
slideshow|Wallpaper mode|appearance
shuffle|Shuffle|appearance
per-display wallpaper|Per-display wallpaper|appearance
arrange displays|Arrange displays|displays
monitor position|Monitor position|displays
primary display|Primary display|displays
CASES

! rg -Fq 'Startup & Services' "$repo_dir/config/dot/quickshell/services/SettingsSearch.qml" \
    || fail 'search index still uses the retired Startup & Services name'

# ── Shortcuts are searchable by what they do ─────────────────────────────────
[[ "$(find_top screenshot | jq -r .topPage)" == "shortcuts" ]] \
    || fail 'searching a shortcut description did not route to the shortcuts page'

# ── Every non-internal schema entry is reachable by its own label ────────────
# A setting that cannot be found by typing its name is a setting the user
# cannot discover, which is the failure this whole page structure exists to fix.
schema="$repo_dir/config/dot/quickshell/config/PreferenceSchema.qml"
missing=0
while IFS= read -r label; do
    [[ -n "$label" ]] || continue
    count="$(find_top "$label" | jq -r .count)"
    if [[ "$count" == "0" ]]; then
        printf 'settings search contract: no result for schema label "%s"\n' "$label" >&2
        missing=$((missing + 1))
    fi
done < <(python3 - "$schema" <<'EXTRACT'
import re, sys
# Entries marked `internal` are shell state the user never edits, so they are
# deliberately excluded from search. Extract only the user-facing labels.
text = open(sys.argv[1]).read()
for block in re.findall(r"\{\s*\n\s+key:.*?\n\s{8}\}", text, re.S):
    if "internal: true" in block:
        continue
    match = re.search(r'label: "([^"]+)"', block)
    if match:
        print(match.group(1))
EXTRACT
)
[[ "$missing" -eq 0 ]] || fail "$missing schema label(s) are not findable by search"

# ── Results only ever route to leaves the shell can open ─────────────────────
# A search result carries a leaf id, never a category: it has to land on the
# exact tab holding the setting, not on whichever tab that category opens
# first. SettingsRoutes is where the leaves are declared -- a tabless category
# is one, and so is every tab.
routes="$repo_dir/config/dot/quickshell/services/SettingsRoutes.qml"
leaves="$( {
    grep -oE '\{ page: "[a-z-]+", label: "[^"]*", icon: "[^"]*", tabs: \[\] \}' "$routes"
    grep -oE '\{ page: "[a-z-]+", label: "[^"]*" \}' "$routes"
} | sed -E 's/\{ page: "([a-z-]+)".*/\1/' | sort -u)"
[[ -n "$leaves" ]] || fail 'no leaf pages could be read from SettingsRoutes, so this proves nothing'
for query in wallpaper blur timezone screenshot pointer lock volume gaps dictation; do
    page="$(find_top "$query" | jq -r .topPage)"
    grep -qx "$page" <<<"$leaves" || fail "search routed '$query' to '$page', which is not a page anyone can land on"
done

trap - EXIT
cleanup
printf 'settings search contract: PASS\n'
