#!/usr/bin/env bash
# x/freeloader: turnError hook handler for free-model sessions on OpenRouter.
#
# Intercepts rate_limit and tool_unsupported errors on :free models, walks
# through every viable free model once per round, and resubmits the previous
# prompt on each switch. When the pool is exhausted, clears the tried list
# and exits 1 so normal error handling takes over.
#
# Configure in an agent .json:
#   { "hooks": { "turnError": "$OLLIE/x/freeloader $OLLIE_SESSION_ID" } }
#
# Exit codes:
#   0  — error intercepted, switched model and resubmitted
#   1  — pool exhausted or error not applicable; normal handling proceeds

set -euo pipefail

OLLIE="${OLLIE:-$HOME/mnt/ollie}"
CFG="${XDG_CONFIG_HOME:-$HOME/.config}/ollie"
BLACKLIST="$CFG/freeloader-blacklist"
cmd=${0##*/}

die() { printf '%s: %s\n' "$cmd" "$*" >&2; exit 1; }

[ $# -ne 1 ] && die "usage: freeloader <session-id>"

SID="$1"
B="$OLLIE/s/$SID"

[ -d "$B" ] || die "session not found: $SID"

error_type="${OLLIE_ERROR_TYPE:-}"
current_model="${OLLIE_MODEL:-$(grep '^model=' "$B/cfg" 2>/dev/null | cut -d= -f2-)}"

# Only handle rate_limit and tool_unsupported.
case "$error_type" in
    rate_limit|tool_unsupported) ;;
    *) exit 1 ;;
esac

# Only act on :free models.
case "$current_model" in
    *:free) ;;
    *) exit 1 ;;
esac

# For tool_unsupported: permanently blacklist the current model.
if [ "$error_type" = "tool_unsupported" ]; then
    mkdir -p "$CFG"
    if ! grep -qxF "$current_model" "$BLACKLIST" 2>/dev/null; then
        printf '%s\n' "$current_model" >> "$BLACKLIST"
        printf '%s: blacklisted %s (tool_unsupported)\n' "$cmd" "$current_model" >&2
    fi
fi

# Per-session tried list: tracks models attempted in the current round.
TRIED="$CFG/freeloader-tried-$SID"
mkdir -p "$CFG"

# Mark current model as tried.
if ! grep -qxF "$current_model" "$TRIED" 2>/dev/null; then
    printf '%s\n' "$current_model" >> "$TRIED"
fi

# Build the full sorted :free pool (blacklist excluded).
mapfile -t pool < <(
    cat "$B/models" 2>/dev/null \
    | grep ':free' \
    | sort \
    | { [ -f "$BLACKLIST" ] && grep -vxFf "$BLACKLIST" || cat; }
)

# Find first model not yet tried this round.
next_model=""
for m in "${pool[@]}"; do
    if ! grep -qxF "$m" "$TRIED" 2>/dev/null; then
        next_model="$m"
        break
    fi
done

if [ -z "$next_model" ]; then
    printf '%s: all free models exhausted, giving up\n' "$cmd" >&2
    rm -f "$TRIED"
    exit 1
fi

# Switch model and resubmit the previous prompt.
printf 'model %s\n' "$next_model" > "$B/ctl" || die "failed to switch model"
prev=$(cat "$B/prompt.prev" 2>/dev/null) || die "prompt.prev not available"
[ -z "$prev" ] && die "prompt.prev is empty"
printf '%s\n' "$prev" > "$B/fifo.in" || die "failed to resubmit prompt"

printf '%s: %s → %s\n' "$cmd" "$current_model" "$next_model" >&2
exit 0
