#!/usr/bin/env bash

# A search result must open the page that actually contains the setting.
#
# Every schema group routes to one page in services/SettingsSearch.qml, and the
# ownership rule in modules/settings/README.md derives a setting's owner from
# that route. Nothing checked the two agreed, and two groups had drifted:
#
#   weather routed to Appearance while every weather control lived on Home, so
#   searching "temperature unit" opened a page without it.
#
#   vitals routed to Appearance, but the refresh interval sat on Home while the
#   toggles it governs sat on Appearance -- one concept, two pages, which is
#   precisely what the ownership rule forbids.
#
# Neither is visible from the code: each page is coherent on its own, and only
# following a search result reveals the mismatch.
#
# Groups whose settings are driven by bespoke UI rather than schema-bound rows
# are skipped, because there is nothing to locate.

set -uo pipefail

repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
schema="$repo_dir/config/dot/quickshell/config/PreferenceSchema.qml"
search="$repo_dir/config/dot/quickshell/services/SettingsSearch.qml"
pages_dir="$repo_dir/config/dot/quickshell/modules/settings"
shell_file="$pages_dir/SettingsShell.qml"

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

routes="$(grep -oE '"[a-zA-Z]+": "[a-z-]+"' "$search" | tr -d '"' | tr ':' ' ')"
[[ -n "$routes" ]] || fail 'no group routes found -- this contract is not reading SettingsSearch correctly'

# page id -> Page component file, as SettingsShell maps them.
page_file() {
    # Derived from SettingsShell rather than restated here. This was a
    # hand-written list of every page, which meant adding one made this contract
    # fail with "not a known page" -- a sixth place to register a page, and the
    # sixth chance to forget. The shell already maps page -> component id and
    # component id -> type, and the type names its file.
    local page="$1" component type
    component="$(grep -oE "case \"$page\": return [a-zA-Z]+;" "$shell_file" \
        | sed -E 's/.*return ([a-zA-Z]+);/\1/' | head -1)"
    if [[ -z "$component" ]]; then
        # Home is the switch's default arm rather than a case.
        [[ "$page" == "home" ]] || { printf ''; return; }
        component="homePage"
    fi
    type="$(grep -oE "Component \{ id: $component; [A-Za-z]+ \{\} \}" "$shell_file" \
        | sed -E 's/.*; ([A-Za-z]+) \{\} \}/\1/' | head -1)"
    [[ -n "$type" ]] || { printf ''; return; }
    printf '%s.qml' "$type"
}

violations=0
checked=0

while read -r group page; do
    [[ -n "$group" && -n "$page" ]] || continue

    target="$(page_file "$page")"
    [[ -n "$target" ]] || fail "group \"$group\" routes to \"$page\", which is not a known page"
    [[ -r "$pages_dir/$target" ]] || fail "group \"$group\" routes to \"$page\" but $target does not exist"

    # Keys belonging to this group.
    keys="$(awk -v g="\"$group\"" '
        /key: "/ { match($0, /key: "[a-zA-Z]+"/); k = substr($0, RSTART+6, RLENGTH-7) }
        $0 ~ "group: " g { if (k != "") print k; k = "" }
    ' "$schema" | sort -u)"

    while read -r key; do
        [[ -n "$key" ]] || continue

        # Only settings rendered as schema-bound rows can be located at all.
        homes="$(grep -rl "setting: \"$key\"" "$pages_dir"/*Page.qml 2>/dev/null | xargs -n1 basename 2>/dev/null || true)"
        [[ -n "$homes" ]] || continue

        checked=$((checked + 1))
        grep -qx "$target" <<<"$homes" && continue

        printf '  %s (group "%s") routes to %s but appears only on: %s\n' \
            "$key" "$group" "$page" "$(tr '\n' ' ' <<<"$homes")" >&2
        violations=$((violations + 1))
    done <<<"$keys"
done <<<"$routes"

if (( violations > 0 )); then
    fail "$violations setting(s) route to a page that does not contain them, so searching for them opens the wrong page"
fi

printf 'search routing contract: PASS (%d routed settings)\n' "$checked"
