#!/bin/bash

set -euo pipefail

ROOT=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
CLI="$ROOT/bin/omarchy"
TMPDIRS=()

export PATH="$ROOT/bin:$PATH"

# Assigns a fresh temporary directory to the named variable and registers it for
# removal on exit, so every scratch directory this suite makes is cleaned up.
make_tmpdir() {
  local -n dir_ref="$1"

  dir_ref=$(mktemp -d)
  TMPDIRS+=("$dir_ref")
}

pass() {
  printf 'ok - %s\n' "$1"
}

fail() {
  printf 'not ok - %s\n' "$1" >&2
  exit 1
}

assert_output_contains() {
  local description="$1"
  local output="$2"
  local expected="$3"

  if [[ $output != *"$expected"* ]]; then
    printf 'Expected output to contain: %s\n' "$expected" >&2
    printf 'Actual output:\n%s\n' "$output" >&2
    fail "$description"
  fi

  pass "$description"
}

cleanup() {
  local dir

  for dir in ${TMPDIRS[@]+"${TMPDIRS[@]}"}; do
    [[ -d $dir ]] && rm -rf "$dir"
  done

  return 0
}
trap cleanup EXIT

output=$("$CLI" --help)
assert_output_contains "main help renders" "$output" "Omarchy command center"
assert_output_contains "main help includes hardware group" "$output" "hw"
assert_output_contains "main help includes package group" "$output" "pkg"
if grep -Eq '^  [a-z0-9-]+[[:space:]].*\([0-9]+\)$' <<<"$output"; then
  fail "main help does not show group counts"
fi
pass "main help does not show group counts"

output=$("$CLI" commands)
assert_output_contains "commands lists documented commands" "$output" "omarchy theme set <theme-name>"

"$CLI" commands --json | jq -e '.ok == true and (.commands | length >= 200)' >/dev/null
pass "commands --json is valid JSON with full bin coverage"

"$CLI" commands --json | jq -e 'all(.commands[]; .summary != "undocumented")' >/dev/null
pass "all included commands have summaries"

"$CLI" commands --json | jq -e 'all(.commands[]; has("binary") and has("filename_route") and has("routes") and (has("legacy") | not) and (has("usage") | not) and (has("visibility") | not) and (has("mutates") | not) and (has("interactive") | not))' >/dev/null
pass "JSON uses binary/routes and omits legacy/usage/extra metadata"

"$CLI" commands --check >/dev/null
pass "commands --check passes"

"$CLI" commands --all >/dev/null
pass "commands --all does not crash"

"$CLI" commands --all --json | jq -e '.commands[] | select(.route == "omarchy hyprland window gaps toggle" and .summary != "undocumented")' >/dev/null
pass "fallback commands are inferred and documented"

"$CLI" commands --all --json | jq -e '.commands[] | select(.route == "omarchy dev benchmark cli")' >/dev/null
pass "benchmark command is discoverable in all commands"

"$CLI" commands --json | jq -e '.commands[] | select(.binary == "omarchy-pkg-add" and .route == "omarchy pkg add" and .filename_route == "omarchy pkg add" and (.routes | index("omarchy pkg add")))' >/dev/null
pass "JSON exposes direct pkg add route"

"$CLI" commands --json | jq -e '.commands[] | select(.binary == "omarchy-install-gaming-xbox-cloud" and .route == "omarchy install gaming xbox-cloud" and .filename_route == "omarchy install gaming xbox cloud" and (.routes | index("omarchy install gaming xbox cloud")))' >/dev/null
pass "Xbox Cloud installer keeps hyphenated canonical route"

"$CLI" commands --json | jq -e '.commands[] | select(.binary == "omarchy-install-gaming-xbox-controllers" and .route == "omarchy install gaming xbox-controllers" and .filename_route == "omarchy install gaming xbox controllers" and (.routes | index("omarchy install gaming xbox controllers")))' >/dev/null
pass "Xbox controller installer keeps hyphenated canonical route"

"$CLI" commands --json | jq -e '.commands[] | select(.binary == "omarchy-remove-gaming-xbox-controllers" and .route == "omarchy remove gaming xbox-controllers" and .filename_route == "omarchy remove gaming xbox controllers" and (.routes | index("omarchy remove gaming xbox controllers")))' >/dev/null
pass "Xbox controller remover keeps hyphenated canonical route"

"$CLI" commands --json | jq -e '.commands[] | select(.binary == "omarchy-install-gaming-geforce-now" and .route == "omarchy install gaming geforce-now" and .filename_route == "omarchy install gaming geforce now" and (.routes | index("omarchy install gaming geforce now")))' >/dev/null
pass "GeForce NOW installer keeps hyphenated canonical route"

"$CLI" commands --json | jq -e '.commands[] | select(.binary == "omarchy-remove-gaming-geforce-now" and .route == "omarchy remove gaming geforce-now" and .filename_route == "omarchy remove gaming geforce now" and (.routes | index("omarchy remove gaming geforce now")))' >/dev/null
pass "GeForce NOW remover keeps hyphenated canonical route"

"$CLI" commands --json | jq -e '.commands[] | select(.binary == "omarchy-install-gaming-gpu-lib32" and .route == "omarchy install gaming gpu-lib32" and .filename_route == "omarchy install gaming gpu lib32" and (.routes | index("omarchy install gaming gpu lib32")))' >/dev/null
pass "GPU lib32 installer keeps hyphenated canonical route"

"$CLI" commands --json | jq -e '.commands[] | select(.binary == "omarchy-refresh-pacman" and .requires_sudo == true)' >/dev/null
pass "sudo metadata marks sudo commands"

output=$("$CLI" theme --help)
assert_output_contains "group help renders" "$output" "Theme commands"

output=$("$CLI" install --help)
assert_output_contains "install group help renders" "$output" "Install commands"
assert_output_contains "install group includes browser route" "$output" "omarchy install browser"

output=$("$CLI" install)
assert_output_contains "bare group renders help instead of picker" "$output" "Install commands"
assert_output_contains "bare group includes browser route" "$output" "omarchy install browser"

output=$("$CLI" toggle)
assert_output_contains "bare root command with children renders help" "$output" "Toggle commands"
assert_output_contains "bare toggle help includes child route" "$output" "omarchy toggle nightlight"

output=$("$CLI" pkg --help)
assert_output_contains "package group includes pkg add fallback route" "$output" "omarchy pkg add <packages...>"

output=$("$CLI" restart --help)
assert_output_contains "restart group includes inferred commands" "$output" "omarchy restart btop"
assert_output_contains "restart group includes all restart commands" "$output" "omarchy restart wifi"

output=$("$CLI" hw --help)
assert_output_contains "hardware group help renders" "$output" "omarchy hw asus rog"
assert_output_contains "hardware group includes touchpad" "$output" "omarchy hw touchpad"

output=$("$CLI" hw asus)
assert_output_contains "partial hardware prefix renders matching commands" "$output" "omarchy hw asus rog"
assert_output_contains "partial hardware prefix includes nested match" "$output" "omarchy hw asus zenbook ux5406aa"

output=$("$CLI" menu --help)
assert_output_contains "menu group includes share fallback route" "$output" "omarchy menu share"

output=$("$CLI" share)
assert_output_contains "bare required-arg alias renders CLI help" "$output" "Usage:"
assert_output_contains "bare share help uses canonical route" "$output" "omarchy share <clipboard|file|folder> [path...]"

output=$("$CLI" menu share)
assert_output_contains "bare required-arg filename route renders CLI help" "$output" "omarchy share <clipboard|file|folder> [path...]"

CLI="$CLI" python3 <<'PY'
import json
import os
import subprocess
import sys

cli = os.environ['CLI']
commands = json.loads(subprocess.check_output([cli, 'commands', '--json'], text=True))['commands']
by_group = {}
for command in commands:
  binary = command['binary']
  stem = binary.removeprefix('omarchy-')
  group = stem.split('-', 1)[0]
  filename_route = 'omarchy ' + stem.replace('-', ' ')
  by_group.setdefault(group, []).append((binary, filename_route, command['route']))

missing = []
for group, rows in sorted(by_group.items()):
  proc = subprocess.run([cli, group, '--help'], text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  output = proc.stdout + proc.stderr
  if proc.returncode != 0:
    missing.append((group, '<group-help-failed>', f'exit {proc.returncode}'))
    continue
  for binary, filename_route, canonical_route in rows:
    if filename_route not in output and canonical_route not in output and binary not in output:
      missing.append((group, binary, filename_route))

if missing:
  for row in missing:
    print('\t'.join(row), file=sys.stderr)
  sys.exit(1)
PY
pass "every filename-derived group help represents its bins"

output=$(timeout 5 "$CLI" theme set --help)
assert_output_contains "command help renders without executing" "$output" "Binary:"
assert_output_contains "theme set help names binary" "$output" "omarchy-theme-set"

output=$(timeout 5 "$CLI" update --help)
assert_output_contains "mutating command help does not execute target" "$output" "omarchy-update"
if [[ $output == *"omarchy update perform"* ]]; then
  fail "hidden update perform compatibility wrapper is not shown"
fi
pass "hidden update perform compatibility wrapper is not shown"

# These three are install-time plumbing the ISO and install leaves call, so
# they stay out of the listing a user browses -- while still routing, because
# those callers use the CLI. A GROUP_DESCRIPTIONS entry would put the group
# back in the top-level listing even with every command in it hidden, which is
# why apply has none.
output=$(timeout 5 "$CLI")
if [[ $output == *"apply"* ]]; then
  fail "a group whose commands are all hidden stays out of the top-level listing"
fi
pass "a group whose commands are all hidden stays out of the top-level listing"

for internal in omarchy-apply-system omarchy-apply-hardware omarchy-apply-lock; do
  if "$CLI" commands --json | jq -e --arg binary "$internal" \
    '.commands[] | select(.binary == $binary)' >/dev/null; then
    fail "$internal stays out of the default command listing"
  fi

  "$CLI" commands --all --json | jq -e --arg binary "$internal" \
    '.commands[] | select(.binary == $binary) | select(.hidden)' >/dev/null ||
    fail "$internal is still listed by commands --all"
done
pass "install-time setup plumbing is hidden from the setup wizards"

output=$(timeout 5 "$CLI" apply hardware --help)
assert_output_contains "hidden setup plumbing still routes" "$output" "omarchy-apply-hardware"

output=$("$CLI" screenshot --help)
assert_output_contains "root alias resolves to command help" "$output" "omarchy-capture-screenshot"

"$CLI" commands --json | jq -e '.commands[] | select(.binary == "omarchy-capture-screenshot") | .aliases | index("omarchy screenshot")' >/dev/null
pass "aliases are included in JSON metadata"

output=$("$CLI" pkg add --help)
assert_output_contains "pkg add help resolves" "$output" "omarchy-pkg-add"
assert_output_contains "pkg add help shows direct route" "$output" "omarchy pkg add <packages...>"

output=$("$CLI" system reboot --help)
assert_output_contains "system command help is safe" "$output" "omarchy-system-reboot"

output=$("$CLI" dev benchmark cli --repeat=1)
assert_output_contains "benchmark command runs" "$output" "Omarchy CLI benchmark"

"$CLI" theme list >/dev/null
pass "safe dispatch works for theme list"

"$CLI" theme current >/dev/null
pass "safe dispatch works for theme current"

"$CLI" font list >/dev/null
pass "safe dispatch works for font list"

"$CLI" font current >/dev/null
pass "safe dispatch works for font current"

make_tmpdir PI_TMPDIR
NEXT_THEME="$PI_TMPDIR/.local/state/omarchy/current/next-theme"
CURRENT_THEME="$PI_TMPDIR/.local/state/omarchy/current/theme"
USER_THEMED="$PI_TMPDIR/.config/omarchy/themed"
mkdir -p "$NEXT_THEME" "$CURRENT_THEME" "$USER_THEMED"

cat >"$NEXT_THEME/colors.toml" <<'EOF'
mode = "light"
accent = "#336699"
hyprland_active_border = "rgba(010203ee) rgba(040506ee) 45deg"
background = "#000000"
dark_background = "#010101"
darker_background = "#020202"
lighter_background = "#030303"
foreground = "#ffffff"
dark_foreground = "#cccccc"
light_foreground = "#dddddd"
selection = "#808080"
red = "#ff0000"
green = "#00ff00"
yellow = "#ffff00"
blue = "#0000ff"
magenta = "#ff00ff"
cyan = "#00ffff"
muted = "#111111"
bright_red = "#ff1111"
bright_green = "#11ff11"
bright_yellow = "#ffff11"
bright_blue = "#1111ff"
bright_magenta = "#ff11ff"
bright_cyan = "#11ffff"
bright_foreground = "#eeeeee"

# Canonical values must win when a transitional theme defines both forms.
bg = "#abcdef"
dark_bg = "#abcdef"
darker_bg = "#abcdef"
lighter_bg = "#abcdef"
fg = "#abcdef"
dark_fg = "#abcdef"
light_fg = "#abcdef"
bright_fg = "#abcdef"
EOF

cat >"$USER_THEMED/mix-test.txt.tpl" <<'EOF'
mix={{ mix background foreground 50% }}
strip={{ mix_strip background foreground 50% }}
rgb={{ mix_rgb background foreground 50% }}
EOF

cat >"$USER_THEMED/gradient-test.txt.tpl" <<'EOF'
hypr={{ hypr_gradient hyprland_active_border accent }}
shell={{ gradient_start hyprland_active_border accent }}
shell-gradient={{ shell_gradient hyprland_active_border accent }}
fallback-hypr={{ hypr_gradient missing accent }}
fallback-shell={{ gradient_start missing accent }}
fallback-shell-gradient={{ shell_gradient missing accent }}
EOF

cat >"$USER_THEMED/alias-test.txt.tpl" <<'EOF'
color={{ color1 }}
strip={{ color1_strip }}
rgb={{ color1_rgb }}
magenta={{ magenta }}
legacy-purple={{ purple }}
bright-magenta={{ bright_magenta }}
legacy-bright-purple={{ bright_purple }}
background={{ background }}
foreground={{ foreground }}
legacy-background={{ bg }}
legacy-dark-background={{ dark_bg }}
legacy-darker-background={{ darker_bg }}
legacy-lighter-background={{ lighter_bg }}
legacy-foreground={{ fg }}
legacy-dark-foreground={{ dark_fg }}
legacy-light-foreground={{ light_fg }}
legacy-bright-foreground={{ bright_fg }}
legacy-mix={{ mix bg fg 50% }}
selection-background={{ selection_background }}
selection-foreground={{ selection_foreground }}
cursor-alias={{ cursor }}
EOF

cat >"$NEXT_THEME/shell.lock.toml" <<'EOF'
text = "#ffffff"
placeholder = "#ffffff"
border = "#ffffff"
EOF

OMARCHY_PATH="$ROOT" HOME="$PI_TMPDIR" "$ROOT/bin/omarchy-theme-set-templates"
grep -qx 'mix=#808080' "$NEXT_THEME/mix-test.txt" || fail "theme template mix helper works"
grep -qx 'strip=808080' "$NEXT_THEME/mix-test.txt" || fail "theme template mix_strip helper works"
grep -qx 'rgb=128,128,128' "$NEXT_THEME/mix-test.txt" || fail "theme template mix_rgb helper works"
pass "theme template color mix helpers work"

grep -qx 'color=#ff0000' "$NEXT_THEME/alias-test.txt" || fail "theme template semantic aliases expose legacy colorN"
grep -qx 'strip=ff0000' "$NEXT_THEME/alias-test.txt" || fail "theme template legacy colorN_strip works"
grep -qx 'rgb=255,0,0' "$NEXT_THEME/alias-test.txt" || fail "theme template legacy colorN_rgb works"
grep -qx 'magenta=#ff00ff' "$NEXT_THEME/alias-test.txt" || fail "theme template magenta helper works"
grep -qx 'legacy-purple=#ff00ff' "$NEXT_THEME/alias-test.txt" || fail "theme template purple alias works"
grep -qx 'bright-magenta=#ff11ff' "$NEXT_THEME/alias-test.txt" || fail "theme template bright magenta helper works"
grep -qx 'legacy-bright-purple=#ff11ff' "$NEXT_THEME/alias-test.txt" || fail "theme template bright purple alias works"
grep -qx 'background=#000000' "$NEXT_THEME/alias-test.txt" || fail "theme template resolves background"
grep -qx 'foreground=#ffffff' "$NEXT_THEME/alias-test.txt" || fail "theme template resolves foreground"
grep -qx 'legacy-background=#000000' "$NEXT_THEME/alias-test.txt" || fail "theme template exposes legacy bg alias"
grep -qx 'legacy-dark-background=#010101' "$NEXT_THEME/alias-test.txt" || fail "theme template exposes legacy dark_bg alias"
grep -qx 'legacy-darker-background=#020202' "$NEXT_THEME/alias-test.txt" || fail "theme template exposes legacy darker_bg alias"
grep -qx 'legacy-lighter-background=#030303' "$NEXT_THEME/alias-test.txt" || fail "theme template exposes legacy lighter_bg alias"
grep -qx 'legacy-foreground=#ffffff' "$NEXT_THEME/alias-test.txt" || fail "theme template exposes legacy fg alias"
grep -qx 'legacy-dark-foreground=#cccccc' "$NEXT_THEME/alias-test.txt" || fail "theme template exposes legacy dark_fg alias"
grep -qx 'legacy-light-foreground=#dddddd' "$NEXT_THEME/alias-test.txt" || fail "theme template exposes legacy light_fg alias"
grep -qx 'legacy-bright-foreground=#eeeeee' "$NEXT_THEME/alias-test.txt" || fail "theme template exposes legacy bright_fg alias"
grep -qx 'legacy-mix=#808080' "$NEXT_THEME/alias-test.txt" || fail "theme template mix helper accepts legacy palette aliases"
grep -qx 'selection-background=#808080' "$NEXT_THEME/alias-test.txt" || fail "theme template derives selection background from selection"
grep -qx 'selection-foreground=#eeeeee' "$NEXT_THEME/alias-test.txt" || fail "theme template derives selection foreground from bright_foreground"
grep -qx 'cursor-alias=#eeeeee' "$NEXT_THEME/alias-test.txt" || fail "theme template derives cursor alias from bright_foreground"
pass "theme template semantic aliases expose legacy palette helpers"

LEGACY_COLORS_FILE="$PI_TMPDIR/legacy-colors.toml"
cat >"$LEGACY_COLORS_FILE" <<'EOF'
mode = "dark"
accent = "#336699"
selection = "#292929"
muted = "#404040"
bg = "#101010"
dark_bg = "#111111"
darker_bg = "#121212"
lighter_bg = "#131313"
fg = "#f0f0f0"
dark_fg = "#d0d0d0"
light_fg = "#e0e0e0"
bright_fg = "#ffffff"
red = "#ff0000"
green = "#00ff00"
yellow = "#ffff00"
blue = "#0000ff"
magenta = "#ff00ff"
cyan = "#00ffff"
bright_red = "#ff1111"
bright_green = "#11ff11"
bright_yellow = "#ffff11"
bright_blue = "#1111ff"
bright_magenta = "#ff11ff"
bright_cyan = "#11ffff"
EOF

while read -r canonical expected; do
  actual=$("$ROOT/bin/omarchy-theme-color" --file "$LEGACY_COLORS_FILE" "$canonical")
  [[ $actual == "$expected" ]] || fail "legacy palette resolves $canonical"
done <<'EOF'
background #101010
dark_background #111111
darker_background #121212
lighter_background #131313
foreground #f0f0f0
dark_foreground #d0d0d0
light_foreground #e0e0e0
bright_foreground #ffffff
EOF
pass "legacy short palette resolves every canonical neutral color"

LEGACY_THEME_HOME="$PI_TMPDIR/legacy-theme-home"
LEGACY_NEXT_THEME="$LEGACY_THEME_HOME/.local/state/omarchy/current/next-theme"
mkdir -p "$LEGACY_NEXT_THEME"
cp "$LEGACY_COLORS_FILE" "$LEGACY_NEXT_THEME/colors.toml"
OMARCHY_PATH="$ROOT" HOME="$LEGACY_THEME_HOME" "$ROOT/bin/omarchy-theme-set-templates"
grep -qx 'background = #101010' "$LEGACY_NEXT_THEME/ghostty.conf" || fail "legacy theme generates terminal background"
grep -qx 'foreground = #f0f0f0' "$LEGACY_NEXT_THEME/ghostty.conf" || fail "legacy theme generates terminal foreground"
grep -Fq 'dark_bg = "#111111"' "$LEGACY_NEXT_THEME/neovim.lua" || fail "legacy theme preserves dark_bg in generated editor theme"
grep -Fq 'lighter_bg = "#131313"' "$LEGACY_NEXT_THEME/neovim.lua" || fail "legacy theme preserves lighter_bg in generated editor theme"
grep -Fq 'dark_fg = "#d0d0d0"' "$LEGACY_NEXT_THEME/neovim.lua" || fail "legacy theme preserves dark_fg in generated editor theme"
grep -Fq 'bright_fg = "#ffffff"' "$LEGACY_NEXT_THEME/neovim.lua" || fail "legacy theme preserves bright_fg in generated editor theme"
if rg -q '\{\{[^}]+\}\}' "$LEGACY_NEXT_THEME"; then
  fail "legacy theme renders every template placeholder"
fi
pass "legacy short palette renders the complete generated theme"

osc_summary=$("$ROOT/bin/omarchy-theme-osc" "$NEXT_THEME/colors.toml" | python -c 'import sys; data = sys.stdin.buffer.read(); print(data.count(b"]4;"), b"]4;1;#ff0000" in data, b"]4;7;#ffffff" in data, b"]4;15;#eeeeee" in data, b"]12;#eeeeee" in data, b"]17;#808080" in data, b"]19;#eeeeee" in data)')
[[ $osc_summary == "16 True True True True True True" ]] || fail "theme OSC aliases semantic colors to ANSI palette"
pass "theme OSC aliases semantic colors to ANSI palette"

preview_output=$(OMARCHY_PATH="$ROOT" HOME="$PI_TMPDIR" "$ROOT/bin/omarchy-dev-theme-preview" "$NEXT_THEME/colors.toml" --no-color)
assert_output_contains "theme preview shows background to bright_foreground gradient" "$preview_output" "background -> bright_foreground gradient"
assert_output_contains "theme preview orders light mode ramp light to dark" "$preview_output" "Neutral ramp (lightest -> darkest)"
assert_output_contains "theme preview includes dark background slots" "$preview_output" "darker_background"
assert_output_contains "theme preview shows selected text sample" "$preview_output" "This is some [selected text] in a sentence"
assert_output_contains "theme preview shows practical terminal samples" "$preview_output" "Terminal/UI samples"
assert_output_contains "theme preview shows ANSI palette strips" "$preview_output" "ANSI palette strips"

tokyo_fg=$(awk -F'"' '/^foreground =/ { print $2; exit }' "$ROOT/themes/tokyo-night/colors.toml")
tokyo_light_fg=$(awk -F'"' '/^light_foreground =/ { print $2; exit }' "$ROOT/themes/tokyo-night/colors.toml")
tokyo_bright_fg=$(awk -F'"' '/^bright_foreground =/ { print $2; exit }' "$ROOT/themes/tokyo-night/colors.toml")
[[ $tokyo_fg == "#a9b1d6" && $tokyo_light_fg == "#b4bee6" && $tokyo_bright_fg == "#c0caf5" ]] || fail "tokyo night foreground ramp stays ordered"
pass "tokyo night foreground ramp stays ordered"

if rg -q '^cursor\s*=' "$ROOT/themes" -g'colors.toml'; then
  fail "built-in colors.toml files omit cursor token"
fi
pass "built-in colors.toml files omit cursor token"

grep -qx 'hypr={ colors = { "rgba(010203ee)", "rgba(040506ee)" }, angle = 45 }' "$NEXT_THEME/gradient-test.txt" || fail "theme template hypr_gradient helper works"
grep -qx 'shell=#010203' "$NEXT_THEME/gradient-test.txt" || fail "theme template gradient_start helper works"
grep -qx 'shell-gradient=rgba(010203ee) rgba(040506ee) 45deg' "$NEXT_THEME/gradient-test.txt" || fail "theme template shell_gradient helper works"
grep -qx 'fallback-hypr="#336699"' "$NEXT_THEME/gradient-test.txt" || fail "theme template hypr_gradient fallback works"
grep -qx 'fallback-shell=#336699' "$NEXT_THEME/gradient-test.txt" || fail "theme template gradient_start fallback works"
grep -qx 'fallback-shell-gradient=#336699' "$NEXT_THEME/gradient-test.txt" || fail "theme template shell_gradient fallback works"
pass "theme template gradient helpers work"

awk '
  /^\[lock\]$/ { in_lock = 1; next }
  /^\[/ { in_lock = 0 }
  in_lock && /^text = "#ffffff"$/ { text = 1 }
  in_lock && /^placeholder = "#ffffff"$/ { placeholder = 1 }
  in_lock && /^border = "#ffffff"$/ { border = 1 }
  END { exit(text && placeholder && border ? 0 : 1) }
' "$NEXT_THEME/shell.toml" || fail "theme shell section override is merged"
pass "theme shell section override is merged"

jq -e '.name == "omarchy-system" and .vars.panel == "#0f0f0f" and .vars.border == "#4d4d4d" and .colors.accent == "accent"' "$NEXT_THEME/pi.json" >/dev/null
pass "pi theme template is generated from standard themed templates"
cp "$NEXT_THEME/pi.json" "$CURRENT_THEME/pi.json"

echo '{"name":"omarchy-system","vars":{"custom":"yes"}}' >"$NEXT_THEME/pi.json"
OMARCHY_PATH="$ROOT" HOME="$PI_TMPDIR" "$ROOT/bin/omarchy-theme-set-templates"
jq -e '.name == "omarchy-system" and .vars.custom == "yes"' "$NEXT_THEME/pi.json" >/dev/null
pass "theme-specific pi.json overrides the default template"

HOME="$PI_TMPDIR" "$ROOT/bin/omarchy-theme-set-pi"
[[ ! -d $PI_TMPDIR/.pi ]] || fail "pi theme sync skips missing Pi agent when not activating"
pass "pi theme sync skips missing Pi agent when not activating"

HOME="$PI_TMPDIR" "$ROOT/bin/omarchy-theme-set-pi" --activate
[[ -f $PI_TMPDIR/.pi/agent/themes/omarchy-system.json ]] || fail "pi theme file is synced"
pass "pi theme file is synced"
[[ -f $PI_TMPDIR/.pi/agent/settings.json ]] || fail "pi settings file is generated"
pass "pi settings file is generated"
jq -e '.name == "omarchy-system" and .vars.panel == "#0f0f0f"' "$PI_TMPDIR/.pi/agent/themes/omarchy-system.json" >/dev/null
pass "pi synced theme JSON is valid"
jq -e '.theme == "omarchy-system"' "$PI_TMPDIR/.pi/agent/settings.json" >/dev/null
pass "pi generated theme is activated"

jq '.model = "keep-me"' "$PI_TMPDIR/.pi/agent/settings.json" >"$PI_TMPDIR/settings.json.tmp"
mv "$PI_TMPDIR/settings.json.tmp" "$PI_TMPDIR/.pi/agent/settings.json"
HOME="$PI_TMPDIR" "$ROOT/bin/omarchy-theme-set-pi" --activate
jq -e '.theme == "omarchy-system" and .model == "keep-me"' "$PI_TMPDIR/.pi/agent/settings.json" >/dev/null
pass "pi theme activation preserves existing settings"

jq -e '.name == "Omarchy" and .base == "light" and .overrides.text == "#ffffff"' "$NEXT_THEME/claude.json" >/dev/null
pass "claude theme template is generated from standard themed templates"
cp "$NEXT_THEME/claude.json" "$CURRENT_THEME/claude.json"

CLAUDE_TEST_CONFIG="$PI_TMPDIR/.claude-work"
mkdir -p "$CLAUDE_TEST_CONFIG"
HOME="$PI_TMPDIR" CLAUDE_CONFIG_DIR="$CLAUDE_TEST_CONFIG" "$ROOT/bin/omarchy-theme-set-claude"
[[ -f $CLAUDE_TEST_CONFIG/themes/omarchy.json ]] || fail "claude theme is synced to the configured directory"
[[ ! -d $PI_TMPDIR/.claude ]] || fail "claude theme sync avoids the default directory when configured"
pass "claude theme sync respects CLAUDE_CONFIG_DIR"

echo '{"model":"keep-me"}' >"$CLAUDE_TEST_CONFIG/settings.json"
HOME="$PI_TMPDIR" CLAUDE_CONFIG_DIR="$CLAUDE_TEST_CONFIG" "$ROOT/bin/omarchy-theme-set-claude" --activate
jq -e '.theme == "custom:omarchy" and .model == "keep-me"' "$CLAUDE_TEST_CONFIG/settings.json" >/dev/null
pass "claude theme activation preserves configured settings"

make_tmpdir THEME_TMPDIR
OMARCHY_PATH="$ROOT" HOME="$THEME_TMPDIR" OMARCHY_THEME_HEADLESS=1 "$ROOT/bin/omarchy-theme-set" "Tokyo Night"
background_path=$(readlink -f "$THEME_TMPDIR/.local/state/omarchy/current/background" 2>/dev/null || true)
expected_background="$THEME_TMPDIR/.local/state/omarchy/current/theme/backgrounds/0-winding-road.webp"
[[ $background_path == "$expected_background" ]] || fail "headless theme set creates current background symlink"
[[ -f $background_path ]] || fail "headless theme background target exists"
[[ ! -e $THEME_TMPDIR/.config/omarchy/current ]] || fail "headless theme set avoids config current state"
pass "headless theme set creates current background symlink"

make_tmpdir MIGRATION_TMPDIR
mkdir -p \
  "$MIGRATION_TMPDIR/.config/omarchy/current/theme" \
  "$MIGRATION_TMPDIR/.config/alacritty" \
  "$MIGRATION_TMPDIR/.config/hypr" \
  "$MIGRATION_TMPDIR/.config/hyprland-preview-share-picker" \
  "$MIGRATION_TMPDIR/.config/btop/themes"
printf 'tokyo-night\n' >"$MIGRATION_TMPDIR/.config/omarchy/current/theme.name"
printf 'x\n' >"$MIGRATION_TMPDIR/.config/omarchy/current/theme/colors.toml"
printf 'general.import = [ "~/.config/omarchy/current/theme/alacritty.toml" ]\n' >"$MIGRATION_TMPDIR/.config/alacritty/alacritty.toml"
printf 'stylesheets: ["../omarchy/current/theme/hyprland-preview-share-picker.css"]\n' >"$MIGRATION_TMPDIR/.config/hyprland-preview-share-picker/config.yaml"
cat >"$MIGRATION_TMPDIR/.config/hypr/hyprland.lua" <<'LUA'
package.path = os.getenv("HOME")
  .. "/.config/?.lua;"
  .. (os.getenv("OMARCHY_PATH") or "/usr/share/omarchy")
  .. "/?.lua;"
  .. package.path
LUA
ln -s "$MIGRATION_TMPDIR/.config/omarchy/current/theme/btop.theme" "$MIGRATION_TMPDIR/.config/btop/themes/current.theme"
HOME="$MIGRATION_TMPDIR" bash -euo pipefail "$ROOT/migrations/1781043107.sh" >/dev/null
[[ -f $MIGRATION_TMPDIR/.local/state/omarchy/current/theme.name ]] || fail "current theme migration moves theme name"
[[ ! -e $MIGRATION_TMPDIR/.config/omarchy/current ]] || fail "current theme migration removes legacy config state"
grep -Fq '~/.local/state/omarchy/current/theme/alacritty.toml' "$MIGRATION_TMPDIR/.config/alacritty/alacritty.toml" || fail "current theme migration updates alacritty import"
grep -Fq '../../.local/state/omarchy/current/theme/hyprland-preview-share-picker.css' "$MIGRATION_TMPDIR/.config/hyprland-preview-share-picker/config.yaml" || fail "current theme migration updates relative stylesheet"
grep -Fq '/.local/state/?.lua;' "$MIGRATION_TMPDIR/.config/hypr/hyprland.lua" || fail "current theme migration keeps Hyprland theme modules discoverable"
[[ $(readlink "$MIGRATION_TMPDIR/.config/btop/themes/current.theme") == "$MIGRATION_TMPDIR/.local/state/omarchy/current/theme/btop.theme" ]] || fail "current theme migration updates btop symlink"
pass "current theme migration moves state and rewrites shipped references"

make_tmpdir NVIM_MIGRATION_TMPDIR
nvim_theme_link="$NVIM_MIGRATION_TMPDIR/.config/nvim/lua/plugins/theme.lua"
nvim_expected_target="../../../../.local/state/omarchy/current/theme/neovim.lua"
nvim_expected_resolved_target="$NVIM_MIGRATION_TMPDIR/.local/state/omarchy/current/theme/neovim.lua"
mkdir -p \
  "$(dirname "$nvim_theme_link")" \
  "$NVIM_MIGRATION_TMPDIR/.config/omarchy/current/theme" \
  "$NVIM_MIGRATION_TMPDIR/.local/state/omarchy/current/theme"
touch "$NVIM_MIGRATION_TMPDIR/.local/state/omarchy/current/theme/neovim.lua"
for nvim_legacy_target in \
  "../../../omarchy/current/theme/neovim.lua" \
  "$NVIM_MIGRATION_TMPDIR/.config/omarchy/current/theme/neovim.lua"; do
  ln -sfn "$nvim_legacy_target" "$nvim_theme_link"
  HOME="$NVIM_MIGRATION_TMPDIR" bash -euo pipefail "$ROOT/migrations/1781158082.sh" >/dev/null
  nvim_actual_target=$(readlink "$nvim_theme_link")
  nvim_resolved_target=$(readlink -f "$nvim_theme_link")
  [[ $nvim_actual_target == $nvim_expected_target ]] || fail "nvim theme migration updates theme symlink"
  [[ $nvim_resolved_target == $nvim_expected_resolved_target ]] || fail "nvim theme migration points to current theme"
done
pass "nvim theme migration relinks current theme"

for nvim_legacy_target in \
  "../../../omarchy/current/theme/neovim.lua" \
  "../../../../.config/omarchy/current/theme/neovim.lua" \
  "~/.config/omarchy/current/theme/neovim.lua" \
  "$NVIM_MIGRATION_TMPDIR/.config/omarchy/current/theme/neovim.lua"; do
  ln -sfn "$nvim_legacy_target" "$nvim_theme_link"
  HOME="$NVIM_MIGRATION_TMPDIR" bash -euo pipefail "$ROOT/migrations/1785002349.sh" >/dev/null
  nvim_actual_target=$(readlink "$nvim_theme_link")
  nvim_resolved_target=$(readlink -f "$nvim_theme_link")
  [[ $nvim_actual_target == $nvim_expected_target ]] || fail "nvim theme repair migration updates $nvim_legacy_target"
  [[ $nvim_resolved_target == $nvim_expected_resolved_target ]] || fail "nvim theme repair migration points to current theme"
done

HOME="$NVIM_MIGRATION_TMPDIR" bash -euo pipefail "$ROOT/migrations/1785002349.sh" >/dev/null
[[ $(readlink "$nvim_theme_link") == $nvim_expected_target ]] || fail "nvim theme repair migration is idempotent"

touch "$NVIM_MIGRATION_TMPDIR/custom-theme.lua"
ln -sfn "../../../../custom-theme.lua" "$nvim_theme_link"
HOME="$NVIM_MIGRATION_TMPDIR" bash -euo pipefail "$ROOT/migrations/1785002349.sh" >/dev/null
[[ $(readlink "$nvim_theme_link") == "../../../../custom-theme.lua" ]] || fail "nvim theme repair migration leaves custom symlinks alone"
pass "nvim theme repair migration relinks every legacy spelling"

make_tmpdir HYPR_MIGRATION_TMPDIR
mkdir -p "$HYPR_MIGRATION_TMPDIR/.config/hypr"
cat >"$HYPR_MIGRATION_TMPDIR/.config/hypr/hyprland.lua" <<'LUA'
-- Learn how to configure Hyprland: https://wiki.hypr.land/Configuring/Start/

-- Load user modules from ~/.config and Omarchy defaults from $OMARCHY_PATH.
package.path = os.getenv("HOME")
  .. "/.config/?.lua;"
  .. (os.getenv("OMARCHY_PATH") or (os.getenv("HOME") .. "/.local/share/omarchy"))
  .. "/?.lua;"
  .. package.path

-- All Omarchy default setups
require("default.hypr.omarchy")

-- Add any other personal Hyprland configuration below.
o.window("Example", { workspace = "1" })
LUA
HOME="$HYPR_MIGRATION_TMPDIR" bash -euo pipefail "$ROOT/migrations/1781063758.sh" >/dev/null
grep -Fq 'dofile((os.getenv("OMARCHY_PATH") or "/usr/share/omarchy") .. "/default/hypr/bootstrap.lua")' "$HYPR_MIGRATION_TMPDIR/.config/hypr/hyprland.lua" || fail "hyprland bootstrap migration adds bootstrap"
! grep -Fq 'package.path = os.getenv("HOME")' "$HYPR_MIGRATION_TMPDIR/.config/hypr/hyprland.lua" || fail "hyprland bootstrap migration removes legacy path block"
grep -Fq 'o.window("Example", { workspace = "1" })' "$HYPR_MIGRATION_TMPDIR/.config/hypr/hyprland.lua" || fail "hyprland bootstrap migration preserves custom config"
pass "hyprland bootstrap migration updates stale user entrypoint"

cp "$NEXT_THEME/colors.toml" "$CURRENT_THEME/colors.toml"
cp "$NEXT_THEME/vscode-theme.json" "$CURRENT_THEME/vscode-theme.json"

FAKE_BIN="$PI_TMPDIR/fake-bin"
mkdir -p "$FAKE_BIN"

cat >"$FAKE_BIN/tmux" <<'EOF'
#!/bin/bash
case "$1" in
  list-sessions)
    [[ $2 == "-F" ]] && echo "session-1"
    exit 0
    ;;
  set-environment|set-option|refresh-client)
    echo "$*" >>"${TMUX_LOG:-/dev/null}"
    exit 0
    ;;
  list-panes|list-clients)
    exit 0
    ;;
  *)
    exit 0
    ;;
esac
EOF
chmod +x "$FAKE_BIN/tmux"

TMUX_LOG="$PI_TMPDIR/tmux.log" PATH="$FAKE_BIN:$ROOT/bin:$PATH" HOME="$PI_TMPDIR" "$ROOT/bin/omarchy-theme-set-tmux"
grep -q 'set-environment -g COLORFGBG 0;15' "$PI_TMPDIR/tmux.log" || fail "tmux sync reads mode from colors.toml"
pass "tmux sync reads mode from colors.toml"

cat >"$FAKE_BIN/gsettings" <<'EOF'
#!/bin/bash
echo "$*" >>"${GSETTINGS_LOG:-/dev/null}"
EOF
chmod +x "$FAKE_BIN/gsettings"

GSETTINGS_LOG="$PI_TMPDIR/gsettings.log" DBUS_SESSION_BUS_ADDRESS="unix:path=$PI_TMPDIR/bus" PATH="$FAKE_BIN:$ROOT/bin:$PATH" HOME="$PI_TMPDIR" "$ROOT/bin/omarchy-theme-set-gnome"
grep -q 'org.gnome.desktop.interface color-scheme prefer-light' "$PI_TMPDIR/gsettings.log" || fail "gnome sync reads mode from colors.toml"
pass "gnome sync reads mode from colors.toml"

cat >"$FAKE_BIN/omarchy-cmd-present" <<'EOF'
#!/bin/bash
[[ $1 == "code" ]]
EOF
chmod +x "$FAKE_BIN/omarchy-cmd-present"

cat >"$FAKE_BIN/omarchy-toggle-enabled" <<'EOF'
#!/bin/bash
exit 1
EOF
chmod +x "$FAKE_BIN/omarchy-toggle-enabled"

mkdir -p "$PI_TMPDIR/.vscode/extensions"
echo '{"local.omarchy-theme-1.0.0":true}' >"$PI_TMPDIR/.vscode/extensions/.obsolete"
PATH="$FAKE_BIN:$ROOT/bin:$PATH" HOME="$PI_TMPDIR" "$ROOT/bin/omarchy-theme-set-vscode"
jq -e '.contributes.themes[0].uiTheme == "vs"' "$PI_TMPDIR/.vscode/extensions/omarchy-theme/package.json" >/dev/null || fail "vscode generated light theme uses VS Code light uiTheme"
pass "vscode generated light theme uses VS Code light uiTheme"
[[ -L $PI_TMPDIR/.vscode/extensions/omarchy-theme/themes/omarchy-color-theme.json ]] || fail "vscode generated theme references current theme file"
[[ $(readlink "$PI_TMPDIR/.vscode/extensions/omarchy-theme/themes/omarchy-color-theme.json") == "$PI_TMPDIR/.local/state/omarchy/current/theme/vscode-theme.json" ]] || fail "vscode generated theme references current theme file"
pass "vscode generated theme references current theme file"
[[ ! -f $PI_TMPDIR/.vscode/extensions/.obsolete ]] || fail "vscode generated theme clears obsolete extension marker"
pass "vscode generated theme clears obsolete extension marker"
jq -e '.[] | select(.identifier.id == "local.omarchy-theme" and .relativeLocation == "omarchy-theme")' "$PI_TMPDIR/.vscode/extensions/extensions.json" >/dev/null || fail "vscode generated theme registers local extension"
pass "vscode generated theme registers local extension"

for binary in \
  omarchy-update \
  omarchy-theme-set \
  omarchy-capture-screenshot \
  omarchy-system-reboot \
  omarchy-pkg-add; do
  [[ -x $ROOT/bin/$binary ]] || fail "binary is executable: $binary"
  pass "binary is executable: $binary"
done

while IFS= read -r binary_path; do
  header=$(awk '
    NR == 1 && /^#!/ { next }
    /^[[:space:]]*$/ { if (seen) print; next }
    /^[[:space:]]*#/ { seen=1; print; next }
    { exit }
  ' "$binary_path")

  grep -q '^# omarchy:summary=' <<<"$header" || fail "metadata summary is present: $binary_path"
  ! grep -q '^# omarchy:binary=' <<<"$header" || fail "metadata does not repeat inferred binary: $binary_path"
  ! grep -q '^# omarchy:args=$' <<<"$header" || fail "metadata does not include empty args: $binary_path"
  ! grep -Eq '^# omarchy:(legacy|usage|visibility|mutates|interactive)=' <<<"$header" || fail "metadata avoids removed fields: $binary_path"
  ! grep -Eq '^# omarchy:requires-sudo=false$' <<<"$header" || fail "metadata omits false booleans: $binary_path"
done < <(find "$ROOT/bin" -maxdepth 1 -type f -executable -name 'omarchy-*' | sort)
pass "all executable bins have slim self-documenting metadata"

make_tmpdir TMPDIR
ln -s "$CLI" "$TMPDIR/omarchy"

{
  printf '#!/bin/bash\n\n'
  printf '# ordinary comments are fine\n'
  printf '# omarchy:this malformed line should be ignored\n'
  printf '# omarchy:group=weird\n'
  printf '# omarchy:name=test\n'
  printf '# omarchy:summary=Survives malformed metadata comments\n'
  printf '# omarchy:made-up=value\n'
  printf 'echo weird-ok\n'
} >"$TMPDIR/omarchy-weird-test"
chmod +x "$TMPDIR/omarchy-weird-test"

{
  printf '#!/bin/bash\n\n'
  printf '# a partial metadata header should not destroy fallback routing\n'
  printf '# omarchy:summary=Partial metadata keeps inferred route\n'
  printf '# omarchy:made-up=value\n'
  printf 'echo partial-ok\n'
} >"$TMPDIR/omarchy-partial-meta-test"
chmod +x "$TMPDIR/omarchy-partial-meta-test"

{
  printf '#!/bin/bash\n\n'
  printf 'echo body-metadata-ok\n'
  printf '# omarchy:group=wrong\n'
  printf '# omarchy:name=wrong\n'
} >"$TMPDIR/omarchy-body-metadata-test"
chmod +x "$TMPDIR/omarchy-body-metadata-test"

"$TMPDIR/omarchy" commands --all --json | jq -e '.commands[] | select(.route == "omarchy weird test" and .summary == "Survives malformed metadata comments")' >/dev/null
pass "unknown metadata values are non-fatal"

"$TMPDIR/omarchy" commands --all --json | jq -e '.commands[] | select(.route == "omarchy partial meta test" and .summary == "Partial metadata keeps inferred route")' >/dev/null
pass "partial metadata keeps inferred fallback route"

"$TMPDIR/omarchy" commands --all --json | jq -e '.commands[] | select(.route == "omarchy body metadata test" and .summary == "Run the body metadata test command")' >/dev/null
pass "metadata-looking comments after script body are ignored"

output=$("$TMPDIR/omarchy" weird test)
assert_output_contains "temporary metadata command dispatches" "$output" "weird-ok"

output=$("$TMPDIR/omarchy" partial meta test)
assert_output_contains "partial metadata command dispatches" "$output" "partial-ok"

output=$("$TMPDIR/omarchy" body metadata test)
assert_output_contains "body metadata command dispatches by filename" "$output" "body-metadata-ok"

# A trailing --help must render help even when the route only partially
# resolves, leaving unresolved words ahead of the flag (e.g. `update aur --help`
# used to start a real update because only the first leftover token was checked).
{
  printf '#!/bin/bash\n\n'
  printf '# omarchy:summary=Partial resolution help test parent\n'
  printf '# omarchy:alias=omarchy parenthelp-alias\n'
  printf 'echo "parenthelp-parent-ran args=[$*]"\n'
} >"$TMPDIR/omarchy-parenthelp"
chmod +x "$TMPDIR/omarchy-parenthelp"

{
  printf '#!/bin/bash\n\n'
  printf '# omarchy:summary=Partial resolution help test child\n'
  printf 'echo parenthelp-child-ran\n'
} >"$TMPDIR/omarchy-parenthelp-child"
chmod +x "$TMPDIR/omarchy-parenthelp-child"

output=$("$TMPDIR/omarchy" parenthelp bogus --help)
assert_output_contains "trailing --help after an unresolved word renders help" "$output" "omarchy-parenthelp"
assert_output_contains "partial resolution help lists related commands" "$output" "omarchy parenthelp child"
if [[ $output == *"parenthelp-parent-ran"* ]]; then
  fail "trailing --help after an unresolved word does not execute the command"
fi
pass "trailing --help after an unresolved word does not execute the command"

output=$("$TMPDIR/omarchy" parenthelp bogus -h)
assert_output_contains "trailing -h after an unresolved word renders help" "$output" "omarchy-parenthelp"

output=$("$TMPDIR/omarchy" parenthelp-alias bogus --help)
assert_output_contains "trailing --help after an unresolved word renders help via alias route" "$output" "omarchy-parenthelp"
if [[ $output == *"parenthelp-parent-ran"* ]]; then
  fail "trailing --help on an alias route does not execute the command"
fi
pass "trailing --help on an alias route does not execute the command"

output=$("$TMPDIR/omarchy" parenthelp bogus --json --help)
grep -q '"ok": true' <<<"$output" || fail "--json with --help renders JSON help"
pass "--json with --help renders JSON help"

output=$("$TMPDIR/omarchy" parenthelp "explain --json output" --help)
assert_output_contains "a --json embedded inside one argument does not switch help to JSON" "$output" "Partial resolution help test parent"

output=$("$TMPDIR/omarchy" parenthelp bogus trailing)
assert_output_contains "leftover args without a help flag still forward" "$output" "parenthelp-parent-ran args=[bogus trailing]"

output=$("$TMPDIR/omarchy" parenthelp run -- --help)
assert_output_contains "a --help behind -- belongs to the command and still forwards" "$output" "parenthelp-parent-ran args=[run -- --help]"

output=$("$TMPDIR/omarchy" parenthelp --helpme x--help)
assert_output_contains "help lookalike tokens do not trigger help" "$output" "parenthelp-parent-ran args=[--helpme x--help]"
