#!/usr/bin/env bash

# How much of the Claude subscription is gone, in the bar.
#
# This is the only thing in Panama that reads an authentication token, so most
# of what is pinned here is about that rather than about the number:
#
#   1. The token never reaches argv. A header passed as an argument is
#      world-readable in /proc/<pid>/cmdline for as long as the request takes,
#      which is the same rule the password and MOK paths already follow.
#   2. The token never reaches the output. The widget has no business seeing a
#      credential and neither does anyone reading the state file.
#   3. It NEVER refreshes the token and never writes to the credentials file.
#      That token expires hourly and Claude Code refreshes it on demand; two
#      processes rotating one credential means being silently signed out of
#      Claude Code by a status widget, and no bar indicator is worth that.
#   4. An expired token is reported as waiting, not as an error, and no request
#      is made with it.
#   5. The widget hides unless it was asked for AND there are real numbers. An
#      indicator reading "unknown" is worse than an empty space.

set -uo pipefail

repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
collector="$repo_dir/config/dot/quickshell/scripts/panama-agent-usage"
service="$repo_dir/config/dot/quickshell/services/AgentUsage.qml"
widget="$repo_dir/config/dot/quickshell/modules/bar/AgentUsageWidget.qml"
schema="$repo_dir/config/dot/quickshell/config/PreferenceSchema.qml"
aliases="$repo_dir/config/dot/quickshell/config/Settings.qml"

findings=()
note() { findings+=("$1"); }

[[ -x "$collector" ]] || { printf 'agent usage contract: %s is not executable\n' "$collector" >&2; exit 1; }

work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
stub="$work/bin"
mkdir -p "$stub"
export XDG_STATE_HOME="$work/state"
output="$XDG_STATE_HOME/panama/agent-usage.json"

readonly SECRET='sk-fixture-token-must-never-appear'

credentials() {
    local expires="$1"
    cat >"$work/credentials.json" <<CREDS
{"claudeAiOauth":{"accessToken":"$SECRET","refreshToken":"refresh-fixture",
"expiresAt":$expires,"rateLimitTier":"default_claude_max_5x","subscriptionType":"max"}}
CREDS
}

# Records how it was invoked, including whether the secret was in argv.
cat >"$stub/curl" <<STUB
#!/usr/bin/env bash
printf '%s\n' "\$*" >>"$work/curl-argv"
printf '%s\n' '{"five_hour":{"utilization":42,"resets_at":"2026-08-22T14:00:00Z"},"seven_day":{"utilization":71,"resets_at":"2026-08-27T00:00:00Z"},"rate_limit_tier":"default_claude_max_5x"}'
STUB
chmod +x "$stub/curl"

run() {
    PATH="$stub:$PATH" PANAMA_AGENT_CREDENTIALS="$work/credentials.json" \
        PANAMA_AGENT_USAGE_ENDPOINT="https://example.invalid/usage" \
        "$collector" >/dev/null 2>&1
}

# ── 4. An expired token waits rather than failing ───────────────────────────

: >"$work/curl-argv"
credentials 1000
run
[[ "$(jq -r .status "$output")" == "stale" ]] \
    || note "an expired token did not report as stale (got: $(jq -r .status "$output"))"
[[ -s "$work/curl-argv" ]] \
    && note 'a request was made with an expired token'

# ── 1 & 2. The token stays out of argv and out of the output ────────────────

: >"$work/curl-argv"
credentials "$(( ($(date +%s) + 3600) * 1000 ))"
run

[[ "$(jq -r .status "$output")" == "ok" ]] \
    || note "a valid token did not produce a reading (got: $(jq -r .detail "$output"))"

grep -qF "$SECRET" "$work/curl-argv" \
    && note 'the token was passed to curl as an argument, where /proc makes it world-readable'
grep -q -- '--config' "$work/curl-argv" \
    || note 'the token is not passed through a curl config file, so it may reach argv'

grep -qrF "$SECRET" "$XDG_STATE_HOME" \
    && note 'the token appears in the state file the widget reads'

# ── 3. Credentials are never written ────────────────────────────────────────

before="$(sha256sum "$work/credentials.json" | cut -d' ' -f1)"
run
[[ "$(sha256sum "$work/credentials.json" | cut -d' ' -f1)" == "$before" ]] \
    || note 'the collector modified the credentials file'

# Comments stripped: the header explains at length that it does not refresh,
# and matching that is matching documentation.
uncommented() { grep -v '^[[:space:]]*#' "$1"; }
uncommented "$collector" | grep -qE 'refreshToken|refresh_token|grant_type' \
    && note 'the collector touches the refresh token, which can sign Claude Code out'
uncommented "$collector" | grep -qE '>[[:space:]]*"?\$?\{?CREDENTIALS' \
    && note 'the collector writes to the credentials file'

# ── The reading it produced ─────────────────────────────────────────────────

# The endpoint already reports percentages. Treating them as 0..1 fractions and
# multiplying is how the bar came to read 1500%.
[[ "$(jq -r '.usage.fiveHour.used' "$output")" == "42" ]] \
    || note "the five-hour reading is $(jq -r '.usage.fiveHour.used' "$output") for a reported 42%"
[[ "$(jq -r '.usage.week.used' "$output")" == "71" ]] \
    || note "the weekly reading is $(jq -r '.usage.week.used' "$output") for a reported 71%"

# Nothing the widget shows may fall outside the range a percentage has, whatever
# the endpoint says. A readout that can print 1500% is one you learn to ignore.
cat >"$stub/curl" <<'STUB'
#!/usr/bin/env bash
printf '%s\n' '{"five_hour":{"utilization":1500},"seven_day":{"utilization":-4}}'
STUB
chmod +x "$stub/curl"
run
[[ "$(jq -r '.usage.fiveHour.used' "$output")" == "100" ]] \
    || note 'an out-of-range reading was not clamped to 100'
[[ "$(jq -r '.usage.week.used' "$output")" == "0" ]] \
    || note 'a negative reading was not clamped to 0'

cat >"$stub/curl" <<'STUB'
#!/usr/bin/env bash
printf '%s\n' '{"five_hour":{"utilization":42,"resets_at":"2026-08-22T14:00:00Z"},"seven_day":{"utilization":71,"resets_at":"2026-08-27T00:00:00Z"},"rate_limit_tier":"default_claude_max_5x"}'
STUB
chmod +x "$stub/curl"

# ── It answers a click ──────────────────────────────────────────────────────
#
# Pill's MouseArea is gated on `interactive`, so a widget that sets it false and
# connects onSecondaryActivated has a handler nothing can ever reach.
uncommented "$widget" | grep -q 'interactive: false' \
    && note 'the widget disables Pill''s mouse area, so its click handlers never fire'
uncommented "$widget" | grep -q 'onActivated' \
    || note 'left-clicking the widget does nothing'

# An endpoint that answers with something else must degrade, not crash.
cat >"$stub/curl" <<'STUB'
#!/usr/bin/env bash
printf '%s\n' '{"error":{"message":"nope"}}'
STUB
chmod +x "$stub/curl"
run
[[ "$(jq -r .status "$output")" == "unavailable" ]] \
    || note 'an error response was not reported as unavailable'

# ── 5. The widget hides itself ──────────────────────────────────────────────

grep -q 'Settings.showAgentUsage && AgentUsage.available' "$widget" \
    || note 'the widget does not gate on both the preference and having real numbers'
grep -q 'key: "showAgentUsage"' "$schema" || note 'there is no showAgentUsage preference'
grep -A2 'key: "showAgentUsage"' "$schema" | grep -q 'def: false' \
    || note 'the usage widget is on by default; it is a coding-tool readout, not general-purpose desktop furniture'
grep -q 'showAgentUsage' "$aliases" \
    || note 'Settings.qml does not alias showAgentUsage, so the binding reads undefined'

# The collector runs on a timer measured in minutes, never on a repaint.
python3 - "$service" <<'PY' || note 'the collector is not run on a minute-scale timer'
import re, sys
text = open(sys.argv[1], encoding="utf-8").read()
match = re.search(r"interval:\s*(\d+)\s*\*\s*60\s*\*\s*1000", text)
if not match or int(match.group(1)) < 1:
    raise SystemExit(1)
PY

if (( ${#findings[@]} > 0 )); then
    printf 'agent usage contract: %d finding(s)\n' "${#findings[@]}" >&2
    printf '  - %s\n' "${findings[@]}" >&2
    exit 1
fi

printf 'agent usage contract: PASS\n'
