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

vi.mock('$env/dynamic/private', () => ({
	env: { API_INTERNAL_URL: 'http://backend:8080' }
}));

import { actions, load } from './+page.server';

type ActionsRecord = Record<string, (e: never) => unknown>;

function makeRequest(form: Record<string, string>): Request {
	const fd = new FormData();
	for (const [k, v] of Object.entries(form)) fd.set(k, v);
	return new Request('http://localhost/login?/login', { method: 'POST', body: fd });
}

function makeCookies() {
	return {
		set: vi.fn(),
		delete: vi.fn(),
		get: vi.fn()
	};
}

function loadEvent(search: string) {
	return {
		url: new URL(`http://localhost/login${search}`),
		request: new Request('http://localhost/login', { method: 'GET' }),
		route: { id: '/login' }
	} as never;
}

describe('login load', () => {
	it('exposes registered=true when ?registered=1 is present', async () => {
		const result = await load(loadEvent('?registered=1'));
		expect(result).toEqual({ registered: true, reason: null });
	});

	it('exposes reason=expired when ?reason=expired is present', async () => {
		const result = await load(loadEvent('?reason=expired'));
		expect(result).toEqual({ registered: false, reason: 'expired' });
	});
});

describe('login action', () => {
	beforeEach(() => vi.restoreAllMocks());

	it('returns 400 when email is missing', async () => {
		const result = await (actions as ActionsRecord).login({
			request: makeRequest({ password: 'pw' }),
			cookies: makeCookies(),
			fetch: vi.fn(),
			url: new URL('http://localhost/login')
		} as never);
		expect((result as { status: number }).status).toBe(400);
	});

	it('returns 401 with INVALID_CREDENTIALS when the backend rejects credentials', async () => {
		const mockFetch = vi.fn().mockResolvedValue(
			new Response(JSON.stringify({ code: 'INVALID_CREDENTIALS' }), {
				status: 401,
				headers: { 'Content-Type': 'application/json' }
			})
		);

		const result = await (actions as ActionsRecord).login({
			request: makeRequest({ email: 'a@b.de', password: 'wrong' }),
			cookies: makeCookies(),
			fetch: mockFetch,
			url: new URL('http://localhost/login')
		} as never);

		expect((result as { status: number }).status).toBe(401);
	});

	it('re-emits fa_session and deletes legacy auth_token on success', async () => {
		const mockFetch = vi.fn().mockResolvedValue(
			new Response('{}', {
				status: 200,
				headers: { 'Set-Cookie': 'fa_session=opaque-id; Path=/; HttpOnly; SameSite=Strict' }
			})
		);
		const cookies = makeCookies();

		// redirect() throws a Redirect instance — assert via rejects.
		const redirected = (actions as ActionsRecord).login({
			request: makeRequest({ email: 'a@b.de', password: 'pw' }),
			cookies,
			fetch: mockFetch,
			url: new URL('http://localhost/login')
		} as never);

		await expect(redirected).rejects.toMatchObject({ status: 303, location: '/' });

		expect(cookies.set).toHaveBeenCalledWith(
			'fa_session',
			'opaque-id',
			expect.objectContaining({ httpOnly: true, sameSite: 'strict', maxAge: 60 * 60 * 8 })
		);
		expect(cookies.delete).toHaveBeenCalledWith('auth_token', { path: '/' });
	});

	it('returns 429 with rateLimited=true when the backend rate-limits the request', async () => {
		const mockFetch = vi.fn().mockResolvedValue(
			new Response(JSON.stringify({ code: 'TOO_MANY_LOGIN_ATTEMPTS' }), {
				status: 429,
				headers: { 'Content-Type': 'application/json' }
			})
		);

		const result = await (actions as ActionsRecord).login({
			request: makeRequest({ email: 'a@b.de', password: 'pw' }),
			cookies: makeCookies(),
			fetch: mockFetch,
			url: new URL('http://localhost/login')
		} as never);

		expect((result as { status: number }).status).toBe(429);
		expect((result as { data: { rateLimited: boolean } }).data.rateLimited).toBe(true);
	});

	it('returns 500 when backend response omits fa_session cookie', async () => {
		const mockFetch = vi.fn().mockResolvedValue(new Response('{}', { status: 200 }));
		const cookies = makeCookies();

		const result = await (actions as ActionsRecord).login({
			request: makeRequest({ email: 'a@b.de', password: 'pw' }),
			cookies,
			fetch: mockFetch,
			url: new URL('http://localhost/login')
		} as never);

		expect((result as { status: number }).status).toBe(500);
		expect(cookies.set).not.toHaveBeenCalled();
	});
});
