import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { prisma } from '@documenso/prisma';
import { seedBlankDocument } from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
import { expect, type Page, test } from '@playwright/test';
import type { Document, Team } from '@prisma/client';

import { apiSignin } from '../fixtures/authentication';
import { signSignaturePad } from '../fixtures/signature';

/**
 * Test helper to complete the document creation flow with duplicate recipients
 */
const completeDocumentFlowWithDuplicateRecipients = async (options: { page: Page; team: Team; document: Document }) => {
  const { page, team, document } = options;

  // Step 1: Settings - Continue with defaults
  await page.getByRole('button', { name: 'Continue' }).click();
  await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();

  // Step 2: Add duplicate recipients
  await page.getByPlaceholder('Email').fill('duplicate@example.com');
  await page.getByPlaceholder('Name').fill('Duplicate Recipient 1');

  // Add second signer with same email
  await page.getByRole('button', { name: 'Add Signer' }).click();
  await page.getByLabel('Email').nth(1).fill('duplicate@example.com');
  await page.getByLabel('Name').nth(1).fill('Duplicate Recipient 2');

  // Add third signer with different email for comparison
  await page.getByRole('button', { name: 'Add Signer' }).click();
  await page.getByLabel('Email').nth(2).fill('unique@example.com');
  await page.getByLabel('Name').nth(2).fill('Unique Recipient');

  // Continue to fields
  await page.getByRole('button', { name: 'Continue' }).click();
  await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();

  // Step 3: Add fields for each recipient
  // Add signature field for first duplicate recipient
  await page.getByRole('button', { name: 'Signature' }).click();
  await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 100, y: 100 } });

  await page.getByText('Duplicate Recipient 1 (duplicate@example.com)').click();

  // Switch to second duplicate recipient and add their field
  await page.getByText('Duplicate Recipient 2 (duplicate@example.com)').click();
  await page.getByRole('button', { name: 'Signature' }).click();
  await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 200, y: 100 } });

  await page.getByText('Duplicate Recipient 2 (duplicate@example.com)').click();

  // Switch to unique recipient and add their field
  await page.getByText('Unique Recipient (unique@example.com)').click();
  await page.getByRole('button', { name: 'Signature' }).click();
  await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 300, y: 100 } });

  // Continue to subject
  await page.getByRole('button', { name: 'Continue' }).click();
  await expect(page.getByRole('heading', { name: 'Distribute Document' })).toBeVisible();

  // Step 4: Complete with subject and send
  await page.waitForTimeout(2500);
  await page.getByRole('button', { name: 'Send' }).click();

  // Wait for send confirmation
  await page.waitForURL(`/t/${team.url}/documents`);

  await expect(page.getByRole('link', { name: document.title })).toBeVisible();
};

test.describe('[DOCUMENT_FLOW]: Duplicate Recipients', () => {
  test('should allow creating document with duplicate recipient emails', async ({ page }) => {
    const { user, team } = await seedUser();

    const document = await seedBlankDocument(user, team.id);

    await apiSignin({
      page,
      email: user.email,
      redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
    });

    // Complete the flow
    await completeDocumentFlowWithDuplicateRecipients({
      page,
      team,
      document,
    });

    // Verify document was created successfully
    await expect(page).toHaveURL(new RegExp(`/t/${team.url}/documents`));
  });

  test('should allow adding duplicate recipient after saving document initially', async ({ page }) => {
    const { user, team } = await seedUser();
    const document = await seedBlankDocument(user, team.id);

    await apiSignin({
      page,
      email: user.email,
      redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
    });

    // Step 1: Settings - Continue with defaults
    await page.getByRole('button', { name: 'Continue' }).click();
    await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();

    // Step 2: Add initial recipient
    await page.getByPlaceholder('Email').fill('test@example.com');
    await page.getByPlaceholder('Name').fill('Test Recipient');

    // Continue to fields and add a field
    await page.getByRole('button', { name: 'Continue' }).click();
    await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();

    await page.getByRole('button', { name: 'Signature' }).click();
    await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 100, y: 100 } });

    // Save the document by going to subject
    await page.getByRole('button', { name: 'Continue' }).click();
    await expect(page.getByRole('heading', { name: 'Distribute Document' })).toBeVisible();

    // Navigate back to signers to add duplicate
    await page.getByRole('button', { name: 'Go Back' }).click();
    await page.getByRole('button', { name: 'Go Back' }).click();
    await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();

    // Add duplicate recipient
    await page.getByRole('button', { name: 'Add Signer' }).click();
    await page.getByLabel('Email').nth(1).fill('test@example.com');
    await page.getByLabel('Name').nth(1).fill('Test Recipient Duplicate');

    // Continue and add field for duplicate
    await page.getByRole('button', { name: 'Continue' }).click();
    await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();

    await page.waitForTimeout(1000);

    // Switch to duplicate recipient and add field
    await page.getByRole('combobox').first().click();
    await page.getByText('Test Recipient Duplicate (test@example.com)').first().click();

    await page.getByRole('button', { name: 'Signature' }).click();
    await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 200, y: 100 } });

    // Complete the flow
    await page.getByRole('button', { name: 'Continue' }).click();
    await expect(page.getByRole('heading', { name: 'Distribute Document' })).toBeVisible();
    await page.waitForTimeout(2500);
    await page.getByRole('button', { name: 'Send' }).click();

    await page.waitForURL(new RegExp(`/t/${team.url}/documents/envelope_.*`));

    await expect(page.getByRole('link', { name: document.title })).toBeVisible();
  });

  test('should isolate fields per recipient token even with duplicate emails', async ({ page, context }) => {
    const { user, team } = await seedUser();

    const document = await seedBlankDocument(user, team.id);

    await apiSignin({
      page,
      email: user.email,
      redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
    });

    // Complete the document flow
    await completeDocumentFlowWithDuplicateRecipients({
      page,
      team,
      document,
    });

    // Navigate to documents list and get the document
    await expect(page).toHaveURL(new RegExp(`/t/${team.url}/documents`));

    const recipients = await prisma.recipient.findMany({
      where: {
        envelopeId: document.id,
      },
    });

    expect(recipients).toHaveLength(3);

    const tokens = recipients.map((r) => r.token);

    expect(new Set(tokens).size).toBe(3); // All tokens should be unique

    // Test each signing experience in separate browser contexts
    for (const recipient of recipients) {
      // Navigate to signing URL
      await page.goto(`/sign/${recipient.token}`, {
        waitUntil: 'networkidle',
      });

      await page.waitForSelector(PDF_VIEWER_PAGE_SELECTOR);

      // Verify only one signature field is visible for this recipient
      expect(await page.locator(`[data-field-type="SIGNATURE"]:not([data-readonly="true"])`).all()).toHaveLength(1);

      // Verify recipient name is correct
      await expect(page.getByLabel('Full Name')).toHaveValue(recipient.name);

      // Sign the document
      await signSignaturePad(page);

      await page.locator('[data-field-type="SIGNATURE"]:not([data-readonly="true"])').first().click();

      await page.getByRole('button', { name: 'Complete' }).click();
      await page.getByRole('button', { name: 'Sign' }).click();

      // Verify completion
      await page.waitForURL(`/sign/${recipient?.token}/complete`);
      await expect(page.getByText('Document Signed')).toBeVisible();
    }
  });

  test('should handle duplicate recipient workflow with different field types', async ({ page }) => {
    const { user, team } = await seedUser();
    const document = await seedBlankDocument(user, team.id);

    await apiSignin({
      page,
      email: user.email,
      redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
    });

    // Step 1: Settings
    await page.getByRole('button', { name: 'Continue' }).click();

    // Step 2: Add duplicate recipients with different roles
    await page.getByPlaceholder('Email').fill('signer@example.com');
    await page.getByPlaceholder('Name').fill('Signer Role');

    await page.getByRole('button', { name: 'Add Signer' }).click();
    await page.getByLabel('Email').nth(1).fill('signer@example.com');
    await page.getByLabel('Name').nth(1).fill('Approver Role');

    // Change second recipient role if role selector is available
    const roleDropdown = page.getByLabel('Role').nth(1);
    let secondRecipientIsApprover = false;

    if (await roleDropdown.isVisible()) {
      await roleDropdown.click();
      await page.getByText('Approver').click();
      secondRecipientIsApprover = true;
    }

    // Step 3: Add different field types for each duplicate
    await page.getByRole('button', { name: 'Continue' }).click();

    // Add signature for first recipient
    await page.getByRole('button', { name: 'Signature' }).click();
    await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 100, y: 100 } });

    // Add name field for second recipient
    await page.getByRole('combobox').first().click();

    await page.getByText('Approver Role (signer@example.com)').first().click();
    await page.getByRole('button', { name: 'Name' }).click();
    await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 200, y: 100 } });

    // Add date field for second recipient
    await page.getByRole('button', { name: 'Date' }).click();
    await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 200, y: 150 } });

    // If second recipient is still a SIGNER (role change wasn't available),
    // add a signature field for them to pass validation
    if (!secondRecipientIsApprover) {
      await page.getByRole('button', { name: 'Signature' }).click();
      await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 200, y: 200 } });
    }

    // Complete the document
    await page.getByRole('button', { name: 'Continue' }).click();

    await page.getByRole('button', { name: 'Send' }).click();

    await page.waitForURL(new RegExp(`/t/${team.url}/documents/envelope_.*`));

    await expect(page.getByRole('link', { name: document.title })).toBeVisible();
  });

  test('should preserve field assignments when editing document with duplicates', async ({ page }) => {
    const { user, team } = await seedUser();
    const document = await seedBlankDocument(user, team.id);

    await apiSignin({
      page,
      email: user.email,
      redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
    });

    // Create document with duplicates and fields
    await completeDocumentFlowWithDuplicateRecipients({
      page,
      team,
      document,
    });

    // Navigate back to edit the document
    await page.goto(`/t/${team.url}/documents/${document.id}/edit`);

    // Go to fields step
    await page.getByRole('button', { name: 'Continue' }).click(); // Settings
    await page.getByRole('button', { name: 'Continue' }).click(); // Signers

    // Verify fields are assigned to correct recipients
    await expect(page.getByRole('heading', { name: 'Add Fields' })).toBeVisible();

    // Click on first duplicate recipient
    await page.getByText('Duplicate Recipient 1 (duplicate@example.com)').click();

    // Verify their field is visible and can be selected
    const firstRecipientFields = await page.locator(`[data-field-type="SIGNATURE"]:not(:disabled)`).all();
    expect(firstRecipientFields.length).toBeGreaterThan(0);

    // Switch to second duplicate recipient
    await page.getByText('Duplicate Recipient 2 (duplicate@example.com)').click();

    // Verify they have their own field
    const secondRecipientFields = await page.locator(`[data-field-type="SIGNATURE"]:not(:disabled)`).all();
    expect(secondRecipientFields.length).toBeGreaterThan(0);

    // Add another field to the second duplicate
    await page.getByRole('button', { name: 'Name' }).click();
    await page.locator(PDF_VIEWER_PAGE_SELECTOR).click({ position: { x: 250, y: 150 } });

    // Save changes
    await page.getByRole('button', { name: 'Continue' }).click();
    await expect(page.getByRole('heading', { name: 'Distribute Document' })).toBeVisible();
    await page.waitForTimeout(2500);
    await page.getByRole('button', { name: 'Send' }).click();

    await page.waitForURL(new RegExp(`/t/${team.url}/documents/envelope_.*`));

    await expect(page.getByRole('link', { name: document.title })).toBeVisible();
  });
});
