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

vi.mock('$lib/shared/api.server', () => ({
	createApiClient: vi.fn(),
	extractErrorCode: (e: unknown) => (e as { code?: string } | undefined)?.code
}));

import { createApiClient } from '$lib/shared/api.server';
import { DEFAULT_VIEW, INITIAL_VIEW, MAX_ZOOM } from '$lib/person/genealogy/panZoom';

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

function mockNetwork() {
	vi.mocked(createApiClient).mockReturnValue({
		GET: vi.fn().mockResolvedValue({ response: { ok: true }, data: { nodes: [], edges: [] } })
	} as unknown as ReturnType<typeof createApiClient>);
}

function mockNetworkResponse(status: number) {
	vi.mocked(createApiClient).mockReturnValue({
		GET: vi.fn().mockResolvedValue({ response: { ok: false, status }, error: { code: 'X' } })
	} as unknown as ReturnType<typeof createApiClient>);
}

function loadEvent(query = '') {
	const url = new URL(`http://localhost/stammbaum${query}`);
	return {
		fetch: vi.fn() as unknown as typeof fetch,
		request: new Request(url),
		url
	};
}

describe('/stammbaum +page.server load — initialView', () => {
	it('returns the readable INITIAL_VIEW (z=3) for a fresh visit with no params', async () => {
		mockNetwork();
		const { load } = await import('./+page.server');
		const result = await load(loadEvent() as never);
		expect(result.initialView).toEqual(INITIAL_VIEW);
	});

	it('parses and returns valid ?cx&cy&z params', async () => {
		mockNetwork();
		const { load } = await import('./+page.server');
		const result = await load(loadEvent('?cx=120&cy=-40&z=1.5') as never);
		expect(result.initialView).toEqual({ x: 120, y: -40, z: 1.5 });
	});

	it('degrades a crafted ?z=Infinity to a safe view (Nora #692)', async () => {
		mockNetwork();
		const { load } = await import('./+page.server');
		const result = await load(loadEvent('?z=Infinity&cx=NaN') as never);
		expect(result.initialView).toEqual(DEFAULT_VIEW);
	});

	it('clamps an out-of-range zoom server-side', async () => {
		mockNetwork();
		const { load } = await import('./+page.server');
		const result = await load(loadEvent('?z=99') as never);
		expect(result.initialView.z).toBe(MAX_ZOOM);
	});

	it('redirects to /login when the network API returns 401', async () => {
		mockNetworkResponse(401);
		const { load } = await import('./+page.server');
		await expect(load(loadEvent() as never)).rejects.toMatchObject({
			status: 302,
			location: '/login'
		});
	});

	it('throws an HTTP error when the network API fails', async () => {
		mockNetworkResponse(500);
		const { load } = await import('./+page.server');
		await expect(load(loadEvent() as never)).rejects.toMatchObject({ status: 500 });
	});
});
