import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';
import { createRawSnippet } from 'svelte';

vi.mock('$env/static/public', () => ({ PUBLIC_NOTIFICATION_POLL_MS: '60000' }));

// NotificationBell calls notificationStore.init() on mount, which creates an
// EventSource and immediately fetches the unread count.  In the test environment
// the SSE connection fails (no backend), triggering onerror → another fetch.
// If that fetch returns 401, notifications.svelte.ts calls
// window.location.href = '/login', navigating the test iframe and breaking the
// Playwright connection.  Stubbing fetch to return 200 keeps it in-check.
beforeEach(() => {
	vi.stubGlobal(
		'fetch',
		vi.fn().mockResolvedValue({
			ok: true,
			status: 200,
			json: vi.fn().mockResolvedValue({ count: 0, content: [] })
		})
	);
});

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

const emptySnippet = createRawSnippet(() => ({ render: () => '<span></span>' }));
import Layout from './+layout.svelte';

const tick = () => new Promise((r) => setTimeout(r, 0));

// Minimal data required by the layout
const makeData = (overrides = {}) => ({
	user: {
		id: '1',
		firstName: 'Max',
		lastName: 'Müller',
		email: 'max@example.com',
		groups: [],
		enabled: true,
		createdAt: ''
	},
	canWrite: true,
	canAnnotate: false,
	canBlogWrite: false,
	...overrides
});

// ─── User avatar ──────────────────────────────────────────────────────────────

describe('Layout – user avatar button', () => {
	it('shows user initials when first and last name are set', async () => {
		render(Layout, { data: makeData(), children: emptySnippet });
		await expect.element(page.getByRole('button', { name: /MM/ })).toBeInTheDocument();
	});

	it('shows fallback icon button when names are not set', async () => {
		render(Layout, {
			data: makeData({
				user: { id: '1', email: 'fallback@example.com', groups: [], enabled: true, createdAt: '' }
			}),
			children: emptySnippet
		});
		// Button should still exist (with aria-label for accessibility)
		await expect.element(page.getByRole('button', { name: /Profil/i })).toBeInTheDocument();
	});
});

// ─── Upload link ──────────────────────────────────────────────────────────────

describe('Layout – upload link', () => {
	it('has aria-label for screen reader access', async () => {
		render(Layout, { data: makeData(), children: emptySnippet });
		const link = page.getByRole('link', { name: /Hochladen|Upload|Subir/i });
		await expect.element(link).toHaveAttribute('aria-label');
	});

	it('navigates to /documents/new', async () => {
		render(Layout, { data: makeData(), children: emptySnippet });
		const link = page.getByRole('link', { name: /Hochladen|Upload|Subir/i });
		await expect.element(link).toHaveAttribute('href', '/documents/new');
	});

	it('is hidden for a user without WRITE_ALL', async () => {
		render(Layout, { data: makeData({ canWrite: false }), children: emptySnippet });
		await expect
			.element(page.getByRole('link', { name: /Hochladen|Upload|Subir/i }))
			.not.toBeInTheDocument();
	});

	it('is hidden for an ANNOTATE_ALL-only user (gate is lack of WRITE_ALL, not READ_ALL)', async () => {
		render(Layout, {
			data: makeData({ canWrite: false, canAnnotate: true }),
			children: emptySnippet
		});
		await expect
			.element(page.getByRole('link', { name: /Hochladen|Upload|Subir/i }))
			.not.toBeInTheDocument();
	});
});

// ─── Dropdown ─────────────────────────────────────────────────────────────────

describe('Layout – user dropdown', () => {
	it('dropdown is hidden initially', async () => {
		render(Layout, { data: makeData(), children: emptySnippet });
		await tick();
		await expect.element(page.getByRole('link', { name: /Profil/i })).not.toBeInTheDocument();
	});

	it('opens dropdown on button click', async () => {
		render(Layout, { data: makeData(), children: emptySnippet });
		((await page.getByRole('button', { name: /MM/ }).element()) as HTMLElement).click();
		await expect.element(page.getByRole('link', { name: /Profil/i })).toBeInTheDocument();
	});

	it('profile link points to /profile', async () => {
		render(Layout, { data: makeData(), children: emptySnippet });
		((await page.getByRole('button', { name: /MM/ }).element()) as HTMLElement).click();
		await expect
			.element(page.getByRole('link', { name: /Profil/i }))
			.toHaveAttribute('href', '/profile');
	});

	it('logout button is in the dropdown', async () => {
		render(Layout, { data: makeData(), children: emptySnippet });
		((await page.getByRole('button', { name: /MM/ }).element()) as HTMLElement).click();
		await expect.element(page.getByRole('button', { name: /Abmelden/i })).toBeInTheDocument();
	});

	it('closes dropdown when Escape is pressed', async () => {
		render(Layout, { data: makeData(), children: emptySnippet });
		const btnEl = (await page.getByRole('button', { name: /MM/ }).element()) as HTMLElement;
		btnEl.click();
		await expect.element(page.getByRole('link', { name: /Profil/i })).toBeInTheDocument();
		btnEl.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
		await tick();
		await expect.element(page.getByRole('link', { name: /Profil/i })).not.toBeInTheDocument();
	});
});
