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

vi.mock('$lib/shared/api.server', () => ({ createApiClient: vi.fn() }));

import { load } from './+page.server';
import { createApiClient } from '$lib/shared/api.server';

beforeEach(() => vi.clearAllMocks());

function makeUrl(params: Record<string, string | string[]> = {}) {
	const url = new URL('http://localhost/documents');
	for (const [key, value] of Object.entries(params)) {
		if (Array.isArray(value)) {
			value.forEach((v) => url.searchParams.append(key, v));
		} else {
			url.searchParams.set(key, value);
		}
	}
	return url;
}

// ─── search params forwarding ─────────────────────────────────────────────────

describe('documents page load — search params', () => {
	it('passes q, from, to to the search API', async () => {
		const mockGet = vi.fn().mockResolvedValue({
			response: { ok: true, status: 200 },
			data: { items: [], totalElements: 0, pageNumber: 0, pageSize: 50, totalPages: 0 }
		});
		vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
			typeof createApiClient
		>);

		await load({
			url: makeUrl({ q: 'Urlaub', from: '1920-01-01', to: '1950-12-31' }),
			fetch: vi.fn() as unknown as typeof fetch
		});

		expect(mockGet).toHaveBeenCalledWith(
			'/api/documents/search',
			expect.objectContaining({
				params: expect.objectContaining({
					query: expect.objectContaining({ q: 'Urlaub', from: '1920-01-01', to: '1950-12-31' })
				})
			})
		);
	});

	it('passes senderId and receiverId to the search API', async () => {
		const mockGet = vi.fn().mockResolvedValue({
			response: { ok: true, status: 200 },
			data: { items: [], totalElements: 0, pageNumber: 0, pageSize: 50, totalPages: 0 }
		});
		vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
			typeof createApiClient
		>);

		await load({
			url: makeUrl({ senderId: 'p-1', receiverId: 'p-2' }),
			fetch: vi.fn() as unknown as typeof fetch
		});

		expect(mockGet).toHaveBeenCalledWith(
			'/api/documents/search',
			expect.objectContaining({
				params: expect.objectContaining({
					query: expect.objectContaining({ senderId: 'p-1', receiverId: 'p-2' })
				})
			})
		);
	});

	it('passes sort, dir, tagQ to the search API', async () => {
		const mockGet = vi.fn().mockResolvedValue({
			response: { ok: true, status: 200 },
			data: { items: [], totalElements: 0, pageNumber: 0, pageSize: 50, totalPages: 0 }
		});
		vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
			typeof createApiClient
		>);

		await load({
			url: makeUrl({ sort: 'TITLE', dir: 'asc', tagQ: 'fam' }),
			fetch: vi.fn() as unknown as typeof fetch
		});

		expect(mockGet).toHaveBeenCalledWith(
			'/api/documents/search',
			expect.objectContaining({
				params: expect.objectContaining({
					query: expect.objectContaining({ sort: 'TITLE', dir: 'asc', tagQ: 'fam' })
				})
			})
		);
	});

	it('returns items and total from the search result', async () => {
		const item = {
			document: { id: 'd1' },
			matchData: {},
			completionPercentage: 0,
			contributors: []
		};
		const mockGet = vi.fn().mockResolvedValue({
			response: { ok: true, status: 200 },
			data: { items: [item], totalElements: 42, pageNumber: 0, pageSize: 50, totalPages: 1 }
		});
		vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
			typeof createApiClient
		>);

		const result = await load({
			url: makeUrl({ q: 'test' }),
			fetch: vi.fn() as unknown as typeof fetch
		});

		expect(result.items).toHaveLength(1);
		expect(result.totalElements).toBe(42);
	});

	it('returns filter values in the result for pre-filling the UI', async () => {
		const mockGet = vi.fn().mockResolvedValue({
			response: { ok: true, status: 200 },
			data: { items: [], totalElements: 0, pageNumber: 0, pageSize: 50, totalPages: 0 }
		});
		vi.mocked(createApiClient).mockReturnValue({ GET: mockGet } as ReturnType<
			typeof createApiClient
		>);

		const result = await load({
			url: makeUrl({ q: 'Urlaub', from: '1920-01-01', sort: 'TITLE', dir: 'asc' }),
			fetch: vi.fn() as unknown as typeof fetch
		});

		expect(result.q).toBe('Urlaub');
		expect(result.from).toBe('1920-01-01');
		expect(result.sort).toBe('TITLE');
		expect(result.dir).toBe('asc');
	});
});

// ─── 401 redirect ─────────────────────────────────────────────────────────────

describe('documents page load — auth redirect', () => {
	it('redirects to /login when search API returns 401', async () => {
		vi.mocked(createApiClient).mockReturnValue({
			GET: vi.fn().mockResolvedValue({ response: { ok: false, status: 401 }, data: null })
		} as ReturnType<typeof createApiClient>);

		await expect(
			load({ url: makeUrl(), fetch: vi.fn() as unknown as typeof fetch })
		).rejects.toMatchObject({ location: '/login' });
	});
});

// ─── network error fallback ───────────────────────────────────────────────────

describe('documents page load — network error fallback', () => {
	it('returns error string instead of throwing when API call throws', async () => {
		vi.mocked(createApiClient).mockReturnValue({
			GET: vi.fn().mockRejectedValue(new Error('Network failure'))
		} as ReturnType<typeof createApiClient>);

		const result = await load({ url: makeUrl(), fetch: vi.fn() as unknown as typeof fetch });

		expect(result.error).toBeTruthy();
		expect(result.items).toEqual([]);
	});
});
