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

vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
vi.mock('$app/state', () => ({ navigating: { to: null } }));

import Page from './+page.svelte';

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

const SEARCH_LABEL = 'Titel, Personen, Tags durchsuchen…';

function makeData(overrides: Record<string, unknown> = {}) {
	return {
		items: [],
		total: 0,
		q: '',
		from: '',
		to: '',
		senderId: '',
		receiverId: '',
		initialSenderName: '',
		initialReceiverName: '',
		tags: [],
		sort: 'DATE',
		dir: 'desc',
		tagQ: '',
		tagOp: 'AND',
		canWrite: false,
		error: null,
		...overrides
	};
}

// ─── Initial state from server data ───────────────────────────────────────────

describe('documents page — initial state', () => {
	it('pre-fills the search input from data.q', async () => {
		render(Page, { data: makeData({ q: 'Geburtstag' }) });
		await expect
			.element(page.getByRole('textbox', { name: SEARCH_LABEL }))
			.toHaveValue('Geburtstag');
	});

	it('leaves the search input empty when data.q is not set', async () => {
		render(Page, { data: makeData() });
		await expect.element(page.getByRole('textbox', { name: SEARCH_LABEL })).toHaveValue('');
	});
});

// ─── URL building via triggerSearch ───────────────────────────────────────────

describe('documents page — URL building', () => {
	beforeEach(() => vi.useFakeTimers());

	it('calls goto with /documents?q=… after the 500 ms debounce', async () => {
		const { goto } = await import('$app/navigation');
		vi.mocked(goto).mockClear();

		render(Page, { data: makeData() });

		const input = page.getByRole('textbox', { name: SEARCH_LABEL });
		await input.fill('Urlaub');

		expect(goto).not.toHaveBeenCalled();

		vi.advanceTimersByTime(500);

		expect(goto).toHaveBeenCalledOnce();
		const [url] = vi.mocked(goto).mock.calls[0];
		expect(url).toContain('q=Urlaub');
		expect(url).toMatch(/^\/documents\?/);
	});

	it('omits q from the URL when the search field is empty', async () => {
		const { goto } = await import('$app/navigation');
		vi.mocked(goto).mockClear();

		render(Page, { data: makeData() });

		const input = page.getByRole('textbox', { name: SEARCH_LABEL });
		await input.fill('');

		vi.advanceTimersByTime(500);

		const [url] = vi.mocked(goto).mock.calls[0] ?? [''];
		expect(url).not.toContain('q=');
	});

	it('second keystroke within 500 ms cancels the first timer — goto called only once', async () => {
		const { goto } = await import('$app/navigation');
		vi.mocked(goto).mockClear();

		render(Page, { data: makeData() });

		const input = page.getByRole('textbox', { name: SEARCH_LABEL });
		await input.fill('U');
		vi.advanceTimersByTime(200);
		await input.fill('Urlaub');
		vi.advanceTimersByTime(500);

		expect(goto).toHaveBeenCalledOnce();
	});

	it('passes keepFocus and noScroll options to goto', async () => {
		const { goto } = await import('$app/navigation');
		vi.mocked(goto).mockClear();

		render(Page, { data: makeData() });
		const input = page.getByRole('textbox', { name: SEARCH_LABEL });
		await input.fill('Brief');
		vi.advanceTimersByTime(500);

		expect(goto).toHaveBeenCalledWith(
			expect.any(String),
			expect.objectContaining({ keepFocus: true, noScroll: true })
		);
	});

	it('filter change does not carry the current page — goto URL drops page param', async () => {
		const { goto } = await import('$app/navigation');
		vi.mocked(goto).mockClear();

		// User is mid-way through results at page 5; change the search text.
		render(Page, { data: makeData({ q: 'old', pageNumber: 5 }) });

		const input = page.getByRole('textbox', { name: SEARCH_LABEL });
		await input.fill('Brief');
		vi.advanceTimersByTime(500);

		const [url] = vi.mocked(goto).mock.calls[0];
		expect(url).toContain('q=Brief');
		expect(url).not.toContain('page=');
	});
});

// ─── Sender / receiver name display ──────────────────────────────────────────

describe('documents page — sender/receiver display', () => {
	it('pre-fills sender typeahead from initialSenderName when senderId filter is active', async () => {
		render(Page, {
			data: makeData({
				senderId: '11111111-1111-1111-1111-111111111111',
				initialSenderName: 'Max Mustermann'
			})
		});
		// Advanced filters are auto-shown when senderId is set
		const inputs = page.getByPlaceholder('Namen tippen...');
		await expect.element(inputs.first()).toHaveValue('Max Mustermann');
	});
});

// ─── Timeline density widget wiring (#385) ────────────────────────────────────

describe('documents page — timeline density widget', () => {
	it('renders the timeline widget when density data is present', async () => {
		render(Page, {
			data: makeData({
				density: [{ month: '1915-08', count: 3 }],
				minDate: '1915-08-01',
				maxDate: '1915-08-31'
			})
		});

		await expect.element(page.getByTestId('timeline-density-filter')).toBeInTheDocument();
	});

	it('hides the timeline widget when density is null (mobile / calendar view)', async () => {
		render(Page, { data: makeData({ density: null, minDate: null, maxDate: null }) });
		expect(document.querySelector('[data-testid="timeline-density-filter"]')).toBeNull();
	});

	it('clicking a timeline bar navigates with from/to set to that month boundary', async () => {
		const { goto } = await import('$app/navigation');
		vi.mocked(goto).mockClear();

		render(Page, {
			data: makeData({
				density: [{ month: '1915-08', count: 3 }],
				minDate: '1915-08-01',
				maxDate: '1915-08-31'
			})
		});

		const bar = document.querySelector('[data-testid="timeline-bar"]') as HTMLButtonElement;
		bar.dispatchEvent(new MouseEvent('click', { bubbles: true }));

		expect(goto).toHaveBeenCalledOnce();
		const [url] = vi.mocked(goto).mock.calls[0];
		expect(url).toContain('from=1915-08-01');
		expect(url).toContain('to=1915-08-31');
	});

	it('the standalone zoom-in button no longer exists (drag replaces it)', async () => {
		render(Page, {
			data: makeData({
				density: [{ month: '1915-08', count: 3 }],
				minDate: '1915-08-01',
				maxDate: '1915-08-31',
				from: '1915-08-01',
				to: '1915-08-31'
			})
		});

		expect(document.querySelector('[data-testid="timeline-zoom-in"]')).toBeNull();
	});

	it('clicking reset-zoom drops zoomFrom/zoomTo from the URL', async () => {
		const { goto } = await import('$app/navigation');
		vi.mocked(goto).mockClear();

		render(Page, {
			data: makeData({
				density: [{ month: '1915-08', count: 3 }],
				minDate: '1915-08-01',
				maxDate: '1915-08-31',
				zoomFrom: '1915-08-01',
				zoomTo: '1915-08-31'
			})
		});

		const resetBtn = document.querySelector(
			'[data-testid="timeline-zoom-reset"]'
		) as HTMLButtonElement;
		resetBtn.dispatchEvent(new MouseEvent('click', { bubbles: true }));

		expect(goto).toHaveBeenCalledOnce();
		const [url] = vi.mocked(goto).mock.calls[0];
		expect(url).not.toContain('zoomFrom=');
		expect(url).not.toContain('zoomTo=');
	});
});
