#!/usr/bin/env bash
# Automated pre-checks for tool_adapter_train_ui PR manual smoke checklist.
# GPU / full-app steps still require operator sign-off (marked MANUAL).
set -euo pipefail

REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$REPO_ROOT"

PASS=0
FAIL=0
MANUAL=0

ok() { echo "  PASS: $1"; PASS=$((PASS + 1)); }
fail() { echo "  FAIL: $1"; FAIL=$((FAIL + 1)); }
manual() { echo "  MANUAL: $1"; MANUAL=$((MANUAL + 1)); }

section() { echo ""; echo "=== $1 ==="; }

section "Unit tests (parser, resolver, stale status, integration)"
if python3 -m unittest \
  tests.test_mlx_lora_train_progress \
  tests.test_tool_adapter_trainer \
  tests.test_mlx_training_stale_status \
  tests.test_tool_adapter_training_integration \
  -q 2>/dev/null; then
  ok "Python unit tests (17 cases)"
else
  fail "Python unit tests"
fi

section "Plan scope"
if make plan-scope ARGS="--diff" >/dev/null 2>&1; then
  ok "make plan-scope --diff (within cap or no staged diff)"
else
  fail "make plan-scope --diff"
fi

section "CLI dry-run (plan resolver)"
MLX_PY="${HOME}/.mugi/mlx-venv/bin/python"
if [[ -x "$MLX_PY" ]]; then
  export MUGI_REPO_ROOT="$REPO_ROOT"
  export PYTHONPATH="$REPO_ROOT${PYTHONPATH:+:$PYTHONPATH}"
  if "$MLX_PY" -m tool_adapter_trainer --help >/dev/null 2>&1 || true; then
    :
  fi
  DRY_JSON="$(./scripts/train_mugi_tool_adapter.sh --dry-run 2>/dev/null)" || DRY_JSON=""
  if [[ -n "$DRY_JSON" ]] && printf '%s' "$DRY_JSON" | python3 -c "
import json, os, sys
p = json.load(sys.stdin)
assert p.get('ok'), p
name = p.get('adapter_name') or ''
assert name.startswith('mugi-agent-tools-'), name
repo = os.environ.get('MUGI_REPO_ROOT', '.')
bundle_ok = all(
    os.path.isfile(os.path.join(repo, 'training/mlx_mugi_agent_tools', f))
    for f in ('train.jsonl', 'valid.jsonl')
)
if not p.get('dataset_ok'):
    assert bundle_ok, (p.get('dataset_error') or 'dataset_ok false')
else:
    assert (p.get('train_examples') or 0) > 0
"; then
    ok "train_mugi_tool_adapter.sh --dry-run (plan ok, adapter_name, dataset)"
  else
    fail "train_mugi_tool_adapter.sh --dry-run"
  fi
else
  manual "train_mugi_tool_adapter.sh --dry-run (MLX venv missing at $MLX_PY)"
fi

section "Bundled dataset in repo"
for f in train.jsonl valid.jsonl dataset_meta.json; do
  if [[ -f "$REPO_ROOT/training/mlx_mugi_agent_tools/$f" ]]; then
    ok "repo training/mlx_mugi_agent_tools/$f"
  else
    fail "missing training/mlx_mugi_agent_tools/$f"
  fi
done

section "Release bundle copy (item 3)"
BUNDLE_TMP="$(mktemp -d)"
trap 'rm -rf "$BUNDLE_TMP"' EXIT
if bash scripts/copy_backend_bundle.sh "$REPO_ROOT" "$BUNDLE_TMP/Backend" 2>/dev/null; then
  for f in train.jsonl valid.jsonl dataset_meta.json; do
    if [[ -f "$BUNDLE_TMP/Backend/training/mlx_mugi_agent_tools/$f" ]]; then
      ok "bundle ships training/mlx_mugi_agent_tools/$f"
    else
      fail "bundle missing training/mlx_mugi_agent_tools/$f"
    fi
  done
  for mod in mlx_training_run.py tool_adapter_trainer.py fine_tuner.py; do
    if [[ -f "$BUNDLE_TMP/Backend/$mod" ]]; then
      ok "bundle ships $mod"
    else
      fail "bundle missing $mod"
    fi
  done
else
  fail "copy_backend_bundle.sh"
fi

section "HTTP contracts (Starlette)"
if python3 -c "import starlette" 2>/dev/null; then
  if python3 -m unittest \
    tests.test_wave2_http_contracts.TestWave2SatelliteRoutes.test_tool_adapter_plan_route \
    tests.test_wave2_http_contracts.TestWave2SatelliteRoutes.test_tool_adapter_start_busy_returns_409 \
    -q 2>/dev/null; then
    ok "HTTP contract tests (tool_adapter plan + 409 busy)"
  else
    fail "HTTP contract tests (tool_adapter routes)"
  fi
else
  manual "HTTP contract tests (pip install starlette, then re-run script)"
fi

section "Swift sources present"
for f in MugiApp/Mugi/ToolAdapterTraining.swift; do
  if [[ -f "$REPO_ROOT/$f" ]]; then
    ok "$f exists"
  else
    fail "missing $f"
  fi
done
if grep -q 'toolAdapterTrainVisible' MugiApp/Mugi/Preferences/LLMsPrefsPane.swift \
  && grep -q 'useExternalBackend' MugiApp/Mugi/Preferences/LLMsPrefsPane.swift; then
  ok "LLMsPrefsPane gates train on mlx && !external backend (item 8 code)"
else
  fail "external backend gating in LLMsPrefsPane"
fi
if grep -q 'grpo_busy' MugiApp/Mugi/Preferences/LLMsPrefsPane.swift; then
  ok "GRPO busy disables train button (item 9 code)"
else
  fail "GRPO gating in LLMsPrefsPane"
fi

section "PR manual smoke checklist (operator)"
manual "1. Dev: Train with progress + ETA after iter 3; cancel dismisses overlay"
manual "2. Complete → adapter listed, active if toggled, chat works (MLX up)"
manual "4. Kill app mid-train → relaunch → resume overlay or interrupted error"
manual "5. Probe native tool calls after apply (README smoke eval prompts)"
manual "6. Overwrite existing adapter with confirmation sheet"
manual "7. Start train → chat/send fails gracefully while overlay shown"
manual "8. External backend ON → train hidden/disabled (verify in Settings)"
manual "9. GRPO running → train blocked (verify if evolution testable)"

section "Post-train eval (training/mlx_mugi_agent_tools/README.md)"
manual "Eval 1: read_file kanban config from ~/.mugi/mugi_config.toml"
manual "Eval 2: kanban_create_batch with parent_indices fan-in"
manual "Eval 3: list_mesh_peers then contact_peer"

echo ""
echo "Summary: PASS=$PASS FAIL=$FAIL MANUAL=$MANUAL"
if [[ "$FAIL" -gt 0 ]]; then
  exit 1
fi
exit 0
