import { describe, it, expect, vi, beforeEach } from 'vitest';

// Capture the beforeNavigate callback so tests can simulate navigation events
let registeredBeforeNavigate:
	| ((nav: { cancel: () => void; to: { url: { href: string } } | null }) => void)
	| null = null;

const mockGoto = vi.fn();

vi.mock('$app/navigation', () => ({
	beforeNavigate: vi.fn((fn: typeof registeredBeforeNavigate) => {
		registeredBeforeNavigate = fn;
	}),
	goto: mockGoto
}));

const { createUnsavedWarning } = await import('./useUnsavedWarning.svelte');

function simulateNavigate(href: string | null = '/somewhere') {
	const cancel = vi.fn();
	registeredBeforeNavigate?.({
		cancel,
		to: href ? { url: { href } } : null
	});
	return cancel;
}

beforeEach(() => {
	registeredBeforeNavigate = null;
	mockGoto.mockClear();
});

describe('createUnsavedWarning', () => {
	it('isDirty starts false', () => {
		const w = createUnsavedWarning();
		expect(w.isDirty).toBe(false);
	});

	it('markDirty sets isDirty to true', () => {
		const w = createUnsavedWarning();
		w.markDirty();
		expect(w.isDirty).toBe(true);
	});

	it('markDirty hides any existing warning banner', () => {
		const w = createUnsavedWarning();
		// Simulate a navigation event that showed the banner
		w.markDirty();
		simulateNavigate();
		expect(w.showUnsavedWarning).toBe(true);
		// Typing again should hide the banner (form input re-triggers markDirty)
		w.markDirty();
		expect(w.showUnsavedWarning).toBe(false);
	});

	it('beforeNavigate cancels and shows banner when dirty', () => {
		const w = createUnsavedWarning();
		w.markDirty();
		const cancel = simulateNavigate('/admin/users');
		expect(cancel).toHaveBeenCalled();
		expect(w.showUnsavedWarning).toBe(true);
	});

	it('beforeNavigate stores the target URL', () => {
		const w = createUnsavedWarning();
		w.markDirty();
		simulateNavigate('/admin/users');
		expect(w.discardTarget).toBe('/admin/users');
	});

	it('beforeNavigate does not cancel when not dirty', () => {
		createUnsavedWarning();
		const cancel = simulateNavigate('/admin/users');
		expect(cancel).not.toHaveBeenCalled();
	});

	it('discard resets state and navigates to target', () => {
		const w = createUnsavedWarning();
		w.markDirty();
		simulateNavigate('/admin/tags');
		w.discard();
		expect(w.isDirty).toBe(false);
		expect(w.showUnsavedWarning).toBe(false);
		expect(mockGoto).toHaveBeenCalledWith('/admin/tags');
	});

	it('clearOnSuccess resets isDirty and warning', () => {
		const w = createUnsavedWarning();
		w.markDirty();
		simulateNavigate('/somewhere');
		w.clearOnSuccess();
		expect(w.isDirty).toBe(false);
		expect(w.showUnsavedWarning).toBe(false);
	});
});
