#!/usr/bin/env bash

# scripts/panama-idle generates hypridle's configuration from Panama's settings.
#
# This is the one generator that can cost the user their automatic screen lock,
# so the properties that matter are: zero means never (rather than "immediately",
# which a naive template would produce), values are clamped even when the
# settings file has been hand-edited, and a missing or corrupt file still yields
# a working configuration rather than an empty one.

set -euo pipefail

repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
helper="$repo_dir/config/dot/quickshell/scripts/panama-idle"
work="$(mktemp -d /tmp/panama-idle-contract.XXXXXX)"

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

cleanup() { rm -rf "$work"; }
trap cleanup EXIT

generated="$work/state/panama/hypridle.conf"

run_with() {
    # Isolated config AND state, so this never touches the real generated file
    # and never restarts the user's hypridle: `apply` only restarts when the
    # systemd drop-in exists, and it cannot exist under this temporary root.
    mkdir -p "$work/config/panama"
    printf '%s' "$1" >"$work/config/panama/settings.json"
    XDG_CONFIG_HOME="$work/config" XDG_STATE_HOME="$work/state" "$helper" apply
}

# ── Shipped defaults ─────────────────────────────────────────────────────────
run_with '{}'
grep -q 'timeout = 300' "$generated" || fail 'default screen blank is not 5 minutes'
grep -q 'timeout = 600' "$generated" || fail 'default lock is not 10 minutes'
grep -q 'before_sleep_cmd' "$generated" || fail 'lock before sleep missing by default'
grep -q 'systemctl suspend' "$generated" && fail 'automatic suspend is on by default'

# ── Zero means never, not immediately ────────────────────────────────────────
run_with '{"screenBlankMinutes":0,"lockMinutes":0,"suspendMinutes":0}'
grep -q 'timeout = 0' "$generated" && fail 'a zero timeout was written as an immediate trigger'
# `dpms` also appears in general.after_sleep_cmd, which is correct and must
# stay; what must not exist is any listener at all.
grep -qE '^listener' "$generated" && fail 'a listener was written when everything is set to never'
grep -q 'after_sleep_cmd' "$generated" || fail 'the general block was lost when all timers are off'

# ── Values are honored ──────────────────────────────────────────────────────
run_with '{"screenBlankMinutes":2,"lockMinutes":7,"suspendMinutes":45}'
grep -q 'timeout = 120' "$generated" || fail '2 minute blank not honored'
grep -q 'timeout = 420' "$generated" || fail '7 minute lock not honored'
grep -q 'timeout = 2700' "$generated" || fail '45 minute suspend not honored'
grep -q 'systemctl suspend' "$generated" || fail 'suspend listener missing when set'

# ── lockOnSleep off removes the pre-sleep lock ───────────────────────────────
run_with '{"lockOnSleep":false}'
grep -q 'before_sleep_cmd' "$generated" && fail 'pre-sleep lock present when disabled'

# ── Hand-edited nonsense is clamped, not passed through ──────────────────────
run_with '{"screenBlankMinutes":99999,"lockMinutes":-40,"suspendMinutes":"soon"}'
grep -q 'timeout = 7200' "$generated" || fail 'an above-range blank was not clamped to the maximum'
grep -q 'timeout = 0' "$generated" && fail 'a negative lock produced an immediate trigger'
grep -q 'systemctl suspend' "$generated" && fail 'a non-numeric suspend produced a listener'

# ── A corrupt or absent settings file still yields a working config ──────────
run_with '{ not json at all'
grep -q 'timeout = 300' "$generated" || fail 'a corrupt settings file did not fall back to defaults'
rm -f "$work/config/panama/settings.json"
XDG_CONFIG_HOME="$work/config" XDG_STATE_HOME="$work/state" "$helper" apply
grep -q 'timeout = 300' "$generated" || fail 'an absent settings file did not fall back to defaults'

# ── status reports what it generated ─────────────────────────────────────────
status="$(XDG_CONFIG_HOME="$work/config" XDG_STATE_HOME="$work/state" "$helper" status)"
jq -e '.managed == false and .blankMinutes == 5 and .lockMinutes == 10' <<<"$status" >/dev/null \
    || fail "status did not report the generated values: $status"

# ── Power source ─────────────────────────────────────────────────────────────
#
# hypridle has no concept of AC versus battery, so there is one config and it
# is rebuilt from the other set of keys when the charger comes or goes. Three
# things must hold, and the third is the one that protects every existing
# desktop:
#
#   1. On battery, the battery keys win.
#   2. A battery key that was never written falls back to its AC counterpart,
#      not to the schema's shorter shipped default -- otherwise unplugging
#      would silently override a deliberately long setting.
#   3. A machine with no battery reads none of them, and generates exactly
#      what it generated before any of this existed.
#
# panama-hw is stubbed rather than the sysfs tree, because this is testing
# which keys panama-idle chooses, not how the hardware is detected. That is
# the hardware predicates contract's job.

stub_hw() {
    mkdir -p "$work/fake/bin"
    cat >"$work/fake/bin/panama-hw" <<STUB
#!/usr/bin/env bash
case "\$1" in
    battery) exit $1 ;;
    ac)      exit $2 ;;
    *)       exit 1 ;;
esac
STUB
    chmod +x "$work/fake/bin/panama-hw"
}

run_powered() {
    mkdir -p "$work/config/panama"
    printf '%s' "$1" >"$work/config/panama/settings.json"
    XDG_CONFIG_HOME="$work/config" XDG_STATE_HOME="$work/state" \
        PANAMA_PATH="$work/fake" "$helper" apply
}

settings='{"screenBlankMinutes":30,"lockMinutes":45,"suspendMinutes":0,
           "screenBlankMinutesBattery":2,"lockMinutesBattery":5,"suspendMinutesBattery":20}'

# On battery: battery keys.
stub_hw 0 1
run_powered "$settings"
grep -q 'timeout = 120' "$generated" || fail 'on battery, the battery blank timing was not used'
grep -q 'timeout = 300' "$generated" || fail 'on battery, the battery lock timing was not used'
grep -q 'systemctl suspend' "$generated" || fail 'on battery, the battery suspend listener is missing'
grep -q 'Power source at generation: battery' "$generated" \
    || fail 'the generated config does not record that it was built for battery'

# Plugged in: AC keys, and no suspend, even though the battery set has one.
stub_hw 0 0
run_powered "$settings"
grep -q 'timeout = 1800' "$generated" || fail 'on wall power, the AC blank timing was not used'
grep -q 'timeout = 2700' "$generated" || fail 'on wall power, the AC lock timing was not used'
grep -q 'systemctl suspend' "$generated" && fail 'on wall power, a battery-only suspend listener was written'

# A battery key that was never written falls back to ITS OWN schema default,
# not to the AC value. This section once pinned the opposite -- protecting a
# deliberate AC setting from being "overridden" by unplugging -- but the
# Power page's battery card shows the schema defaults for unwritten battery
# keys, so the AC fallback made the generator disagree with what the screen
# said: a fresh laptop displayed "suspend at 20 minutes on battery" and
# generated no suspend listener at all. Whatever the sliders show is what
# must be generated; a person who wants battery to match AC sets it so, on
# the card that has said the real values all along.
stub_hw 0 1
run_powered '{"screenBlankMinutes":30,"lockMinutes":45}'
grep -q 'timeout = 120' "$generated" \
    || fail 'an unset battery blank did not use the schema default the Power page displays'
grep -q 'timeout = 300' "$generated" \
    || fail 'an unset battery lock did not use the schema default the Power page displays'
grep -q 'systemctl suspend' "$generated" \
    || fail 'an unset battery suspend generated no suspend listener -- the discharged-in-a-bag bug, back again'

# No battery at all: the battery keys are never consulted, even when present.
stub_hw 1 0
run_powered "$settings"
grep -q 'timeout = 1800' "$generated" || fail 'a machine with no battery did not use the AC blank timing'
grep -q 'timeout = 120' "$generated" && fail 'a machine with no battery read the battery keys'
grep -q 'Power source at generation: wall power' "$generated" \
    || fail 'a machine with no battery did not record wall power'

status="$(XDG_CONFIG_HOME="$work/config" XDG_STATE_HOME="$work/state" \
    PANAMA_PATH="$work/fake" "$helper" status)"
jq -e '.powerSource == "ac"' <<<"$status" >/dev/null \
    || fail "status did not report the power source: $status"

trap - EXIT
cleanup
printf 'idle config contract: PASS\n'
