import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';
import { createNotificationStore, NOTIFICATION_KEY } from '$lib/notification/notifications.svelte';

const mockNavigating = { type: null };
const mockPage = { url: new URL('http://localhost/aktivitaeten') };

vi.mock('$app/state', () => ({
	get navigating() {
		return mockNavigating;
	},
	get page() {
		return mockPage;
	}
}));

vi.mock('$app/navigation', () => ({
	beforeNavigate: () => {},
	afterNavigate: () => {},
	goto: vi.fn(),
	invalidate: vi.fn(),
	invalidateAll: vi.fn(),
	preloadCode: vi.fn(),
	preloadData: vi.fn(),
	pushState: vi.fn(),
	replaceState: vi.fn(),
	disableScrollHandling: vi.fn(),
	onNavigate: () => () => {}
}));

// The Chronik page's onMount calls store.init(), opening an EventSource and
// fetching the unread count. Stub both so no real network / 401 → /login fires.
class NoopEventSource {
	static CLOSED = 2;
	readyState = 0;
	onopen: (() => void) | null = null;
	onerror: (() => void) | null = null;
	addEventListener() {}
	close() {}
}

beforeEach(() => {
	vi.stubGlobal('EventSource', NoopEventSource);
	vi.stubGlobal(
		'fetch',
		vi
			.fn()
			.mockResolvedValue(new Response(JSON.stringify({ count: 0, content: [] }), { status: 200 }))
	);
});

const { default: AktivitaetenPage } = await import('./+page.svelte');

afterEach(() => {
	cleanup();
	vi.unstubAllGlobals();
});

const notificationContext = () => new Map([[NOTIFICATION_KEY, createNotificationStore()]]);

const baseData = (overrides: Record<string, unknown> = {}) => ({
	filter: 'alle' as const,
	activityFeed: [],
	unreadNotifications: [],
	loadError: null,
	...overrides
});

describe('aktivitaeten page', () => {
	it('renders the page heading', async () => {
		render(AktivitaetenPage, { context: notificationContext(), props: { data: baseData() } });

		await expect.element(page.getByRole('heading', { name: /aktivitäten/i })).toBeVisible();
	});

	it('renders the error card when loadError is "activity"', async () => {
		render(AktivitaetenPage, {
			context: notificationContext(),
			props: { data: baseData({ loadError: 'activity' }) }
		});

		// ChronikErrorCard renders some retry mechanism
		const main = document.querySelector('main');
		expect(main).not.toBeNull();
		// FuerDichBox should NOT render when loadError is set
		const fuerDich = document.querySelector('[data-testid="chronik-inbox-zero"]');
		expect(fuerDich).toBeNull();
	});

	it('renders the FuerDichBox and FilterPills when loadError is null', async () => {
		render(AktivitaetenPage, { context: notificationContext(), props: { data: baseData() } });

		// FuerDichBox shows the inbox-zero state when no unread
		const fuerDich = document.querySelector('[data-testid="chronik-inbox-zero"]');
		expect(fuerDich).not.toBeNull();

		// FilterPills present
		const radiogroup = document.querySelector('[role="radiogroup"]');
		expect(radiogroup).not.toBeNull();
	});

	it('renders the first-run empty state when activityFeed is empty', async () => {
		render(AktivitaetenPage, { context: notificationContext(), props: { data: baseData() } });

		const empty = document.querySelector('[data-testid="chronik-empty-state"]');
		expect(empty?.getAttribute('data-variant')).toBe('first-run');
	});

	it('renders the filter-empty empty state when feed has items but filter rules out all', async () => {
		render(AktivitaetenPage, {
			context: notificationContext(),
			props: {
				data: baseData({
					filter: 'fuer-dich' as const,
					activityFeed: [
						{
							kind: 'TEXT_SAVED',
							documentId: 'doc-1',
							documentTitle: 'Brief',
							actor: { id: 'u1', name: 'Anna', initials: 'AS', color: '#000' },
							count: 1,
							happenedAt: '2026-01-01T00:00:00Z',
							happenedAtUntil: null,
							youMentioned: false
						}
					]
				})
			}
		});

		const empty = document.querySelector('[data-testid="chronik-empty-state"]');
		expect(empty?.getAttribute('data-variant')).toBe('filter-empty');
	});

	it('renders the timeline when displayFeed is non-empty', async () => {
		render(AktivitaetenPage, {
			context: notificationContext(),
			props: {
				data: baseData({
					filter: 'alle' as const,
					activityFeed: [
						{
							kind: 'TEXT_SAVED',
							documentId: 'doc-1',
							documentTitle: 'Brief 1899',
							actor: { id: 'u1', name: 'Anna', initials: 'AS', color: '#000' },
							count: 1,
							happenedAt: '2026-01-01T00:00:00Z',
							happenedAtUntil: null,
							youMentioned: false
						}
					]
				})
			}
		});

		// Timeline renders the document title from the feed item
		expect(document.body.textContent).toContain('Brief 1899');
		// No empty-state in this case
		const empty = document.querySelector('[data-testid="chronik-empty-state"]');
		expect(empty).toBeNull();
	});

	it('renders without crashing when filter is set to a non-default value', async () => {
		render(AktivitaetenPage, {
			context: notificationContext(),
			props: { data: baseData({ filter: 'transkription' as const }) }
		});

		const main = document.querySelector('main');
		expect(main).not.toBeNull();
	});
});
