#!/usr/bin/env bash
# u/complete: AI code completion. Reads prefix from stdin, prints completion to stdout.
set -euo pipefail

PREFIX_MAX=12000
SUFFIX_MAX=1000

MOUNT=${OLLIE:-$HOME/mnt/ollie}

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

for _arg; do
    [ "$_arg" = "-help" ] && cat <<'EOF' && exit 0
usage: complete [-model MODEL] [-backend BACKEND] [-file NAME] [-suffix TEXT]
       echo "prefix" | complete -file main.go -suffix "rest of code"

Reads prefix from stdin, prints raw completion to stdout.
Cost/info lines (:: prefix) go to stderr.

Flags:
  -model MODEL      model (default: $OLLIE_COMPLETE_MODEL)
  -backend BACKEND  backend (default: $OLLIE_COMPLETE_BACKEND)
  -file NAME        filename for language context
  -suffix TEXT      code after the cursor
  -context TEXT     additional context (LSP symbols, completions, hover)
  -help             show this help
EOF
done

model=${OLLIE_COMPLETE_MODEL:-}
backend=${OLLIE_COMPLETE_BACKEND:-}
file=
suffix=
context=
cwd=

while [ $# -gt 0 ]; do
    case $1 in
        -model)   model=$2;   shift ;;
        -backend) backend=$2; shift ;;
        -file)    file=$2;    shift ;;
        -suffix)  suffix=$2;  shift ;;
        -context) context=$2; shift ;;
        -cwd)     cwd=$2;     shift ;;
        -help)    ;;
        -*) die "unknown flag: $1" ;;
        *)  break ;;
    esac
    shift
done

[ -z "$model" ]   && die "model required: set OLLIE_COMPLETE_MODEL or pass -model"
[ -z "$backend" ] && die "backend required: set OLLIE_COMPLETE_BACKEND or pass -backend"

prefix=$(cat)

# Truncate to context budget
[ ${#prefix} -gt $PREFIX_MAX ] && prefix="${prefix: -$PREFIX_MAX}"
[ ${#suffix} -gt $SUFFIX_MAX ] && suffix="${suffix:0:$SUFFIX_MAX}"

# Find or create a copilot session for the current working directory.
find_or_create_session() {
    local target_cwd="$1"
    local id kv deadline

    id="$(printf '%s' "$target_cwd" | cksum | awk '{print $1}')-copilot"

    if [ -d "$MOUNT/s/$id" ]; then
        local state
        state=$(cat "$MOUNT/s/$id/state" 2>/dev/null) || true
        case "$state" in
            failed*|error*)
                printf 'kill\n' > "$MOUNT/s/$id/ctl" 2>/dev/null || true
                sleep 0.1
                ;;
            *)
                printf '%s\n' "$id"
                return 0
                ;;
        esac
    fi

    kv="name=${id}
cwd=${target_cwd}
agent=copilot
backend=${backend}
model=${model}"
    printf '%s\n' "$kv" > "$MOUNT/s/new" 2>/dev/null || true

    deadline=$(( $(date +%s) + 5 ))
    until [ -d "$MOUNT/s/$id" ] || [ "$(date +%s)" -ge "$deadline" ]; do sleep 0.05; done
    [ -d "$MOUNT/s/$id" ] || die "timeout waiting for copilot session"

    printf '%s\n' "$id"
}

sid=$(find_or_create_session "${cwd:-$PWD}")
B="$MOUNT/s/$sid"

file_hint=
[ -n "$file" ] && file_hint=" in ${file}"

context_block=""
[ -n "$context" ] && context_block="
${context}"

printf '%s\n' "Implement the code at the cursor${file_hint}. The prefix ends at the point where new code is needed. Write the implementation — do not echo stubs, TODOs, or placeholder returns from the prefix. Output ONLY raw code. No reasoning, no shell commands, no explanations, no markdown fences, no backticks, no preamble. Your entire response must be valid code that can be inserted directly into the file.
${context_block}
<prefix>
${prefix}
</prefix>
<suffix>
${suffix}
</suffix>" > "$B/prompt"

# Wait for idle
while true; do
    state=$(cat "$B/statewait" 2>/dev/null | tr -d '[:space:]') || break
    [ "$state" = "idle" ] && break
done

# Read result from offset, then clear context for next completion
offset=$(cat "$B/offset" 2>/dev/null || echo 0)
result=$(tail -c +$((offset + 1)) "$B/chat" 2>/dev/null)
# Strip markdown code fences and leaked XML tags if the model wrapped the output.
result=$(printf '%s' "$result" | sed '/^```[a-zA-Z]*$/d; /^```$/d; /^<\/\?\(prefix\|suffix\)>$/d')
# Strip info lines (:: prefix)
result=$(printf '%s' "$result" | grep -v '^:: ')
echo "clear" > "$B/ctl"

# Strip prefix echo: if the result starts with a suffix of the prefix, remove it.
# Check progressively shorter tails of the prefix (up to 200 chars).
tail_max=200
[ ${#prefix} -lt $tail_max ] && tail_max=${#prefix}
i=$tail_max
while [ "$i" -gt 0 ]; do
    tail="${prefix: -$i}"
    case "$result" in
        "$tail"*)
            result="${result#"$tail"}"
            break
            ;;
    esac
    i=$((i - 1))
done

printf '%s' "$result"
