#!/usr/bin/env bash
set -euo pipefail

BEAGLE_DIR="$(cd "$(dirname "$0")/.." && pwd)"
BIN="$BEAGLE_DIR/bin"
STORE_DIR="${BEAGLE_STORE_HOME:-$BEAGLE_DIR/store}"

# Generated target projection of beagle-lib/private/targets.rkt (the canonical
# table). Regenerate with `bin/beagle doc-fill`; drift fails the test suite.
# Guarded: a partial copy of bin/ (the checkout-first gate builds one) must
# still dispatch. It then says the table is MISSING rather than printing a
# hand-written list, which is the whole point of generating it.
if [[ -f "$BEAGLE_DIR/share/targets.sh" ]]; then
    source "$BEAGLE_DIR/share/targets.sh"
else
    BEAGLE_TARGET_NAMES="<share/targets.sh missing — run: beagle doc-fill>"
    BEAGLE_TARGET_COUNT="?"
fi

# The self-hosted Graal artifact is the normal compiler for every public hosted
# surface it implements. This selection runs before Racket setup, and a native
# diagnostic is final: there is no failure-driven Racket or Babashka fallback.
if [[ -f "$BIN/_beagle-hosted-dispatch" &&
      -f "$BEAGLE_DIR/share/targets.sh" ]]; then
    source "$BIN/_beagle-hosted-dispatch"
    if beagle_try_hosted_dispatch "$@"; then
        exit 0
    else
        _hosted_status=$?
        if [[ $_hosted_status -ne $BEAGLE_HOSTED_NOT_APPLICABLE ]]; then
            exit "$_hosted_status"
        fi
        unset _hosted_status
    fi
fi

# Racket remains the explicit oracle/recovery implementation for surfaces the
# self-host does not implement and for direct bin/beagle-* oracle commands.
if [[ "${1:-}" == "ast-bundle" ]]; then
    export BEAGLE_ZO_GATE_QUIET=1
fi
source "$BIN/_beagle-racket"

usage() {
    cat >&2 <<EOF
beagle — a typed Clojure subset; one AST → $BEAGLE_TARGET_NAMES.
($BEAGLE_TARGET_COUNT live targets; \`beagle langs\` renders the table.)

Usage: beagle <command> [args...]

Author:
  syntax FILE               Parse-check (--ledger, --repair --emit-patch)
  check [--profile N] [--module-root LOGICAL=PHYSICAL]... [PATH...]
                            Type-check without emitting
  validate [FILE...]        Parse + check + schema validation
  build [--target T] PATH [OUT]
                            Emit an explicit hosted target
  build --shadow-facts PATH <file-or-dir> ...
                            Emit the shadow-only canonical fact graph
  build [--module-root LOGICAL=PHYSICAL]... PATH [OUT]
                            Emit a hosted source-profile target with a closed module graph
  dts [--provider SOURCE.bjs]... SOURCE.bjs [OUT.d.ts]
                            Project checked beagle/js types to TypeScript declarations
  build --materializer M... [--module-root LOGICAL=PHYSICAL]...
        --out DIR [--entry NS/NAME]... PATH.bgl...
                            Freeze Core once, then project each selected materializer
                            ($BEAGLE_MATERIALIZER_IDS_LIST; wasm requires --abi wasm32)
  fmt --check|--write PATH...  Check or rewrite canonical source layout
  fix [--dry-run] [PATH...] Surface high-confidence auto-fixes
  repair DIR VERIFY         Evidence-ranked repair (--emit-patch / --auto)
  doctor [--deep]           Is the authoring loop online and working?

Query (the compiler is the source of truth):
  sig <fn> <path...>        Typed signature
  fields <record> <path>    Record fields, types, accessors
  callers <fn> <path...>    Call sites
  provides <file>           Exports
  ts-externs ENTRY.d.ts     Typed beagle wrappers from TypeScript declarations
  ts-import SOURCE.ts       Deterministic typed Beagle/JS from TypeScript source
  impact <fn> <path...>     Change-impact
  expand <file>             Macro-expanded source
  ast <file>                Versioned checked-program JSON (no execution)
  ast-bundle                Closed exact-byte checked projection (JSON stdin)
  project-session           Resident exact-byte check/emit session (JSONL stdin)
  explain <CODE>            Diagnostic explanation (E001, …)
  explain-type <file>       Inferred types as a view
  facts-roundtrip MODE FILE Lossless source/fact projection (--emit-edn|--render)
  langs [--json] [--view V] Language targets from the canonical table
                            (views: names, table, extensions, domains, pipeline)
  doc-fill [--check]        Refill every doc span the table owns
                            (--check exits 3 on drift)

Project / loop:
  init [--target T]            Bootstrap a project and its AGENTS.md
  daemon start|status|stop  Watch + cache type results
  promote [REV]             Promote clean checkout HEAD + restart its daemon
  native-exe --out FILE --entry NS/NAME
             [--module-root LOGICAL=PHYSICAL]... PATH...
                            Link a native C17 executable; entry accepts [] or
                            one typed (Vec String) process-argument parameter
  test [--full] [--include-native]
                            Run hosted default evidence; Native is explicit
  rejection-stats DIR       Diagnostics by cause-class
  lsp | repl                LSP server / typed REPL

Store:
  store tell SUBJECT SLOT VALUE
  store retract SUBJECT SLOT VALUE
  store show SUBJECT
  store query EDN
  store scan T1|_ T2|_ T3|_
  store occurrences
  store version|status|validate
  store doctor --deep
  store backup create|verify ...
  store code on|off|status ...
  store up [--restart]
  store serve ...

Deeper dev tools stay as bin/beagle-* (blame, specfix, trace, cascade,
oracle, proptest, muttest, dtrace, smap).
EOF
    exit 1
}

store_usage() {
    cat <<'EOF'
Usage: beagle store <command> [args...]

Data:
  tell SUBJECT SLOT VALUE
  retract SUBJECT SLOT VALUE
  show SUBJECT
  query EDN
  scan T1|_ T2|_ T3|_
  occurrences
  version | status | validate

Operator:
  doctor --deep
  backup create|verify ...
  code on|off|status ...
  up [--restart]
  serve ...
EOF
}

store_prepare_local_state() {
    if [[ "${BEAGLE_STORE_PACKAGED:-0}" == "1" ]]; then
        if [[ -z "${BEAGLE_STORE_LOG:-}" ||
              -z "${BEAGLE_STORE_THREADS:-}" ]]; then
            if [[ -n "${BEAGLE_STORE_STATE_DIR:-}" ]]; then
                state_dir="$BEAGLE_STORE_STATE_DIR"
            elif [[ -n "${XDG_STATE_HOME:-}" ]]; then
                state_dir="$XDG_STATE_HOME/beagle-store"
            elif [[ -n "${HOME:-}" ]]; then
                state_dir="$HOME/.local/state/beagle-store"
            else
                echo "beagle store: set BEAGLE_STORE_LOG + BEAGLE_STORE_THREADS, BEAGLE_STORE_STATE_DIR, XDG_STATE_HOME, or HOME" >&2
                exit 2
            fi
        fi
        [[ -n "${BEAGLE_STORE_THREADS:-}" ]] ||
            BEAGLE_STORE_THREADS="$state_dir/threads"
        [[ -n "${BEAGLE_STORE_LOG:-}" ]] ||
            BEAGLE_STORE_LOG="$state_dir/coordination.log"
    else
        BEAGLE_STORE_THREADS="${BEAGLE_STORE_THREADS:-$STORE_DIR/threads}"
        BEAGLE_STORE_LOG="${BEAGLE_STORE_LOG:-$STORE_DIR/coordination.log}"
    fi
    export BEAGLE_STORE_THREADS BEAGLE_STORE_LOG

    local log_parent="${BEAGLE_STORE_LOG%/*}"
    [[ "$log_parent" != "$BEAGLE_STORE_LOG" ]] || log_parent=.
    if [[ ! -d "$BEAGLE_STORE_THREADS" || ! -d "$log_parent" ]]; then
        mkdir -p "$BEAGLE_STORE_THREADS" "$log_parent"
    fi
}

store_dispatch() {
    local command="${1:-}"
    case "$command" in
        ""|help|-h|--help)
            store_usage
            return 0
            ;;
        doctor)
            shift
            exec "$STORE_DIR/bin/beagle-store-doctor" "$@"
            ;;
        backup)
            shift
            exec "$STORE_DIR/bin/beagle-store-backup" "$@"
            ;;
        code)
            local code_command="${2:-}"
            case "$code_command" in
                on|off|status)
                    shift 2
                    exec "$STORE_DIR/bin/beagle-store-code-$code_command" "$@"
                    ;;
                *)
                    echo "beagle store code: expected on, off, or status" >&2
                    return 2
                    ;;
            esac
            ;;
        up)
            shift
            exec "$STORE_DIR/bin/beagle-store-up" "$@"
            ;;
        serve)
            shift
            exec "$STORE_DIR/bin/beagle-store-server" "$@"
            ;;
        tell|retract|tell-existing|retract-existing|show|query|scan|occurrences|version|status|validate)
            if [[ -z "${BEAGLE_STORE_SPACE_ID:-}" ]]; then
                echo "beagle store: BEAGLE_STORE_SPACE_ID is required for data commands" >&2
                return 2
            fi
            exec "$STORE_DIR/bin/beagle-store-cli" "$@"
            ;;
        *)
            store_prepare_local_state
            exec bb -cp "$STORE_DIR/out" -m store.main "$@"
            ;;
    esac
}

[[ $# -eq 0 ]] && usage

cmd="$1"; shift

case "$cmd" in
    store)
        store_dispatch "$@"
        ;;
    check)
        # Count non-flag args to decide single-file vs batch.
        # Value-taking options do not contribute source-file arguments.
        _check_files=()
        _skip_next=false
        for _a in "$@"; do
            if $_skip_next; then
                _skip_next=false
                continue
            fi
            case "$_a" in
                --profile|--module-root) _skip_next=true ;;
                --*) ;;
                *) _check_files+=("$_a") ;;
            esac
        done
        if [[ ${#_check_files[@]} -eq 1 ]] && [[ -f "${_check_files[0]}" ]]; then
            exec "$BIN/beagle-check" "$@"
        else
            exec "$BIN/beagle-check-all" "$@"
        fi
        ;;
    build)
        _core_build=false
        for _a in "$@"; do
            case "$_a" in
                --materializer|--materializer=*) _core_build=true ;;
            esac
        done
        if $_core_build; then
            exec "$BIN/beagle-build-core" "$@"
        fi

        # A single hosted source (plus an optional output path) uses the
        # focused compiler. Count positionals independently of option order;
        # batch-only or unknown options retain the build-all route.
        _build_positionals=()
        _build_skip_next=false
        _build_after_double_dash=false
        _build_single=true
        for _a in "$@"; do
            if $_build_after_double_dash; then
                _build_positionals+=("$_a")
                continue
            fi
            if $_build_skip_next; then
                _build_skip_next=false
                continue
            fi
            case "$_a" in
                --target|--module-root) _build_skip_next=true ;;
                --target=*|--module-root=*) ;;
                --) _build_after_double_dash=true ;;
                --*) _build_single=false ;;
                *) _build_positionals+=("$_a") ;;
            esac
        done
        if $_build_single &&
           [[ ${#_build_positionals[@]} -ge 1 ]] &&
           [[ ${#_build_positionals[@]} -le 2 ]] &&
           [[ -f "${_build_positionals[0]}" ]]; then
            exec "$BIN/beagle-build" "$@"
        else
            exec "$BIN/beagle-build-all" "$@"
        fi
        ;;
    fix)
        exec "$BIN/beagle-fix" "$@"
        ;;
    fmt)
        exec "$BIN/beagle-fmt" "$@"
        ;;
    sig)
        exec "$BIN/beagle-sig" "$@"
        ;;
    fields)
        exec "$BIN/beagle-fields" "$@"
        ;;
    ast)
        exec "$BIN/beagle-ast" "$@"
        ;;
    ast-bundle)
        exec "$BIN/beagle-ast-bundle" "$@"
        ;;
    project-session)
        exec "$BIN/beagle-project-session" "$@"
        ;;
    expand)
        exec "$BIN/beagle-expand" "$@"
        ;;
    lsp)
        exec "$BIN/beagle-lsp" "$@"
        ;;
    repl)
        exec "$BIN/beagle-repl" "$@"
        ;;
    init)            exec "$BIN/beagle-init" "$@" ;;
    syntax)          exec "$BIN/beagle-syntax-dispatch" "$@" ;;
    validate)        exec "$BIN/beagle-validate" "$@" ;;
    repair)          exec "$BIN/beagle-repair" "$@" ;;
    doctor)          exec "$BIN/beagle-doctor" "$@" ;;
    callers)         exec "$BIN/beagle-callers" "$@" ;;
    provides)        exec "$BIN/beagle-provides" "$@" ;;
    dts)             exec "$BIN/beagle-dts" "$@" ;;
    ts-externs)      exec "$BIN/beagle-ts-externs" "$@" ;;
    ts-import)       exec "$BIN/beagle-ts-import" "$@" ;;
    impact)          exec "$BIN/beagle-impact" "$@" ;;
    explain)         exec "$BIN/beagle-explain" "$@" ;;
    explain-type)    exec "$BIN/beagle-explain-type" "$@" ;;
    langs)           exec "$BIN/beagle-langs" "$@" ;;
    doc-fill)        exec "$BIN/beagle-doc-fill" "$@" ;;
    facts-roundtrip)
        echo "beagle: native hosted dispatcher is unavailable for facts-roundtrip" >&2
        exit 2
        ;;
    daemon)          exec "$BIN/beagle-daemon" "$@" ;;
    promote)         exec "$BIN/beagle-promote" "$@" ;;
    native-exe)      exec "$BIN/beagle-native-exe" "$@" ;;
    test)            exec "$BIN/beagle-test" "$@" ;;
    rejection-stats) exec "$BIN/beagle-rejection-stats" "$@" ;;
    help|--help|-h)
        usage
        ;;
    *)
        echo "beagle: unknown command '$cmd'" >&2
        echo "Run 'beagle help' for usage." >&2
        exit 1
        ;;
esac
