import { prisma } from '@documenso/prisma';
import { seedUser } from '@documenso/prisma/seed/users';
import { expect, type Page, test } from '@playwright/test';

import { apiSignin, apiSignout, checkSessionValid } from '../fixtures/authentication';

test.use({ storageState: { cookies: [], origins: [] } });

test('[USER] can reset password via forgot password', async ({ page }: { page: Page }) => {
  const oldPassword = 'Test123!';
  const newPassword = 'Test124!';

  const { user } = await seedUser({
    password: oldPassword,
  });

  await page.goto('http://localhost:3000/signin');
  await page.getByRole('link', { name: 'Forgot your password?' }).click();
  await expect(page).toHaveURL('http://localhost:3000/forgot-password');

  await page.getByRole('textbox', { name: 'Email' }).click();
  await page.getByRole('textbox', { name: 'Email' }).fill(user.email);

  await expect(page.getByRole('button', { name: 'Reset Password' })).toBeEnabled();
  await page.getByRole('button', { name: 'Reset Password' }).click();

  await expect(page.locator('body')).toContainText('Reset email sent', {
    timeout: 10000,
  });

  const foundToken = await prisma.passwordResetToken.findFirstOrThrow({
    where: {
      userId: user.id,
    },
    include: {
      user: true,
    },
  });

  await page.goto(`http://localhost:3000/reset-password/${foundToken.token}`);

  // Assert that password cannot be same as old password.
  await page.getByLabel('Password', { exact: true }).fill(oldPassword);
  await page.getByLabel('Repeat Password').fill(oldPassword);

  // Ensure both fields are filled before clicking
  await expect(page.getByLabel('Password', { exact: true })).toHaveValue(oldPassword);
  await expect(page.getByLabel('Repeat Password')).toHaveValue(oldPassword);

  await page.getByRole('button', { name: 'Reset Password' }).click();
  await expect(page.locator('body')).toContainText('Your new password cannot be the same as your old password.');

  // Assert password reset.
  await page.getByLabel('Password', { exact: true }).fill(newPassword);
  await page.getByLabel('Repeat Password').fill(newPassword);

  // Ensure both fields are filled before clicking
  await expect(page.getByLabel('Password', { exact: true })).toHaveValue(newPassword);
  await expect(page.getByLabel('Repeat Password')).toHaveValue(newPassword);

  await page.getByRole('button', { name: 'Reset Password' }).click();
  await expect(page.locator('body')).toContainText('Your password has been updated successfully.');

  // Assert sign in works.
  await apiSignin({
    page,
    email: user.email,
    password: newPassword,
    redirectPath: '/settings/profile',
  });

  await page.waitForURL('/settings/profile');
  await expect(page).toHaveURL('/settings/profile');
});

test('[USER] can reset password via user settings', async ({ page }: { page: Page }) => {
  const oldPassword = 'Test123!';
  const newPassword = 'Test124!';

  const { user } = await seedUser({
    password: oldPassword,
  });

  await apiSignin({
    page,
    email: user.email,
    password: oldPassword,
    redirectPath: '/settings/security',
  });

  await page.getByLabel('Current password').fill(oldPassword);
  await page.getByLabel('New password').fill(newPassword);
  await page.getByLabel('Repeat password').fill(newPassword);
  await page.getByRole('button', { name: 'Update password' }).click();
  await expect(page.locator('body')).toContainText('Password updated');

  await apiSignout({
    page,
  });

  // Assert sign in works.
  await apiSignin({
    page,
    email: user.email,
    password: newPassword,
    redirectPath: '/settings/profile',
  });

  await page.waitForURL('/settings/profile');
  await expect(page).toHaveURL('/settings/profile');
});

test('[USER] password reset invalidates all sessions', async ({ page }: { page: Page }) => {
  const oldPassword = 'Test123!';
  const newPassword = 'Test124!';

  const { user } = await seedUser({
    password: oldPassword,
  });

  await apiSignin({
    page,
    email: user.email,
    password: oldPassword,
    redirectPath: '/settings/profile',
  });

  expect(await checkSessionValid(page)).toBe(true);

  const initialCookies = await page.context().cookies();

  await page.context().clearCookies();

  await page.goto('http://localhost:3000/signin');
  await page.getByRole('link', { name: 'Forgot your password?' }).click();
  await expect(page).toHaveURL('http://localhost:3000/forgot-password');
  await page.getByRole('textbox', { name: 'Email' }).fill(user.email);
  await page.getByRole('button', { name: 'Reset Password' }).click();
  await expect(page.locator('body')).toContainText('Reset email sent', {
    timeout: 10000,
  });

  const foundToken = await prisma.passwordResetToken.findFirstOrThrow({
    where: { userId: user.id },
  });

  await page.goto(`http://localhost:3000/reset-password/${foundToken.token}`);
  await page.getByLabel('Password', { exact: true }).fill(newPassword);
  await page.getByLabel('Repeat Password').fill(newPassword);
  await page.getByRole('button', { name: 'Reset Password' }).click();
  await expect(page.locator('body')).toContainText('Your password has been updated successfully.');

  await page.context().addCookies(initialCookies);

  await page.goto('http://localhost:3000/settings/profile');
  await expect(page).toHaveURL('http://localhost:3000/signin');

  expect(await checkSessionValid(page)).toBe(false);

  await apiSignin({
    page,
    email: user.email,
    password: newPassword,
    redirectPath: '/settings/profile',
  });

  await page.waitForURL('/settings/profile');
  expect(await checkSessionValid(page)).toBe(true);
});

test('[USER] password update invalidates other sessions but keeps current', async ({ page }: { page: Page }) => {
  const oldPassword = 'Test123!';
  const newPassword = 'Test124!';

  const { user } = await seedUser({
    password: oldPassword,
  });

  await apiSignin({
    page,
    email: user.email,
    password: oldPassword,
    redirectPath: '/settings/profile',
  });

  expect(await checkSessionValid(page)).toBe(true);

  const initialCookies = await page.context().cookies();

  await page.context().clearCookies();
  await apiSignin({
    page,
    email: user.email,
    password: oldPassword,
    redirectPath: '/settings/profile',
  });

  expect(await checkSessionValid(page)).toBe(true);

  await page.goto('http://localhost:3000/settings/security');
  await page.getByLabel('Current password').fill(oldPassword);
  await page.getByLabel('New password').fill(newPassword);
  await page.getByLabel('Repeat password').fill(newPassword);
  await page.getByRole('button', { name: 'Update password' }).click();
  await expect(page.locator('body')).toContainText('Password updated');

  const finalCookies = await page.context().cookies();

  await page.context().clearCookies();
  await page.context().addCookies(initialCookies);
  await page.goto('http://localhost:3000/settings/profile');
  await expect(page).toHaveURL('http://localhost:3000/signin');
  expect(await checkSessionValid(page)).toBe(false);

  await page.context().clearCookies();
  await page.context().addCookies(finalCookies);
  await page.goto('http://localhost:3000/settings/security');
  await expect(page).toHaveURL('http://localhost:3000/settings/security');
  expect(await checkSessionValid(page)).toBe(true);
});
