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

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
SETUP="$REPO_DIR/scripts/setup.sh"
UNINSTALL="$REPO_DIR/scripts/uninstall.sh"
TEST_ROOT="$(mktemp -d)"

cleanup() {
  rm -rf "$TEST_ROOT"
}
trap cleanup EXIT

fail() { echo "[FAIL] $*" >&2; exit 1; }
pass() { echo "[PASS] $*"; }

run_setup() {
  local home="$1"
  OPENCODE_DIR="$home" bash "$SETUP" >/dev/null
}

run_uninstall() {
  local home="$1"
  OPENCODE_DIR="$home" bash "$UNINSTALL" >/dev/null
}

json_query() {
  node -e "const fs=require('fs'); const data=JSON.parse(fs.readFileSync(process.argv[1],'utf8')); console.log(JSON.stringify(($2), null, 0));" "$1"
}

assert_file_exists() { [ -e "$1" ] || fail "Expected file to exist: $1"; }
assert_file_missing() { [ ! -e "$1" ] || fail "Expected file to be missing: $1"; }
assert_contains() { grep -q "$2" "$1" || fail "Expected $1 to contain $2"; }
assert_not_contains() { ! grep -q "$2" "$1" || fail "Expected $1 not to contain $2"; }

test_created_config_roundtrip() {
  local home="$TEST_ROOT/created-config"
  mkdir -p "$home"

  run_setup "$home"
  assert_file_exists "$home/opencode.jsonc"
  assert_file_exists "$home/.opencode-scholar-manifest.txt"
  assert_file_exists "$home/.opencode-scholar-install-state"
  assert_file_exists "$home/skills/research-ideation/references/research-contract.md"
  grep -Fxq "skills/research-ideation/references/research-contract.md" "$home/.opencode-scholar-manifest.txt" || fail "research contract missing from manifest"

  run_uninstall "$home"
  assert_file_missing "$home/opencode.jsonc"
  assert_file_missing "$home/.opencode-scholar-manifest.txt"
  assert_file_missing "$home/.opencode-scholar-install-state"
  pass "created opencode.jsonc is removed when unchanged"
}

test_existing_config_preserves_user_entries() {
  local home="$TEST_ROOT/existing-config"
  mkdir -p "$home"
  cat > "$home/opencode.jsonc" <<'JSON'
{
  "$schema": "https://opencode.ai/schema.json",
  "agent": {
    "custom-agent": { "description": "mine", "tools": { "read": true }, "prompt": "keep" }
  },
  "mcp": {
    "zotero": { "enabled": false }
  },
  "permission": {
    "read": "allow"
  },
  "plugin": ["file:///user/plugin.ts"]
}
JSON

  run_setup "$home"
  node - "$home/opencode.jsonc" <<'NODE'
const fs = require('fs');
const data = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
if (!data.agent['code-reviewer']) throw new Error('missing injected agent');
if (!data.agent['custom-agent']) throw new Error('lost custom agent');
if (data.mcp.zotero.enabled !== false) throw new Error('overwrote existing zotero.enabled');
if (!data.mcp.zotero.command) throw new Error('missing nested zotero.command');
if (!data.plugin.includes('file:///user/plugin.ts')) throw new Error('lost user plugin');
NODE

  run_uninstall "$home"
  node - "$home/opencode.jsonc" <<'NODE'
const fs = require('fs');
const data = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
if (data.agent && data.agent['code-reviewer']) throw new Error('injected agent remained');
if (!data.agent['custom-agent']) throw new Error('lost custom agent');
if (data.mcp.zotero.enabled !== false) throw new Error('lost user zotero.enabled');
if ('command' in data.mcp.zotero) throw new Error('injected zotero.command remained');
if (!data.plugin.includes('file:///user/plugin.ts')) throw new Error('lost user plugin');
NODE
  pass "existing opencode.jsonc keeps user entries and removes only recorded additions"
}

test_manifest_missing_fails_safely() {
  local home="$TEST_ROOT/no-manifest"
  mkdir -p "$home"
  cat > "$home/opencode.jsonc" <<'JSON'
{ "plugin": ["file:///user/plugin.ts"] }
JSON
  if OPENCODE_DIR="$home" bash "$UNINSTALL" >/dev/null 2>&1; then
    fail "uninstall should fail without manifest/state"
  fi
  assert_contains "$home/opencode.jsonc" "user/plugin"
  pass "uninstall refuses to guess ownership without manifest/state"
}

test_identical_preexisting_file_not_owned() {
  local home="$TEST_ROOT/preexisting-identical"
  mkdir -p "$home/skills/daily-coding"
  cp "$REPO_DIR/skills/daily-coding/SKILL.md" "$home/skills/daily-coding/SKILL.md"

  run_setup "$home"
  assert_file_exists "$home/skills/daily-coding/SKILL.md"
  if grep -Fxq "skills/daily-coding/SKILL.md" "$home/.opencode-scholar-manifest.txt"; then
    fail "identical pre-existing file was incorrectly owned"
  fi

  run_uninstall "$home"
  assert_file_exists "$home/skills/daily-coding/SKILL.md"
  pass "identical pre-existing files are not removed unless previously owned"
}

test_reinstall_preserves_ownership() {
  local home="$TEST_ROOT/reinstall"
  mkdir -p "$home"

  run_setup "$home"
  run_setup "$home"
  assert_file_exists "$home/skills/results-analysis/SKILL.md"
  grep -Fxq "skills/results-analysis/SKILL.md" "$home/.opencode-scholar-manifest.txt" || fail "managed file missing from manifest after reinstall"

  run_uninstall "$home"
  assert_file_missing "$home/skills/results-analysis/SKILL.md"
  pass "reinstall preserves ownership for unchanged managed files"
}

test_legacy_install_adopts_existing_files() {
  local home="$TEST_ROOT/legacy"
  mkdir -p "$home/skills/daily-coding"
  cp "$REPO_DIR/skills/daily-coding/SKILL.md" "$home/skills/daily-coding/SKILL.md"
  cat > "$home/opencode.jsonc" <<JSON
{ "plugin": ["file://$home/plugins/session-summary.ts"] }
JSON

  run_setup "$home"
  grep -Fxq "skills/daily-coding/SKILL.md" "$home/.opencode-scholar-manifest.txt" || fail "legacy identical file was not adopted"
  run_uninstall "$home"
  assert_file_missing "$home/skills/daily-coding/SKILL.md"
  pass "legacy installs can be adopted into manifest-based ownership"
}

test_created_config_changed_is_preserved() {
  local home="$TEST_ROOT/changed-created-config"
  mkdir -p "$home"

  run_setup "$home"
  node - "$home/opencode.jsonc" <<'NODE'
const fs = require('fs');
const path = process.argv[2];
const data = JSON.parse(fs.readFileSync(path, 'utf8'));
data.userOwned = true;
fs.writeFileSync(path, JSON.stringify(data, null, 2) + '\n');
NODE
  run_uninstall "$home"
  assert_file_exists "$home/opencode.jsonc"
  assert_contains "$home/opencode.jsonc" "userOwned"
  pass "installer-created opencode.jsonc is preserved if user changed it"
}

main() {
  bash -n "$SETUP"
  bash -n "$UNINSTALL"
  test_created_config_roundtrip
  test_existing_config_preserves_user_entries
  test_manifest_missing_fails_safely
  test_identical_preexisting_file_not_owned
  test_reinstall_preserves_ownership
  test_legacy_install_adopts_existing_files
  test_created_config_changed_is_preserved
  echo "All installer uninstall smoke tests passed."
}

main "$@"