import { expect, test } from '@playwright/test';
import { PLAYWRIGHT_BASE_URL } from '../playwright/config';
import { getPasswordResetUrl } from './utils/mailpit';

async function registerNewUser(page, email, password) {
    await page.goto(PLAYWRIGHT_BASE_URL + '/register');
    await page.getByLabel('Name').fill('John Doe');
    await page.getByLabel('Email').fill(email);
    await page.getByLabel('Password', { exact: true }).fill(password);
    await page.getByLabel('Confirm Password').fill(password);
    await page.getByLabel('I agree to the Terms of').click();
    await page.getByRole('button', { name: 'Register' }).click();
    await expect(page.getByTestId('dashboard_view')).toBeVisible();
}

test('can register, logout and log back in', async ({ page }) => {
    await page.goto(PLAYWRIGHT_BASE_URL);
    const email = `john+${Math.round(Math.random() * 10000)}@doe.com`;
    const password = 'suchagreatpassword123';
    await registerNewUser(page, email, password);
    await expect(page.getByTestId('dashboard_view')).toBeVisible();
    await page.getByTestId('current_user_button').click();
    await page.getByText('Log Out').click();
    await page.waitForURL(PLAYWRIGHT_BASE_URL + '/login');
    await page.goto(PLAYWRIGHT_BASE_URL + '/login');
    await page.getByLabel('Email').fill(email);
    await page.getByLabel('Password').fill(password);
    await page.getByRole('button', { name: 'Log in' }).click();
    await expect(page.getByTestId('dashboard_view')).toBeVisible();
});

test('can register and delete account', async ({ page }) => {
    await page.goto(PLAYWRIGHT_BASE_URL);
    const email = `john+${Math.round(Math.random() * 10000)}@doe.com`;
    const password = 'suchagreatpassword123';
    await registerNewUser(page, email, password);
    await page.goto(PLAYWRIGHT_BASE_URL + '/user/profile');
    await page.getByRole('button', { name: 'Delete Account' }).click();
    await expect(page.getByRole('dialog')).toBeVisible();
    await page.getByPlaceholder('Password').fill(password);
    await page.getByRole('dialog').getByRole('button', { name: 'Delete Account' }).click();
    await page.waitForURL(PLAYWRIGHT_BASE_URL + '/login');
    await page.goto(PLAYWRIGHT_BASE_URL + '/login');
    await page.getByLabel('Email').fill(email);
    await page.getByLabel('Password').fill(password);
    await page.getByRole('button', { name: 'Log in' }).click();
    await expect(page.getByRole('alert')).toContainText(
        'These credentials do not match our records.'
    );
});

test('shows error for invalid email on forgot password', async ({ page }) => {
    await page.goto(PLAYWRIGHT_BASE_URL + '/forgot-password');

    // Request password reset with non-existent email
    await page.getByLabel('Email').fill('nonexistent@example.com');
    await page.getByRole('button', { name: 'Email Password Reset Link' }).click();

    // Should show error message
    await expect(page.getByText("We can't find a user with that email address.")).toBeVisible();
});

test('shows browser validation for invalid email format on forgot password', async ({ page }) => {
    await page.goto(PLAYWRIGHT_BASE_URL + '/forgot-password');

    // Request password reset with invalid email format
    const emailInput = page.getByLabel('Email');
    await emailInput.fill('notanemail');

    // Check for browser validation - the input should be invalid
    const isInvalid = await emailInput.evaluate((el: HTMLInputElement) => !el.validity.valid);
    expect(isInvalid).toBe(true);
});

test('shows browser validation for empty email on forgot password', async ({ page }) => {
    await page.goto(PLAYWRIGHT_BASE_URL + '/forgot-password');

    // The email input is required, so it should be invalid when empty
    const emailInput = page.getByLabel('Email');

    // Check for browser validation - the input should be invalid because it's required and empty
    const isInvalid = await emailInput.evaluate((el: HTMLInputElement) => el.validity.valueMissing);
    expect(isInvalid).toBe(true);
});

test('can reset password via email link', async ({ page, request }) => {
    // First register a new user
    const email = `john+${Math.round(Math.random() * 10000)}@doe.com`;
    const originalPassword = 'suchagreatpassword123';
    const newPassword = 'mynewsecurepassword456';
    await registerNewUser(page, email, originalPassword);

    // Log out
    await page.getByTestId('current_user_button').click();
    await page.getByText('Log Out').click();
    await page.waitForURL(PLAYWRIGHT_BASE_URL + '/login');

    // Request password reset
    await page.goto(PLAYWRIGHT_BASE_URL + '/forgot-password');
    await page.getByLabel('Email').fill(email);
    await page.getByRole('button', { name: 'Email Password Reset Link' }).click();
    await expect(page.getByText('We have emailed your password reset link.')).toBeVisible();

    // Get password reset URL from email
    const resetUrl = await getPasswordResetUrl(request, email);

    // Navigate to reset page
    await page.goto(resetUrl);

    // Fill in new password
    await page.getByLabel('Password', { exact: true }).fill(newPassword);
    await page.getByLabel('Confirm Password').fill(newPassword);
    await page.getByRole('button', { name: 'Reset Password' }).click();

    // Should redirect to login page after successful reset
    await page.waitForURL(PLAYWRIGHT_BASE_URL + '/login');

    // Try logging in with new password
    await page.getByLabel('Email').fill(email);
    await page.getByLabel('Password').fill(newPassword);
    await page.getByRole('button', { name: 'Log in' }).click();
    await expect(page.getByTestId('dashboard_view')).toBeVisible();
});

test('shows validation error for password mismatch on reset', async ({ page, request }) => {
    // First register a new user
    const email = `john+${Math.round(Math.random() * 10000)}@doe.com`;
    const originalPassword = 'suchagreatpassword123';
    await registerNewUser(page, email, originalPassword);

    // Log out
    await page.getByTestId('current_user_button').click();
    await page.getByText('Log Out').click();
    await page.waitForURL(PLAYWRIGHT_BASE_URL + '/login');

    // Request password reset
    await page.goto(PLAYWRIGHT_BASE_URL + '/forgot-password');
    await page.getByLabel('Email').fill(email);
    await page.getByRole('button', { name: 'Email Password Reset Link' }).click();
    await expect(page.getByText('We have emailed your password reset link.')).toBeVisible();

    // Get password reset URL from email
    const resetUrl = await getPasswordResetUrl(request, email);

    // Navigate to reset page
    await page.goto(resetUrl);

    // Fill in mismatched passwords
    await page.getByLabel('Password', { exact: true }).fill('newpassword123');
    await page.getByLabel('Confirm Password').fill('differentpassword456');
    await page.getByRole('button', { name: 'Reset Password' }).click();

    // Should show validation error
    await expect(page.getByText('The password field confirmation does not match.')).toBeVisible();
});

test('shows validation error for short password on reset', async ({ page, request }) => {
    // First register a new user
    const email = `john+${Math.round(Math.random() * 10000)}@doe.com`;
    const originalPassword = 'suchagreatpassword123';
    await registerNewUser(page, email, originalPassword);

    // Log out
    await page.getByTestId('current_user_button').click();
    await page.getByText('Log Out').click();
    await page.waitForURL(PLAYWRIGHT_BASE_URL + '/login');

    // Request password reset
    await page.goto(PLAYWRIGHT_BASE_URL + '/forgot-password');
    await page.getByLabel('Email').fill(email);
    await page.getByRole('button', { name: 'Email Password Reset Link' }).click();
    await expect(page.getByText('We have emailed your password reset link.')).toBeVisible();

    // Get password reset URL from email
    const resetUrl = await getPasswordResetUrl(request, email);

    // Navigate to reset page
    await page.goto(resetUrl);

    // Fill in short password
    await page.getByLabel('Password', { exact: true }).fill('short');
    await page.getByLabel('Confirm Password').fill('short');
    await page.getByRole('button', { name: 'Reset Password' }).click();

    // Should show validation error about minimum length
    await expect(page.getByText('must be at least')).toBeVisible();
});

test('shows error for invalid login credentials', async ({ page }) => {
    await page.goto(PLAYWRIGHT_BASE_URL + '/login');
    await page.getByLabel('Email').fill('nonexistent@example.com');
    await page.getByLabel('Password').fill('wrongpassword123');
    await page.getByRole('button', { name: 'Log in' }).click();

    await expect(page.getByText('These credentials do not match our records.')).toBeVisible();
});

test('shows error when registering with existing email', async ({ page }) => {
    const email = `john+${Math.round(Math.random() * 10000)}@doe.com`;
    const password = 'suchagreatpassword123';

    // Register first user
    await registerNewUser(page, email, password);

    // Log out
    await page.getByTestId('current_user_button').click();
    await page.getByText('Log Out').click();
    await page.waitForURL(PLAYWRIGHT_BASE_URL + '/login');

    // Try to register with the same email
    await page.goto(PLAYWRIGHT_BASE_URL + '/register');
    await page.getByLabel('Name').fill('Another User');
    await page.getByLabel('Email').fill(email);
    await page.getByLabel('Password', { exact: true }).fill(password);
    await page.getByLabel('Confirm Password').fill(password);
    await page.getByLabel('I agree to the Terms of').click();
    await page.getByRole('button', { name: 'Register' }).click();

    // Should show error about email already taken
    await expect(page.getByText('The resource already exists.')).toBeVisible();
});

test('shows validation error for weak password on registration', async ({ page }) => {
    await page.goto(PLAYWRIGHT_BASE_URL + '/register');
    await page.getByLabel('Name').fill('Weak Password User');
    await page.getByLabel('Email').fill(`weak+${Math.round(Math.random() * 10000)}@test.com`);
    await page.getByLabel('Password', { exact: true }).fill('short');
    await page.getByLabel('Confirm Password').fill('short');
    await page.getByLabel('I agree to the Terms of').click();
    await page.getByRole('button', { name: 'Register' }).click();

    await expect(page.getByText('must be at least')).toBeVisible();
});
