#!/usr/bin/env bash
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: 2025-2026 Marcus Quinn
# Test script for task ID collision prevention (t319.6, t1047)
#
# Tests:
#   1. Parallel claim-task-id.sh calls get different IDs (via .task-counter CAS)
#   2. Offline fallback uses +100 offset from .task-counter
#   3. Supervisor dedup Phase 0.5 resolves duplicates in DB
#   4. Supervisor dedup Phase 0.5b resolves duplicates in TODO.md
#   5. Pre-commit hook rejects duplicate task IDs
#   6. coderabbit-task-creator-helper.sh references claim-task-id.sh correctly (archived t1336 — skipped)
#   7. claim-task-id.sh basic functionality
#   8. get_highest_task_id accuracy (legacy, kept for regression)
#   9. Edge cases
#  10. Batch allocation (--count N)
#  11. .task-counter file validation
#
# All tests use isolated temp directories — no side effects on real data.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" || exit
TEST_DIR="/tmp/t319.6-test-$$"
PASS=0
FAIL=0
SKIP=0

cleanup_test() {
	rm -rf "$TEST_DIR"
	return 0
}

trap cleanup_test EXIT

mkdir -p "$TEST_DIR"

# Colors (Pattern C — prefixed names, test harness only)
readonly TEST_RED=$'\033[0;31m'
readonly TEST_GREEN=$'\033[0;32m'
readonly TEST_YELLOW=$'\033[1;33m'
readonly TEST_BLUE=$'\033[0;34m'
readonly TEST_RESET=$'\033[0m'

pass() {
	local msg="$1"
	printf "%s\n" "${TEST_GREEN}[PASS]${TEST_RESET} $msg"
	PASS=$((PASS + 1))
	return 0
}

fail() {
	local msg="$1"
	printf "%s\n" "${TEST_RED}[FAIL]${TEST_RESET} $msg"
	FAIL=$((FAIL + 1))
	return 0
}

skip() {
	local msg="$1"
	printf "%s\n" "${TEST_YELLOW}[SKIP]${TEST_RESET} $msg"
	SKIP=$((SKIP + 1))
	return 0
}

info() {
	local msg="$1"
	printf "%s\n" "${TEST_BLUE}[INFO]${TEST_RESET} $msg"
	return 0
}

read_counter_or_default() {
	local counter_file="$1"

	if [[ -f "$counter_file" ]]; then
		tr -d '[:space:]' <"$counter_file"
	else
		printf '%s\n' "0"
	fi

	return 0
}

# =============================================================================
# Test 1: Parallel claim-task-id.sh — two concurrent calls get different IDs
# =============================================================================
test_sequential_offline_claim() {
	echo ""
	echo "=== Test 1: Sequential claim-task-id.sh (offline mode) ==="
	info "Two sequential offline calls should get different task IDs (counter increments locally)"

	local test_repo="$TEST_DIR/test-repo-parallel"
	mkdir -p "$test_repo"

	# Initialize a git repo with .task-counter
	(
		cd "$test_repo"
		git init -q
		git config user.email "test@test.com"
		git config user.name "Test"
		echo "100" >.task-counter
		cat >TODO.md <<'EOF'
# TODO

## Active Tasks

- [ ] t99 First task
EOF
		git add TODO.md .task-counter
		git commit -q -m "init"
	)

	# Run two claim-task-id.sh calls sequentially in offline mode
	# (offline mode updates local .task-counter, so sequential calls get different IDs)
	local out1="$TEST_DIR/claim1.out"
	local out2="$TEST_DIR/claim2.out"

	"$SCRIPT_DIR/claim-task-id.sh" --title "Parallel test 1" --offline --no-issue --repo-path "$test_repo" >"$out1" 2>/dev/null || true
	"$SCRIPT_DIR/claim-task-id.sh" --title "Parallel test 2" --offline --no-issue --repo-path "$test_repo" >"$out2" 2>/dev/null || true

	local id1 id2
	id1=$(grep "^task_id=" "$out1" 2>/dev/null | cut -d= -f2 || echo "")
	id2=$(grep "^task_id=" "$out2" 2>/dev/null | cut -d= -f2 || echo "")

	info "Call 1 got: $id1"
	info "Call 2 got: $id2"

	if [[ -n "$id1" && -n "$id2" ]]; then
		if [[ "$id1" != "$id2" ]]; then
			pass "Sequential offline calls got different IDs: $id1 vs $id2"
		else
			fail "Sequential offline calls got same ID ($id1) — counter should have incremented"
		fi
	else
		fail "One or both calls failed to produce a task_id (id1='$id1', id2='$id2')"
	fi

	# Verify counter file was updated
	local final_counter
	final_counter=$(read_counter_or_default "$test_repo/.task-counter")
	info "Final .task-counter value: $final_counter"

	if [[ "$final_counter" -gt 200 ]]; then
		pass "Counter file incremented correctly (now $final_counter)"
	else
		fail "Counter file not incremented as expected (value: $final_counter)"
	fi
	return 0
}

# =============================================================================
# Test 2: Offline fallback uses +100 offset
# =============================================================================
test_offline_fallback() {
	echo ""
	echo "=== Test 2: Offline fallback (+100 offset from .task-counter) ==="
	info "Offline mode should allocate t(counter_value + 100)"

	local test_repo="$TEST_DIR/test-repo-offline"
	mkdir -p "$test_repo"

	# Initialize a git repo with .task-counter at 80
	(
		cd "$test_repo"
		git init -q
		git config user.email "test@test.com"
		git config user.name "Test"
		echo "80" >.task-counter
		cat >TODO.md <<'EOF'
# TODO

## Active Tasks

- [ ] t50 Some task
- [ ] t75 Another task
- [x] t79 Completed task
EOF
		git add TODO.md .task-counter
		git commit -q -m "init"
	)

	local output
	output=$("$SCRIPT_DIR/claim-task-id.sh" --title "Offline test" --offline --no-issue --repo-path "$test_repo" 2>/dev/null) || true

	local task_id ref
	task_id=$(echo "$output" | grep "^task_id=" | cut -d= -f2 || echo "")
	ref=$(echo "$output" | grep "^ref=" | cut -d= -f2 || echo "")

	info "Output: task_id=$task_id ref=$ref"

	# Counter is 80, so offline should give t180 (80 + 100)
	if [[ "$task_id" == "t180" ]]; then
		pass "Offline fallback correctly allocated t180 (counter 80 + offset 100)"
	else
		fail "Expected t180, got '$task_id'"
	fi

	if [[ "$ref" == "offline" ]]; then
		pass "Ref correctly set to 'offline'"
	else
		fail "Expected ref=offline, got '$ref'"
	fi

	# Verify counter was updated locally
	local new_counter
	new_counter=$(read_counter_or_default "$test_repo/.task-counter")
	if [[ "$new_counter" == "181" ]]; then
		pass "Local counter updated to 181 (next available after t180)"
	else
		fail "Expected counter=181, got '$new_counter'"
	fi
	return 0
}

# =============================================================================
# Test 3: Supervisor dedup Phase 0.5 (DB-level)
# =============================================================================

# Helper: create supervisor DB and inject duplicate task IDs
_dedup_db_setup() {
	local test_db="$1"

	sqlite3 "$test_db" <<'SQL'
CREATE TABLE IF NOT EXISTS tasks (
    id TEXT NOT NULL,
    status TEXT NOT NULL DEFAULT 'queued',
    repo TEXT DEFAULT '',
    description TEXT DEFAULT '',
    error TEXT DEFAULT '',
    created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ','now')),
    updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ','now')),
    started_at TEXT,
    log_file TEXT DEFAULT ''
);

-- Insert duplicate task IDs
INSERT INTO tasks (id, status, description, created_at) VALUES ('t200', 'queued', 'First t200', '2026-02-12T10:00:00Z');
INSERT INTO tasks (id, status, description, created_at) VALUES ('t200', 'queued', 'Duplicate t200', '2026-02-12T10:01:00Z');
INSERT INTO tasks (id, status, description, created_at) VALUES ('t201', 'queued', 'Unique t201', '2026-02-12T10:02:00Z');
INSERT INTO tasks (id, status, description, created_at) VALUES ('t202', 'queued', 'First t202', '2026-02-12T10:03:00Z');
INSERT INTO tasks (id, status, description, created_at) VALUES ('t202', 'queued', 'Duplicate t202', '2026-02-12T10:04:00Z');
INSERT INTO tasks (id, status, description, created_at) VALUES ('t202', 'queued', 'Triple t202', '2026-02-12T10:05:00Z');
SQL

	local dup_count
	dup_count=$(sqlite3 "$test_db" "SELECT COUNT(*) FROM (SELECT id FROM tasks GROUP BY id HAVING COUNT(*) > 1);")

	if [[ "$dup_count" -eq 2 ]]; then
		pass "Injected 2 duplicate task IDs (t200 x2, t202 x3)"
	else
		fail "Expected 2 duplicate IDs, found $dup_count"
	fi
	return 0
}

# Helper: run Phase 0.5 dedup logic against the DB
_dedup_db_run_dedup() {
	local test_db="$1"

	local duplicate_ids
	duplicate_ids=$(sqlite3 "$test_db" "
		SELECT id, COUNT(*) as cnt
		FROM tasks
		GROUP BY id
		HAVING cnt > 1;
	" 2>/dev/null || echo "")

	if [[ -n "$duplicate_ids" ]]; then
		while IFS='|' read -r dup_id _dup_count_val; do
			[[ -z "$dup_id" ]] && continue

			# Keep oldest, cancel others
			local all_instances
			all_instances=$(sqlite3 -separator '|' "$test_db" "
				SELECT rowid, created_at, status
				FROM tasks
				WHERE id = '$dup_id'
				ORDER BY created_at ASC;
			" 2>/dev/null || echo "")

			local first_row=true
			while IFS='|' read -r rowid _created_at _status; do
				[[ -z "$rowid" ]] && continue
				if [[ "$first_row" == "true" ]]; then
					first_row=false
				else
					sqlite3 "$test_db" "
						UPDATE tasks
						SET status = 'cancelled',
						    error = 'Duplicate task ID - cancelled by Phase 0.5 dedup (t303)'
						WHERE rowid = $rowid;
					" 2>/dev/null || true
				fi
			done <<<"$all_instances"
		done <<<"$duplicate_ids"
	fi
	return 0
}

# Helper: verify dedup results in the DB
_dedup_db_verify() {
	local test_db="$1"

	local remaining_dups
	remaining_dups=$(sqlite3 "$test_db" "
		SELECT COUNT(*) FROM (
			SELECT id FROM tasks WHERE status != 'cancelled' GROUP BY id HAVING COUNT(*) > 1
		);
	")

	if [[ "$remaining_dups" -eq 0 ]]; then
		pass "Phase 0.5 dedup resolved all DB-level duplicates"
	else
		fail "Still $remaining_dups duplicate IDs after dedup"
	fi

	local cancelled_count
	cancelled_count=$(sqlite3 "$test_db" "SELECT COUNT(*) FROM tasks WHERE status = 'cancelled' AND error LIKE '%Phase 0.5 dedup%';")

	if [[ "$cancelled_count" -eq 3 ]]; then
		pass "3 duplicate tasks correctly cancelled (1 from t200, 2 from t202)"
	else
		fail "Expected 3 cancelled tasks, got $cancelled_count"
	fi

	local queued_count
	queued_count=$(sqlite3 "$test_db" "SELECT COUNT(*) FROM tasks WHERE status = 'queued';")

	if [[ "$queued_count" -eq 3 ]]; then
		pass "3 original tasks remain queued (t200, t201, t202)"
	else
		fail "Expected 3 queued tasks, got $queued_count"
	fi
	return 0
}

test_supervisor_dedup_db() {
	echo ""
	echo "=== Test 3: Supervisor dedup Phase 0.5 (DB-level) ==="
	info "Injecting duplicate task IDs into supervisor DB, verifying dedup"

	local test_db="$TEST_DIR/test-supervisor.db"

	_dedup_db_setup "$test_db"
	_dedup_db_run_dedup "$test_db"
	_dedup_db_verify "$test_db"
	return 0
}

# =============================================================================
# Test 4: Supervisor dedup Phase 0.5b (TODO.md level)
# =============================================================================

# Helper: create TODO.md with intentional duplicate task IDs
_dedup_todo_setup() {
	local test_repo="$1"

	mkdir -p "$test_repo"
	cat >"$test_repo/TODO.md" <<'EOF'
# TODO

## Active Tasks

- [ ] t300 First task description
- [ ] t301 Second task description
- [ ] t300 DUPLICATE of first task
- [ ] t302 Third task description
- [ ] t302 DUPLICATE of third task
- [ ] t302 TRIPLE of third task
- [x] t299 Completed task (should be ignored)

## Backlog

- [ ] t303 Backlog task
EOF
	return 0
}

# Helper: detect duplicates and rename them; writes changes_made count to $2 (temp file)
_dedup_todo_run_dedup() {
	local test_repo="$1"
	local count_file="$2"

	local task_lines
	task_lines=$(grep -nE '^[[:space:]]*- \[ \] t[0-9]+' "$test_repo/TODO.md" | while IFS=: read -r lnum line_content; do
		if [[ "$line_content" =~ ^[[:space:]]*-[[:space:]]\[[[:space:]]\][[:space:]](t[0-9]+(\.[0-9]+)*) ]]; then
			echo "${lnum}|${BASH_REMATCH[1]}"
		fi
	done)

	local dup_ids
	dup_ids=$(echo "$task_lines" | awk -F'|' '{print $2}' | sort | uniq -d)

	if [[ -n "$dup_ids" ]]; then
		pass "Detected duplicate task IDs in TODO.md: $(echo "$dup_ids" | tr '\n' ' ')"
	else
		fail "Failed to detect duplicate task IDs in TODO.md"
		printf '%s\n' "0" >"$count_file"
		return 0
	fi

	local max_num
	max_num=$(grep -oE '(^|[[:space:]])t([0-9]+)' "$test_repo/TODO.md" | grep -oE '[0-9]+' | sort -n | tail -1 || echo "0")
	max_num=$((10#${max_num}))

	info "Highest task number before dedup: t$max_num"

	local changes_made=0

	while IFS= read -r dup_id; do
		[[ -z "$dup_id" ]] && continue

		local occurrences
		occurrences=$(echo "$task_lines" | awk -F'|' -v id="$dup_id" '$2 == id {print $1}')

		local first=true
		while IFS= read -r line_num; do
			[[ -z "$line_num" ]] && continue

			if [[ "$first" == "true" ]]; then
				first=false
				continue
			fi

			max_num=$((max_num + 1))
			local new_id="t${max_num}"
			local old_num="${dup_id#t}"

			# Rename using sed (macOS compatible)
			if [[ "$(uname)" == "Darwin" ]]; then
				sed -i '' -E "${line_num}s/t${old_num}( |$)/${new_id}\1/" "$test_repo/TODO.md"
			else
				sed -i -E "${line_num}s/t${old_num}( |$)/${new_id}\1/" "$test_repo/TODO.md"
			fi

			changes_made=$((changes_made + 1))
		done <<<"$occurrences"
	done <<<"$dup_ids"

	info "Renamed $changes_made duplicate task IDs"
	printf '%s\n' "$changes_made" >"$count_file"
	return 0
}

# Helper: verify no duplicates remain and rename count is correct
_dedup_todo_verify() {
	local test_repo="$1"
	local changes_made="$2"

	local post_task_ids
	post_task_ids=$(grep -E '^[[:space:]]*- \[ \] t[0-9]+' "$test_repo/TODO.md" |
		sed -E 's/^[[:space:]]*- \[ \] (t[0-9]+(\.[0-9]+)*).*/\1/')

	local post_dups
	post_dups=$(echo "$post_task_ids" | sort | uniq -d || true)

	if [[ -z "$post_dups" ]]; then
		pass "All duplicates resolved — no duplicate task IDs remain"
	else
		fail "Still have duplicates after dedup: $post_dups"
	fi

	local renamed_ids
	renamed_ids=$(echo "$post_task_ids" | sort -t't' -k1 -n | tail -"$changes_made")
	info "Renamed tasks now have IDs: $(echo "$renamed_ids" | tr '\n' ' ')"

	if [[ "$changes_made" -eq 3 ]]; then
		pass "Correctly renamed 3 duplicates (1 from t300, 2 from t302)"
	else
		fail "Expected 3 renames, got $changes_made"
	fi
	return 0
}

test_supervisor_dedup_todo() {
	echo ""
	echo "=== Test 4: Supervisor dedup Phase 0.5b (TODO.md level) ==="
	info "Injecting duplicate task IDs into TODO.md, verifying dedup"

	local test_repo="$TEST_DIR/test-repo-dedup"
	local count_file="$TEST_DIR/dedup-todo-count.txt"

	_dedup_todo_setup "$test_repo"
	_dedup_todo_run_dedup "$test_repo" "$count_file"

	local changes_made
	changes_made=$(tr -d '[:space:]' <"$count_file")

	_dedup_todo_verify "$test_repo" "$changes_made"
	return 0
}

# =============================================================================
# Test 5: Pre-commit hook rejects duplicate task IDs
# =============================================================================

# Helper: initialise a git repo with a clean TODO.md baseline
_precommit_setup() {
	local test_repo="$1"

	mkdir -p "$test_repo"
	(
		cd "$test_repo"
		git init -q
		git config user.email "test@test.com"
		git config user.name "Test"
		cat >TODO.md <<'EOF'
# TODO

- [ ] t400 First task
- [ ] t401 Second task
EOF
		git add TODO.md
		git commit -q -m "init"
	)
	return 0
}

# Helper: stage a TODO.md with duplicates and verify the hook detects them
_precommit_check_duplicates() {
	local test_repo="$1"

	cat >"$test_repo/TODO.md" <<'EOF'
# TODO

- [ ] t400 First task
- [ ] t401 Second task
- [ ] t400 DUPLICATE task
- [ ] t402 New task
EOF

	(cd "$test_repo" && git add TODO.md)

	# Simulate the pre-commit hook's duplicate detection logic
	# (We can't install the actual hook in a test repo easily, so we test the logic)
	local task_ids
	task_ids=$(cd "$test_repo" && git show :TODO.md 2>/dev/null |
		grep -E '^[[:space:]]*- \[[x ]\] t[0-9]+' |
		sed -E 's/^[[:space:]]*- \[[x ]\] (t[0-9]+(\.[0-9]+)*).*/\1/' ||
		true)

	local duplicates
	duplicates=$(echo "$task_ids" | sort | uniq -d || true)

	if [[ -n "$duplicates" ]]; then
		pass "Pre-commit logic correctly detected duplicate: $duplicates"
	else
		fail "Pre-commit logic failed to detect duplicate task IDs"
	fi

	if echo "$duplicates" | grep -q "t400"; then
		pass "Correctly identified t400 as the duplicate"
	else
		fail "Expected t400 as duplicate, got: $duplicates"
	fi

	if ! echo "$duplicates" | grep -q "t401"; then
		pass "t401 correctly not flagged as duplicate"
	else
		fail "t401 incorrectly flagged as duplicate"
	fi

	local dup_count
	dup_count=$(echo "$duplicates" | grep -c "." 2>/dev/null || true)
	if [[ "$dup_count" -gt 0 ]]; then
		pass "Pre-commit hook would reject commit ($dup_count duplicate(s) found)"
	else
		fail "Pre-commit hook would incorrectly allow commit"
	fi
	return 0
}

# Helper: stage a clean TODO.md and verify the hook allows it
_precommit_check_clean() {
	local test_repo="$1"

	cat >"$test_repo/TODO.md" <<'EOF'
# TODO

- [ ] t400 First task
- [ ] t401 Second task
- [ ] t402 New task
EOF

	(cd "$test_repo" && git add TODO.md)

	local clean_ids
	clean_ids=$(cd "$test_repo" && git show :TODO.md 2>/dev/null |
		grep -E '^[[:space:]]*- \[[x ]\] t[0-9]+' |
		sed -E 's/^[[:space:]]*- \[[x ]\] (t[0-9]+(\.[0-9]+)*).*/\1/' ||
		true)

	local clean_dups
	clean_dups=$(echo "$clean_ids" | sort | uniq -d || true)

	if [[ -z "$clean_dups" ]]; then
		pass "Pre-commit logic correctly allows clean TODO.md (no duplicates)"
	else
		fail "Pre-commit logic incorrectly flags clean TODO.md"
	fi
	return 0
}

test_precommit_duplicate_rejection() {
	echo ""
	echo "=== Test 5: Pre-commit hook rejects duplicate task IDs ==="
	info "Simulating staged TODO.md with duplicates"

	local test_repo="$TEST_DIR/test-repo-precommit"

	_precommit_setup "$test_repo"
	_precommit_check_duplicates "$test_repo"
	_precommit_check_clean "$test_repo"
	return 0
}

# =============================================================================
# Test 6: coderabbit-task-creator references claim-task-id.sh correctly
# =============================================================================
test_coderabbit_path() {
	echo ""
	echo "=== Test 6: coderabbit-task-creator-helper.sh path verification ==="
	info "coderabbit-task-creator-helper.sh archived (t1336) — skipping"
	skip "coderabbit-task-creator-helper.sh archived to scripts/archived/ (t1336)"
	return 0
}

# =============================================================================
# Test 7: claim-task-id.sh basic functionality
# =============================================================================
test_claim_basic() {
	echo ""
	echo "=== Test 7: claim-task-id.sh basic functionality ==="
	info "Testing --help, --dry-run, and argument validation"

	# Test --help
	local help_output
	help_output=$("$SCRIPT_DIR/claim-task-id.sh" --help 2>&1) || true

	if echo "$help_output" | grep -q "claim-task-id.sh"; then
		pass "--help outputs usage information"
	else
		fail "--help does not output expected usage info"
	fi

	# Test missing --title
	local no_title_output
	no_title_output=$("$SCRIPT_DIR/claim-task-id.sh" 2>&1) || true

	if echo "$no_title_output" | grep -qi "missing.*title\|required.*title"; then
		pass "Missing --title correctly produces error"
	else
		fail "Missing --title did not produce expected error"
	fi

	# Test --dry-run with offline
	local test_repo="$TEST_DIR/test-repo-basic"
	mkdir -p "$test_repo"
	(
		cd "$test_repo"
		git init -q
		git config user.email "test@test.com"
		git config user.name "Test"
		echo "10" >.task-counter
		cat >TODO.md <<'EOF'
# TODO
- [ ] t9 Task
EOF
		git add TODO.md .task-counter
		git commit -q -m "init"
	)

	local dry_output
	dry_output=$("$SCRIPT_DIR/claim-task-id.sh" --title "Dry run test" --offline --dry-run --repo-path "$test_repo" 2>/dev/null) || true

	if echo "$dry_output" | grep -q "DRY_RUN"; then
		pass "--dry-run correctly outputs DRY_RUN placeholder"
	else
		fail "--dry-run did not output expected DRY_RUN placeholder"
	fi
	return 0
}

# =============================================================================
# Test 8: get_highest_task_id function accuracy
# =============================================================================
test_highest_task_id() {
	echo ""
	echo "=== Test 8: get_highest_task_id accuracy ==="
	info "Testing task ID extraction from various TODO.md formats"

	# Source the function from claim-task-id.sh
	# We can't source the whole script (it runs main), so we extract the function
	local test_todo

	# Test case 1: Simple sequential IDs
	test_todo="- [ ] t1 First
- [ ] t2 Second
- [ ] t10 Tenth
- [x] t15 Completed"

	local highest
	highest=0
	while IFS= read -r line; do
		if [[ "$line" =~ ^[[:space:]]*-[[:space:]]\[[[:space:]xX]\][[:space:]]t([0-9]+) ]]; then
			local task_num="${BASH_REMATCH[1]}"
			if ((10#$task_num > 10#$highest)); then
				highest="$task_num"
			fi
		fi
	done <<<"$test_todo"

	if [[ "$highest" -eq 15 ]]; then
		pass "Correctly found highest ID t15 in simple list"
	else
		fail "Expected highest=15, got $highest"
	fi

	# Test case 2: Subtask IDs (tNNN.N)
	test_todo="- [ ] t100 Parent task
  - [ ] t100.1 Subtask 1
  - [ ] t100.2 Subtask 2
- [ ] t200 Another parent
- [x] t150 Completed"

	highest=0
	while IFS= read -r line; do
		if [[ "$line" =~ ^[[:space:]]*-[[:space:]]\[[[:space:]xX]\][[:space:]]t([0-9]+) ]]; then
			local task_num="${BASH_REMATCH[1]}"
			if ((10#$task_num > 10#$highest)); then
				highest="$task_num"
			fi
		fi
	done <<<"$test_todo"

	if [[ "$highest" -eq 200 ]]; then
		pass "Correctly found highest ID t200 with subtasks present"
	else
		fail "Expected highest=200, got $highest"
	fi

	# Test case 3: Empty TODO
	test_todo="# TODO

No tasks yet."

	highest=0
	while IFS= read -r line; do
		if [[ "$line" =~ ^[[:space:]]*-[[:space:]]\[[[:space:]xX]\][[:space:]]t([0-9]+) ]]; then
			local task_num="${BASH_REMATCH[1]}"
			if ((10#$task_num > 10#$highest)); then
				highest="$task_num"
			fi
		fi
	done <<<"$test_todo"

	if [[ "$highest" -eq 0 ]]; then
		pass "Correctly returns 0 for empty TODO"
	else
		fail "Expected highest=0 for empty TODO, got $highest"
	fi
	return 0
}

# =============================================================================
# Test 9: Edge cases — subtask duplicates, completed tasks
# =============================================================================
test_edge_cases() {
	echo ""
	echo "=== Test 9: Edge cases ==="

	# Test: completed tasks should not be flagged as duplicates of open tasks
	local test_todo="- [ ] t500 Open task
- [x] t500 Completed version of same task"

	local open_ids
	open_ids=$(echo "$test_todo" |
		grep -E '^[[:space:]]*- \[ \] t[0-9]+' |
		sed -E 's/^[[:space:]]*- \[ \] (t[0-9]+(\.[0-9]+)*).*/\1/')

	local open_dups
	open_dups=$(echo "$open_ids" | sort | uniq -d || true)

	if [[ -z "$open_dups" ]]; then
		pass "Open + completed with same ID not flagged as duplicate (open-only check)"
	else
		fail "Incorrectly flagged open + completed as duplicate"
	fi

	# Test: pre-commit hook checks ALL task lines (both open and completed)
	local all_ids
	all_ids=$(echo "$test_todo" |
		grep -E '^[[:space:]]*- \[[x ]\] t[0-9]+' |
		sed -E 's/^[[:space:]]*- \[[x ]\] (t[0-9]+(\.[0-9]+)*).*/\1/')

	local all_dups
	all_dups=$(echo "$all_ids" | sort | uniq -d || true)

	if [[ -n "$all_dups" ]]; then
		pass "Pre-commit check (all states) correctly detects t500 duplicate"
	else
		fail "Pre-commit check (all states) missed t500 duplicate"
	fi

	# Test: subtask IDs are treated independently
	test_todo="- [ ] t600 Parent
  - [ ] t600.1 Subtask 1
  - [ ] t600.2 Subtask 2
- [ ] t601 Another parent
  - [ ] t601.1 Subtask 1"

	local subtask_ids
	subtask_ids=$(echo "$test_todo" |
		grep -E '^[[:space:]]*- \[ \] t[0-9]+' |
		sed -E 's/^[[:space:]]*- \[ \] (t[0-9]+(\.[0-9]+)*).*/\1/')

	local subtask_dups
	subtask_dups=$(echo "$subtask_ids" | sort | uniq -d || true)

	if [[ -z "$subtask_dups" ]]; then
		pass "Subtask IDs (t600.1, t601.1) correctly treated as unique"
	else
		fail "Subtask IDs incorrectly flagged as duplicates: $subtask_dups"
	fi

	# Test: inline references (blocked-by:tNNN) should NOT be counted
	test_todo="- [ ] t700 First task blocked-by:t699
- [ ] t701 Second task blocked-by:t700 blocks:t702"

	local ref_ids
	ref_ids=$(echo "$test_todo" |
		grep -E '^[[:space:]]*- \[[x ]\] t[0-9]+' |
		sed -E 's/^[[:space:]]*- \[[x ]\] (t[0-9]+(\.[0-9]+)*).*/\1/')

	local ref_dups
	ref_dups=$(echo "$ref_ids" | sort | uniq -d || true)

	if [[ -z "$ref_dups" ]]; then
		pass "Inline references (blocked-by:tNNN) not counted as task definitions"
	else
		fail "Inline references incorrectly counted as task definitions: $ref_dups"
	fi
	return 0
}

# =============================================================================
# Test 10: Batch allocation (--count N)
# =============================================================================
test_batch_allocation() {
	echo ""
	echo "=== Test 10: Batch allocation (--count N) ==="
	info "Allocating multiple IDs in one atomic operation"

	local test_repo="$TEST_DIR/test-repo-batch"
	mkdir -p "$test_repo"

	(
		cd "$test_repo"
		git init -q
		git config user.email "test@test.com"
		git config user.name "Test"
		echo "500" >.task-counter
		cat >TODO.md <<'EOF'
# TODO
- [ ] t499 Task
EOF
		git add TODO.md .task-counter
		git commit -q -m "init"
	)

	local output
	output=$("$SCRIPT_DIR/claim-task-id.sh" --title "Batch test" --count 5 --offline --no-issue --repo-path "$test_repo" 2>/dev/null) || true

	local task_id task_id_last task_count
	task_id=$(echo "$output" | grep "^task_id=" | cut -d= -f2 || echo "")
	task_id_last=$(echo "$output" | grep "^task_id_last=" | cut -d= -f2 || echo "")
	task_count=$(echo "$output" | grep "^task_count=" | cut -d= -f2 || echo "")

	info "Output: task_id=$task_id task_id_last=$task_id_last task_count=$task_count"

	# Counter starts at 500, offline offset +100 = 600, batch of 5 = t600..t604
	if [[ "$task_id" == "t600" ]]; then
		pass "Batch first ID correct: t600"
	else
		fail "Expected first ID t600, got '$task_id'"
	fi

	if [[ "$task_id_last" == "t604" ]]; then
		pass "Batch last ID correct: t604"
	else
		fail "Expected last ID t604, got '$task_id_last'"
	fi

	if [[ "$task_count" == "5" ]]; then
		pass "Batch count correct: 5"
	else
		fail "Expected count 5, got '$task_count'"
	fi

	# Verify counter was updated
	local new_counter
	new_counter=$(read_counter_or_default "$test_repo/.task-counter")
	if [[ "$new_counter" == "605" ]]; then
		pass "Counter updated to 605 after batch allocation"
	else
		fail "Expected counter=605, got '$new_counter'"
	fi
	return 0
}

# =============================================================================
# Test 11: .task-counter file validation
# =============================================================================
# Helper: initialise a git repo without .task-counter for validation tests
_counter_validation_setup() {
	local test_repo="$1"

	mkdir -p "$test_repo"
	(
		cd "$test_repo"
		git init -q
		git config user.email "test@test.com"
		git config user.name "Test"
		cat >TODO.md <<'EOF'
# TODO
- [ ] t1 Task
EOF
		git add TODO.md
		git commit -q -m "init"
	)
	return 0
}

# Helper: verify missing .task-counter auto-bootstraps from TODO.md (GH#6569)
_counter_validation_check_missing() {
	local test_repo="$1"

	# Remove any existing counter to simulate missing state
	rm -f "$test_repo/.task-counter"

	local output
	output=$("$SCRIPT_DIR/claim-task-id.sh" --title "No counter test" --offline --no-issue --repo-path "$test_repo" 2>&1) || true

	local task_id
	task_id=$(echo "$output" | grep "^task_id=" | cut -d= -f2 || echo "")

	# TODO.md has t1, so seed=2, offline offset=100 → t102
	if [[ -n "$task_id" ]] && [[ "$task_id" =~ ^t[0-9]+$ ]]; then
		pass "Missing .task-counter auto-bootstraps from TODO.md (got $task_id)"
	else
		fail "Missing .task-counter should auto-bootstrap, got: $task_id (output: $output)"
	fi

	# Verify BOOTSTRAP_COUNTER_OK marker was emitted
	if echo "$output" | grep -q "BOOTSTRAP_COUNTER_OK"; then
		pass "BOOTSTRAP_COUNTER_OK marker emitted for observability"
	else
		fail "BOOTSTRAP_COUNTER_OK marker not found in output"
	fi

	# Verify .task-counter was created locally
	if [[ -f "$test_repo/.task-counter" ]]; then
		pass ".task-counter file created after bootstrap"
	else
		fail ".task-counter file not created after bootstrap"
	fi
	return 0
}

# Helper: verify non-numeric .task-counter auto-bootstraps, and valid counter works
_counter_validation_check_values() {
	local test_repo="$1"

	# Test with non-numeric .task-counter — should auto-bootstrap from TODO.md
	echo "abc" >"$test_repo/.task-counter"
	local output
	output=$("$SCRIPT_DIR/claim-task-id.sh" --title "Bad counter test" --offline --no-issue --repo-path "$test_repo" 2>&1) || true

	local task_id
	task_id=$(echo "$output" | grep "^task_id=" | cut -d= -f2 || echo "")

	if [[ -n "$task_id" ]] && [[ "$task_id" =~ ^t[0-9]+$ ]]; then
		pass "Non-numeric .task-counter auto-bootstraps from TODO.md (got $task_id)"
	else
		fail "Non-numeric .task-counter should auto-bootstrap, got: $task_id"
	fi

	# Test with valid .task-counter
	echo "42" >"$test_repo/.task-counter"
	output=$("$SCRIPT_DIR/claim-task-id.sh" --title "Valid counter test" --offline --no-issue --repo-path "$test_repo" 2>/dev/null) || true

	task_id=$(echo "$output" | grep "^task_id=" | cut -d= -f2 || echo "")

	if [[ "$task_id" == "t142" ]]; then
		pass "Valid .task-counter (42) correctly allocates t142 (42 + 100 offset)"
	else
		fail "Expected t142, got '$task_id'"
	fi
	return 0
}

test_counter_validation() {
	echo ""
	echo "=== Test 11: .task-counter file validation (GH#6569 auto-bootstrap) ==="
	info "Testing auto-bootstrap when .task-counter is missing or invalid"

	local test_repo="$TEST_DIR/test-repo-validate"

	_counter_validation_setup "$test_repo"
	_counter_validation_check_missing "$test_repo"
	_counter_validation_check_values "$test_repo"
	return 0
}

# =============================================================================
# Test 12: Fresh repo bootstrap (no TODO.md, no .task-counter) — GH#6569
# =============================================================================
test_fresh_repo_bootstrap() {
	echo ""
	echo "=== Test 12: Fresh repo bootstrap (no TODO.md, no .task-counter) ==="
	info "Testing auto-bootstrap seeds counter at 1 when no TODO.md exists"

	local test_repo="$TEST_DIR/test-repo-fresh"
	mkdir -p "$test_repo"
	(
		cd "$test_repo"
		git init -q
		git config user.email "test@test.com"
		git config user.name "Test"
		# No TODO.md, no .task-counter — truly fresh repo
		touch README.md
		git add README.md
		git commit -q -m "init"
	)

	local output
	output=$("$SCRIPT_DIR/claim-task-id.sh" --title "Fresh repo test" --offline --no-issue --repo-path "$test_repo" 2>&1) || true

	local task_id
	task_id=$(echo "$output" | grep "^task_id=" | cut -d= -f2 || echo "")

	# Seed=1 (no TODO.md), offline offset=100 → t101
	if [[ "$task_id" == "t101" ]]; then
		pass "Fresh repo (no TODO.md) bootstraps at seed=1, allocates t101"
	else
		fail "Expected t101 for fresh repo, got '$task_id'"
	fi

	# Verify .task-counter was created
	if [[ -f "$test_repo/.task-counter" ]]; then
		pass ".task-counter created in fresh repo after bootstrap"
	else
		fail ".task-counter not created in fresh repo"
	fi
	return 0
}

# =============================================================================
# Run all tests
# =============================================================================

echo "============================================="
echo "  Task ID Collision Prevention Tests (t319.6, t1047)"
echo "============================================="
echo ""
echo "Test environment: $TEST_DIR"
echo "Script directory: $SCRIPT_DIR"
echo ""

test_sequential_offline_claim
test_offline_fallback
test_supervisor_dedup_db
test_supervisor_dedup_todo
test_precommit_duplicate_rejection
test_coderabbit_path
test_claim_basic
test_highest_task_id
test_edge_cases
test_batch_allocation
test_counter_validation
test_fresh_repo_bootstrap

# =============================================================================
# Summary
# =============================================================================

echo ""
echo "============================================="
echo "  Test Summary"
echo "============================================="
echo ""
echo -e "  ${TEST_GREEN}PASS${TEST_RESET}: $PASS"
echo -e "  ${TEST_RED}FAIL${TEST_RESET}: $FAIL"
echo -e "  ${TEST_YELLOW}SKIP${TEST_RESET}: $SKIP"
echo ""

TOTAL=$((PASS + FAIL + SKIP))
echo "  Total: $TOTAL tests"
echo ""

if [[ "$FAIL" -eq 0 ]]; then
	echo -e "${TEST_GREEN}All tests passed!${TEST_RESET}"
	exit 0
else
	echo -e "${TEST_RED}$FAIL test(s) failed${TEST_RESET}"
	exit 1
fi
