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

repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)"
tmp_root="$(mktemp -d)"
trap 'rm -rf "$tmp_root"' EXIT
file="$tmp_root/timeline.jsonl"
append="$repo_root/ai/tools/action-timeline/append.sh"
list="$repo_root/ai/tools/action-timeline/list.sh"
archive="$repo_root/ai/tools/action-timeline/archive.sh"

assert_contains() {
  local haystack="$1"
  local needle="$2"
  local label="$3"
  if [[ "$haystack" != *"$needle"* ]]; then
    printf 'FAIL: %s\nExpected: %s\nOutput:\n%s\n' "$label" "$needle" "$haystack" >&2
    exit 1
  fi
}

assert_not_contains() {
  local haystack="$1"
  local needle="$2"
  local label="$3"
  if [[ "$haystack" == *"$needle"* ]]; then
    printf 'FAIL: %s\nUnexpected: %s\nOutput:\n%s\n' "$label" "$needle" "$haystack" >&2
    exit 1
  fi
}

"$append" --file "$file" --type heartbeat --status info --action dispatcher --question-id noisy000 --message "heartbeat tick" >/dev/null
"$append" --file "$file" --type approved_action_registered --status info --action close_commit --question-id qa123456 --message "close_commit registered" >/dev/null
"$append" --file "$file" --type approved_action_blocked --status blocked --action close_commit --question-id qa123456 --message "stale approval: git_status_changed" >/dev/null
"$append" --file "$file" --type daemon_action_completed --status success --action git_add_approved --request-id req99999 --message "staged tracked deletion" >/dev/null
"$append" --file "$file" --type validation_failed --status failed --action runtime_e2e --run-id runabc123 --message "scorecard degraded" --next-action "Run node ai/runtime/dist/cli.js doctor --json" >/dev/null
"$append" --file "$file" --type approved_action_blocked --status blocked --action close_commit --question-id qa123456 --message "stale approval: git_status_changed" >/dev/null
"$append" --file "$file" --type approved_action_blocked --status blocked --action close_commit --question-id qa123456 --message "stale approval: git_status_changed" >/dev/null

human="$($list --human --file "$file" --limit 10)"
assert_contains "$human" "Action Timeline" "human heading"
assert_contains "$human" "Current state" "human includes current state"
assert_contains "$human" "Blocked/stale approvals" "human includes blocked section"
assert_contains "$human" "approved_action_blocked" "human includes blocked action"
assert_contains "$human" "3 repeats" "human deduplicates repeated blocked events"
assert_contains "$human" "Next:" "human includes recommended next action"
assert_not_contains "$human" "heartbeat tick" "human suppresses noisy heartbeat"

json="$($list --json --file "$file" --filter qa123456 --limit 10)"
node -e "const data=JSON.parse(process.argv[1]); if (data.filtered_count !== 4) throw new Error('filter count mismatch'); if (!data.recent.every((e)=>e.question_id==='qa123456')) throw new Error('filter leaked unrelated events'); if (!data.current_state) throw new Error('missing current_state')" "$json"

telegram="$($list --telegram --file "$file" --filter qa123456 --limit 10)"
assert_contains "$telegram" "Action timeline" "telegram heading"
assert_contains "$telegram" "qa123456" "telegram includes id"
assert_contains "$telegram" "More: /timeline_full or /timeline <id>" "telegram includes mobile follow-up"
assert_not_contains "$telegram" '{"' "telegram avoids raw json"

all="$($list --human --all --file "$file" --limit 10)"
assert_contains "$all" "heartbeat tick" "--all includes noisy heartbeat"

run_filtered="$($list --telegram --file "$file" --filter runabc123 --limit 10)"
assert_contains "$run_filtered" "runabc123" "telegram filter supports run id"
assert_contains "$run_filtered" "Run node ai/runtime/dist/cli.js doctor --json" "telegram output includes explicit next action"
assert_not_contains "$run_filtered" "qa123456" "telegram filter excludes unrelated question id"

dry_archive="$($archive --dry-run --file "$file" --max-events 3)"
assert_contains "$dry_archive" "Timeline archive dry-run" "archive dry-run reports plan"
assert_contains "$dry_archive" "Archive events:" "archive dry-run reports archive count"
[[ ! -d "$tmp_root/archive" ]] || {
  printf 'FAIL: dry-run should not create archive dir\n' >&2
  exit 1
}

archive_out="$($archive --file "$file" --max-events 3)"
assert_contains "$archive_out" "Timeline archive rotation" "archive execution reports rotation"
[[ -d "$tmp_root/archive" ]] || {
  printf 'FAIL: archive execution should create archive dir\n' >&2
  exit 1
}
remaining_lines="$(wc -l < "$file" | tr -d ' ')"
[[ "$remaining_lines" == "3" ]] || {
  printf 'FAIL: archive should retain 3 active events, got %s\n' "$remaining_lines" >&2
  exit 1
}
archived_filtered="$($list --json --all --file "$file" --filter qa123456 --limit 10)"
node -e "const data=JSON.parse(process.argv[1]); if (data.archived_count < 1) throw new Error('archive not included with --all/filter'); if (data.archived_total_count < 1) throw new Error('archive total missing'); if (data.filtered_count < 1) throw new Error('archived filter did not find event')" "$archived_filtered"

printf 'action timeline tests passed\n'
