/**
 * Notification Event Model — regression tests
 *
 * Verifies that:
 * 1. headless-events.ts classification functions use metadata fields first.
 * 2. String-matching fallbacks still work for untagged notifications.
 * 3. notification-store deduplicates by dedupe_key when provided.
 * 4. Automated (non-blocking, non-terminal) notices do NOT produce terminal/blocked results.
 */

import assert from "node:assert/strict";
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, test } from 'vitest';

import {
	isBlockedNotification,
	isMilestoneReadyNotification,
	isPauseNotification,
	isTerminalNotification,
} from "../../../../headless-events.ts";
import {
	appendNotification,
	initNotificationStore,
	readNotifications,
} from "../notification-store.ts";

// ── Helpers ──────────────────────────────────────────────────────────────────

function notifyEvent(
	message: string,
	metadata?: Record<string, unknown>,
): Record<string, unknown> {
	return {
		type: "extension_ui_request",
		method: "notify",
		message,
		...(metadata ? { metadata } : {}),
	};
}

// ── headless-events: metadata-first classification ────────────────────────────

describe("isTerminalNotification — metadata-first", () => {
	test("returns true when metadata.kind === 'terminal', even with non-matching text", () => {
		const event = notifyEvent("Some unrelated message.", { kind: "terminal" });
		assert.equal(isTerminalNotification(event), true);
	});

	test("returns true for legacy 'auto-mode stopped' prefix (fallback)", () => {
		const event = notifyEvent("Auto-mode stopped. Session: ...");
		assert.equal(isTerminalNotification(event), true);
	});

	test("returns true for legacy 'step-mode stopped' prefix (fallback)", () => {
		const event = notifyEvent("Step-mode stopped.");
		assert.equal(isTerminalNotification(event), true);
	});

	test("returns false for unrelated notification without metadata", () => {
		const event = notifyEvent("Slice S01 complete.");
		assert.equal(isTerminalNotification(event), false);
	});

	test("returns false for progress metadata (non-terminal)", () => {
		const event = notifyEvent("Compiling...", { kind: "progress" });
		assert.equal(isTerminalNotification(event), false);
	});
});

describe("isBlockedNotification — metadata-first", () => {
	test("returns true when metadata.blocking === true", () => {
		const event = notifyEvent("Write gate prevented this action.", {
			blocking: true,
			kind: "notice",
		});
		assert.equal(isBlockedNotification(event), true);
	});

	test("returns false for progress notice even with blocking=true", () => {
		const event = notifyEvent("Loading...", {
			blocking: true,
			kind: "progress",
		});
		assert.equal(isBlockedNotification(event), false);
	});

	test("returns true for legacy 'blocked:' text (fallback)", () => {
		const event = notifyEvent("Auto-mode stopped — Blocked: write gate.");
		assert.equal(isBlockedNotification(event), true);
	});

	test("returns false for non-blocking automated notice (the key regression)", () => {
		// An automated status notice must NOT be classified as blocked.
		const event = notifyEvent("Iteration 3 complete. Running verification...", {
			kind: "progress",
			source: "workflow",
		});
		assert.equal(isBlockedNotification(event), false);
	});
});

describe("isMilestoneReadyNotification — metadata-first", () => {
	test("returns true when metadata.kind === 'approval_request' and blocking === true", () => {
		const event = notifyEvent("Milestone M001 ready.", {
			kind: "approval_request",
			blocking: true,
		});
		assert.equal(isMilestoneReadyNotification(event), true);
	});

	test("returns false when approval_request but blocking not set (text doesn't match fallback)", () => {
		// Use text that doesn't match the legacy regex so we can test metadata-only path.
		const event = notifyEvent("Review needed.", {
			kind: "approval_request",
		});
		assert.equal(isMilestoneReadyNotification(event), false);
	});

	test("returns true for legacy 'Milestone M001 ready' text (fallback)", () => {
		const event = notifyEvent("Milestone M001 is ready for review.");
		assert.equal(isMilestoneReadyNotification(event), true);
	});
});

describe("isPauseNotification — metadata-first", () => {
	test("returns true when metadata.kind=terminal and blocking=true", () => {
		const event = notifyEvent(
			"Autonomous mode paused. Type to interact.",
			{ kind: "terminal", blocking: true },
		);
		assert.equal(isPauseNotification(event), true);
	});
});

// ── notification-store: dedupe_key deduplication ─────────────────────────────

describe("notification-store — dedupe_key", () => {
	let tmpDir: string;

	beforeEach(() => {
		tmpDir = mkdtempSync(join(tmpdir(), "sf-notif-test-"));
		mkdirSync(join(tmpDir, ".sf"), { recursive: true });
		initNotificationStore(tmpDir);
	});

	afterEach(() => {
		rmSync(tmpDir, { recursive: true, force: true });
	});

	test("deduplicates by dedupe_key within the window", () => {
		appendNotification("Syncing...", "info", "notify", {
			dedupe_key: "sync:progress",
		});
		appendNotification("Syncing with different text...", "info", "notify", {
			dedupe_key: "sync:progress",
		});
		const entries = readNotifications(tmpDir);
		assert.equal(entries.length, 1, "second entry with same dedupe_key should be dropped");
	});

	test("does not deduplicate across different dedupe_keys", () => {
		appendNotification("Syncing A...", "info", "notify", {
			dedupe_key: "sync:A",
		});
		appendNotification("Syncing B...", "info", "notify", {
			dedupe_key: "sync:B",
		});
		const entries = readNotifications(tmpDir);
		assert.equal(entries.length, 2, "different dedupe_keys should produce separate entries");
	});

	test("stores metadata on the entry", () => {
		appendNotification("Ready.", "info", "notify", {
			kind: "approval_request",
			blocking: true,
			source: "workflow",
		});
		const entries = readNotifications(tmpDir);
		assert.equal(entries.length, 1);
		assert.equal(entries[0].metadata?.kind, "approval_request");
		assert.equal(entries[0].metadata?.blocking, true);
		assert.equal(entries[0].metadata?.source, "workflow");
	});

	test("automated progress notice does not affect blocking classification", () => {
		appendNotification("Running checks...", "info", "notify", {
			kind: "progress",
			source: "workflow",
		});
		const entries = readNotifications(tmpDir);
		assert.equal(entries.length, 1);
		// The notice is stored, but kind=progress means headless will not treat it as blocked.
		assert.equal(entries[0].metadata?.kind, "progress");
		// Confirm headless classification: this event should NOT be blocked
		const fakeEvent = notifyEvent("Running checks...", {
			kind: "progress",
			source: "workflow",
		});
		assert.equal(isBlockedNotification(fakeEvent), false);
	});
});
