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

afterEach(cleanup);

const baseData = (overrides: Record<string, unknown> = {}) => ({
	code: 'invite-token',
	prefill: null as { firstName?: string; lastName?: string; email?: string } | null,
	codeError: null as string | null,
	...overrides
});

describe('register page', () => {
	it('renders the hero headline when there is no codeError', async () => {
		render(RegisterPage, { props: { data: baseData(), form: undefined } });

		await expect
			.element(page.getByRole('heading', { name: /schön, dass du da bist/i }))
			.toBeVisible();
	});

	it('renders the NO_INVITE_CODE error card with invite-only heading', async () => {
		render(RegisterPage, {
			props: { data: baseData({ codeError: 'NO_INVITE_CODE' }), form: undefined }
		});

		await expect.element(page.getByRole('heading', { name: /nur auf einladung/i })).toBeVisible();
	});

	it('renders the invalid-code error card for any other codeError value', async () => {
		render(RegisterPage, {
			props: { data: baseData({ codeError: 'EXPIRED' }), form: undefined }
		});

		await expect
			.element(page.getByRole('heading', { name: /ungültiger einladungslink/i }))
			.toBeVisible();
	});

	it('hides the form when there is a codeError', async () => {
		render(RegisterPage, {
			props: { data: baseData({ codeError: 'NO_INVITE_CODE' }), form: undefined }
		});

		await expect.element(page.getByLabelText(/^vorname$/i)).not.toBeInTheDocument();
	});

	it('shows the back-to-login link in the codeError card', async () => {
		render(RegisterPage, {
			props: { data: baseData({ codeError: 'NO_INVITE_CODE' }), form: undefined }
		});

		await expect
			.element(page.getByRole('link', { name: /zurück zum login/i }))
			.toHaveAttribute('href', '/login');
	});

	it('renders the form sections when there is no codeError', async () => {
		render(RegisterPage, { props: { data: baseData(), form: undefined } });

		await expect.element(page.getByText('Über dich')).toBeVisible();
		await expect.element(page.getByLabelText(/vorname/i)).toBeVisible();
		await expect.element(page.getByLabelText(/^e-mail-adresse$/i)).toBeVisible();
	});

	it('hydrates the form fields from prefill', async () => {
		render(RegisterPage, {
			props: {
				data: baseData({
					prefill: { firstName: 'Anna', lastName: 'Schmidt', email: 'anna@example.com' }
				}),
				form: undefined
			}
		});

		const firstName = (await page.getByLabelText(/vorname/i).element()) as HTMLInputElement;
		const lastName = (await page.getByLabelText(/nachname/i).element()) as HTMLInputElement;
		const email = (await page.getByLabelText(/^e-mail-adresse$/i).element()) as HTMLInputElement;
		expect(firstName.value).toBe('Anna');
		expect(lastName.value).toBe('Schmidt');
		expect(email.value).toBe('anna@example.com');
	});

	it('shows the prefill hint when an email is prefilled', async () => {
		render(RegisterPage, {
			props: {
				data: baseData({ prefill: { email: 'anna@example.com' } }),
				form: undefined
			}
		});

		await expect
			.element(page.getByText('Von deiner Einladung übernommen – du kannst es ändern'))
			.toBeVisible();
	});

	it('omits the prefill hint when no email is prefilled', async () => {
		render(RegisterPage, { props: { data: baseData(), form: undefined } });

		await expect
			.element(page.getByText('Von deiner Einladung übernommen – du kannst es ändern'))
			.not.toBeInTheDocument();
	});

	it('puts the invite code in a hidden input', async () => {
		render(RegisterPage, { props: { data: baseData({ code: 'tok-99' }), form: undefined } });

		const codeInput = document.querySelector('input[name="code"]') as HTMLInputElement;
		expect(codeInput.type).toBe('hidden');
		expect(codeInput.value).toBe('tok-99');
	});

	it('renders an empty hidden code input when data.code is null', async () => {
		render(RegisterPage, { props: { data: baseData({ code: null }), form: undefined } });

		const codeInput = document.querySelector('input[name="code"]') as HTMLInputElement;
		expect(codeInput.value).toBe('');
	});

	it('toggles password visibility when the show/hide button is clicked', async () => {
		render(RegisterPage, { props: { data: baseData(), form: undefined } });

		const pw = document.querySelector('input[name="password"]') as HTMLInputElement;
		expect(pw.type).toBe('password');

		const toggle = pw.parentElement?.querySelector('button[type="button"]') as HTMLButtonElement;
		toggle.click();
		await vi.waitFor(() => expect(pw.type).toBe('text'));

		toggle.click();
		await vi.waitFor(() => expect(pw.type).toBe('password'));
	});

	it('toggles passwordConfirm visibility independently from password', async () => {
		render(RegisterPage, { props: { data: baseData(), form: undefined } });

		const confirm = document.querySelector('#passwordConfirm') as HTMLInputElement;
		expect(confirm.type).toBe('password');

		const toggle = confirm.parentElement?.querySelector(
			'button[type="button"]'
		) as HTMLButtonElement;
		toggle.click();

		await vi.waitFor(() => expect(confirm.type).toBe('text'));
	});

	it('shows the password length hint after typing a short password', async () => {
		render(RegisterPage, { props: { data: baseData(), form: undefined } });

		const pw = document.querySelector('input[name="password"]') as HTMLInputElement;
		pw.value = 'abc';
		pw.dispatchEvent(new Event('input', { bubbles: true }));

		await expect.element(page.getByText(/mindestens 8 zeichen/i)).toBeVisible();
	});

	it('shows the password OK hint when password is at least 8 chars', async () => {
		render(RegisterPage, { props: { data: baseData(), form: undefined } });

		const pw = document.querySelector('input[name="password"]') as HTMLInputElement;
		pw.value = 'longenoughpw';
		pw.dispatchEvent(new Event('input', { bubbles: true }));

		await vi.waitFor(() => {
			expect(document.querySelectorAll('.text-green-700').length).toBeGreaterThan(0);
		});
	});

	it('shows the password mismatch error when confirm differs from password', async () => {
		render(RegisterPage, { props: { data: baseData(), form: undefined } });

		const pw = document.querySelector('input[name="password"]') as HTMLInputElement;
		const confirm = document.querySelector('#passwordConfirm') as HTMLInputElement;
		pw.value = 'longenoughpw';
		pw.dispatchEvent(new Event('input', { bubbles: true }));
		confirm.value = 'differentpw';
		confirm.dispatchEvent(new Event('input', { bubbles: true }));

		await vi.waitFor(() => {
			expect(document.querySelectorAll('.text-red-600').length).toBeGreaterThan(0);
		});
	});

	it('shows the password match success when confirm equals password', async () => {
		render(RegisterPage, { props: { data: baseData(), form: undefined } });

		const pw = document.querySelector('input[name="password"]') as HTMLInputElement;
		const confirm = document.querySelector('#passwordConfirm') as HTMLInputElement;
		pw.value = 'longenoughpw';
		pw.dispatchEvent(new Event('input', { bubbles: true }));
		confirm.value = 'longenoughpw';
		confirm.dispatchEvent(new Event('input', { bubbles: true }));

		await vi.waitFor(() => {
			// Both "pw OK" and "match OK" hints render with text-green-700.
			expect(document.querySelectorAll('.text-green-700').length).toBeGreaterThanOrEqual(2);
		});
	});

	it('renders the form error from the form prop', async () => {
		render(RegisterPage, {
			props: { data: baseData(), form: { error: 'EMAIL_TAKEN' } }
		});

		const errorBox = document.querySelector('.text-red-600.font-medium');
		expect(errorBox).not.toBeNull();
	});

	it('toggles the notifyOnMention checkbox', async () => {
		render(RegisterPage, { props: { data: baseData(), form: undefined } });

		const cb = document.querySelector('input[name="notifyOnMention"]') as HTMLInputElement;
		expect(cb.checked).toBe(true);

		cb.click();
		await vi.waitFor(() => expect(cb.checked).toBe(false));
	});
});
