#!/usr/bin/env bash

set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
OKF_BIN="$ROOT_DIR/bin/okf"

fail() {
  printf 'FAIL: %s\n' "$1" >&2
  exit 1
}

assert_eq() {
  local actual="$1"
  local expected="$2"
  local message="$3"
  [[ "$actual" == "$expected" ]] || fail "$message (expected '$expected', got '$actual')"
}

assert_contains() {
  local haystack="$1"
  local needle="$2"
  local message="$3"
  [[ "$haystack" == *"$needle"* ]] || fail "$message (missing '$needle')"
}

assert_file_contains() {
  local file_path="$1"
  local needle="$2"
  local message="$3"
  [[ -f "$file_path" ]] || fail "$message (missing file $file_path)"
  grep -Fq -- "$needle" "$file_path" || fail "$message (missing '$needle' in $file_path)"
}

run_okf() {
  local stdout_file="$1"
  local stderr_file="$2"
  shift 2

  "$OKF_BIN" "$@" >"$stdout_file" 2>"$stderr_file"
}

make_fixture() {
  local tmpdir
  tmpdir="$(mktemp -d)"
  mkdir -p "$tmpdir/repo" "$tmpdir/home"
  printf '%s\n' "$tmpdir"
}

write_memory_file() {
  local repo_root="$1"
  local home_dir="$2"
  local file_name="$3"
  local content="$4"

  local encoded_repo
  encoded_repo="$(node -e "console.log(require('node:path').resolve(process.argv[1]).replace(/[\\\\/]/g, '-'))" "$repo_root")"
  local memory_dir="$home_dir/.claude/projects/$encoded_repo/memory"

  mkdir -p "$memory_dir"
  printf '%s' "$content" >"$memory_dir/$file_name"
}

test_usage_error() {
  local tmpdir stdout stderr rc
  tmpdir="$(make_fixture)"
  stdout="$tmpdir/stdout"
  stderr="$tmpdir/stderr"

  rc=0
  run_okf "$stdout" "$stderr" unknown || rc=$?
  assert_eq "$rc" "2" "unknown verb exits 2"
  assert_contains "$(cat "$stderr")" "unknown verb" "usage error explains unknown verb"

  rm -rf "$tmpdir"
}

test_project_and_capture_and_sync() {
  local tmpdir repo_root home_dir stdout stderr rc sync_output
  tmpdir="$(make_fixture)"
  repo_root="$tmpdir/repo"
  home_dir="$tmpdir/home"
  stdout="$tmpdir/stdout"
  stderr="$tmpdir/stderr"

  write_memory_file "$repo_root" "$home_dir" "fact-alpha.md" "$(cat <<'EOF'
---
metadata:
  type: reference
name: fact-alpha
description: Alpha fact.
---

Alpha body.
EOF
)"

  rc=0
  HOME="$home_dir" run_okf "$stdout" "$stderr" capture --repo "$repo_root" || rc=$?
  assert_eq "$rc" "0" "capture exits 0"
  assert_file_contains "$repo_root/.okf/concepts/fact-alpha.md" "okf_source: claude-memory" "capture writes concept"

  rc=0
  HOME="$home_dir" run_okf "$stdout" "$stderr" project --repo "$repo_root" --project-name harness-nongated || rc=$?
  assert_eq "$rc" "0" "project exits 0"
  assert_file_contains "$home_dir/CLAUDE.md" "project \`harness-nongated\`" "project writes managed home projection"

  rm -rf "$repo_root/.okf"
  rm -f "$home_dir/CLAUDE.md" "$home_dir/AGENTS.md" "$home_dir/MEMORY.md"
  rm -rf "$home_dir/.claude/memory.md"

  rc=0
  HOME="$home_dir" run_okf "$stdout" "$stderr" sync --repo "$repo_root" --project-name harness-nongated || rc=$?
  assert_eq "$rc" "0" "sync exits 0"
  sync_output="$(cat "$stdout")"
  assert_contains "$sync_output" '"verb":"capture"' "sync reports capture first"
  assert_contains "$sync_output" '"verb":"project"' "sync reports project second"
  [[ "$sync_output" == *'"verb":"capture"'*'"verb":"project"'* ]] || fail "sync preserves capture-then-project ordering"
  assert_file_contains "$repo_root/.okf/concepts/fact-alpha.md" "Alpha body." "sync captures memory before finishing"
  assert_file_contains "$home_dir/CLAUDE.md" "OKF root" "sync projects after capture"

  rm -rf "$tmpdir"
}

test_new_and_index() {
  local tmpdir repo_root stdout stderr rc
  tmpdir="$(make_fixture)"
  repo_root="$tmpdir/repo"
  stdout="$tmpdir/stdout"
  stderr="$tmpdir/stderr"

  rc=0
  run_okf "$stdout" "$stderr" new --repo "$repo_root" --path concepts/topic-alpha.md --type reference --title "Topic Alpha" --description "Alpha summary." --body "Alpha details." --tags alpha,topic || rc=$?
  assert_eq "$rc" "0" "new exits 0"
  assert_file_contains "$repo_root/.okf/concepts/topic-alpha.md" "type: reference" "new writes frontmatter"
  assert_file_contains "$repo_root/.okf/concepts/index.md" "- [Topic Alpha](topic-alpha.md) - Alpha summary." "new updates parent index"

  rc=0
  run_okf "$stdout" "$stderr" new --repo "$repo_root" --path concepts/topic-beta.md --type reference --title "Topic Beta" --description "Beta summary." --body "Beta details." || rc=$?
  assert_eq "$rc" "0" "second new exits 0"

  rm -f "$repo_root/.okf/concepts/index.md"
  rc=0
  run_okf "$stdout" "$stderr" index --repo "$repo_root" --dir concepts || rc=$?
  assert_eq "$rc" "0" "index exits 0"
  assert_file_contains "$repo_root/.okf/concepts/index.md" "- [Topic Alpha](topic-alpha.md) - Alpha summary." "index rebuilds alpha entry"
  assert_file_contains "$repo_root/.okf/concepts/index.md" "- [Topic Beta](topic-beta.md) - Beta summary." "index rebuilds beta entry"

  rm -rf "$tmpdir"
}

test_promote_and_doctor() {
  local tmpdir repo_root stdout stderr rc
  tmpdir="$(make_fixture)"
  repo_root="$tmpdir/repo"
  stdout="$tmpdir/stdout"
  stderr="$tmpdir/stderr"

  mkdir -p "$repo_root/.okf"
  cat >"$repo_root/.okf/scope.json" <<'EOF'
{
  "global": { "tier": "regular" },
  "projects": {
    "alpha": {
      "jobs": {
        "build": { "tier": "critical", "retries": 2 }
      }
    }
  }
}
EOF

  rc=0
  run_okf "$stdout" "$stderr" promote --repo "$repo_root" --scope .okf/scope.json --project alpha --job build || rc=$?
  assert_eq "$rc" "0" "promote exits 0"
  assert_file_contains "$repo_root/.okf/scope.json" '"tier": "critical"' "promote writes promoted tier"
  assert_file_contains "$repo_root/.okf/scope.json" '"retries": 2' "promote keeps sibling job data"

  run_okf "$stdout" "$stderr" new --repo "$repo_root" --path concepts/topic-alpha.md --type reference --title "Topic Alpha" --description "Alpha summary." --body "Alpha details." >/dev/null 2>&1 || true
  rc=0
  run_okf "$stdout" "$stderr" doctor --repo "$repo_root" || rc=$?
  assert_eq "$rc" "0" "doctor exits 0 for healthy store"
  assert_contains "$(cat "$stdout")" '"ok":true' "doctor reports healthy status"

  printf '%s\n' '# broken index' >"$repo_root/.okf/concepts/index.md"
  rc=0
  run_okf "$stdout" "$stderr" doctor --repo "$repo_root" || rc=$?
  assert_eq "$rc" "3" "doctor exits 3 for malformed store"
  assert_contains "$(cat "$stderr")" "missing index entry" "doctor reports missing index entry"

  rm -rf "$tmpdir"
}

main() {
  [[ -x "$OKF_BIN" ]] || fail "missing executable $OKF_BIN"
  test_usage_error
  test_project_and_capture_and_sync
  test_new_and_index
  test_promote_and_doctor
  printf 'PASS okf CLI\n'
}

main "$@"
