import * as fs from 'node:fs';
import * as path from 'node:path';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token';
import { prisma } from '@documenso/prisma';
import { EnvelopeType, RecipientRole } from '@documenso/prisma/client';
import { seedTeam } from '@documenso/prisma/seed/teams';
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
import type {
  TCreateEnvelopePayload,
  TCreateEnvelopeResponse,
} from '@documenso/trpc/server/envelope-router/create-envelope.types';
import { expect, test } from '@playwright/test';

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

const WEBAPP_BASE_URL = NEXT_PUBLIC_WEBAPP_URL();
const baseUrl = `${WEBAPP_BASE_URL}/api/v2-beta`;

test.describe.configure({ mode: 'parallel' });

/**
 * Helper function to set default recipients for a team
 */
const setTeamDefaultRecipients = async (
  teamId: number,
  defaultRecipients: Array<{ email: string; name: string; role: RecipientRole }>,
) => {
  const teamSettings = await prisma.teamGlobalSettings.findFirstOrThrow({
    where: {
      team: {
        id: teamId,
      },
    },
  });

  await prisma.teamGlobalSettings.update({
    where: {
      id: teamSettings.id,
    },
    data: {
      defaultRecipients,
    },
  });
};

test.describe('Default Recipients', () => {
  test('[DEFAULT_RECIPIENTS]: default recipients are added to documents created via UI', async ({ page }) => {
    const { team, owner } = await seedTeam({
      createTeamMembers: 2,
    });

    // Get a team member to set as default recipient
    const teamMembers = await prisma.organisationMember.findMany({
      where: {
        organisationId: team.organisationId,
        userId: {
          not: owner.id,
        },
      },
      include: {
        user: true,
      },
    });

    const defaultRecipientUser = teamMembers[0].user;

    // Set up default recipients for the team
    await setTeamDefaultRecipients(team.id, [
      {
        email: defaultRecipientUser.email,
        name: defaultRecipientUser.name || defaultRecipientUser.email,
        role: RecipientRole.CC,
      },
    ]);

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

    // Upload document via UI - this triggers document creation with default recipients
    const [fileChooser] = await Promise.all([
      page.waitForEvent('filechooser'),
      page
        .locator('input[type=file]')
        .nth(1)
        .evaluate((e) => {
          if (e instanceof HTMLInputElement) {
            e.click();
          }
        }),
    ]);

    await fileChooser.setFiles(path.join(__dirname, '../../../../assets/example.pdf'));

    // Wait to be redirected to the edit page (v2 envelope editor)
    await page.waitForURL(new RegExp(`/t/${team.url}/documents/envelope_.*`));

    // Extract document ID from URL
    const urlParts = page.url().split('/');
    const documentId = urlParts.find((part) => part.startsWith('envelope_'));

    // Wait for the Recipients card to be visible (v2 envelope editor)
    await expect(page.getByRole('heading', { name: 'Recipients' })).toBeVisible();

    await expect(page.getByTestId('signer-email-input').first()).not.toBeEmpty();

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

    // Add a regular signer using the v2 editor
    await page.getByTestId('signer-email-input').first().fill('regular-signer@documenso.com');
    await page
      .getByPlaceholder(/Recipient/)
      .first()
      .fill('Regular Signer');

    // Wait for autosave to complete
    await page.waitForTimeout(3000);

    // Verify that default recipient is present in the database
    await expect(async () => {
      const envelope = await prisma.envelope.findFirstOrThrow({
        where: {
          id: documentId,
        },
        include: {
          recipients: true,
        },
      });

      // Should have 2 recipients: the regular signer + the default recipient
      expect(envelope.recipients.length).toBe(2);

      const defaultRecipient = envelope.recipients.find(
        (r) => r.email.toLowerCase() === defaultRecipientUser.email.toLowerCase(),
      );
      expect(defaultRecipient).toBeDefined();
      expect(defaultRecipient?.role).toBe(RecipientRole.CC);

      const regularSigner = envelope.recipients.find((r) => r.email === 'regular-signer@documenso.com');
      expect(regularSigner).toBeDefined();
    }).toPass();
  });

  // TODO: Are we intending to allow default recipients to be removed from a document?
  test.skip('[DEFAULT_RECIPIENTS]: default recipients cannot be removed from a document', async ({ page }) => {
    const { team, owner } = await seedTeam({
      createTeamMembers: 2,
    });

    // Get a team member to set as default recipient
    const teamMembers = await prisma.organisationMember.findMany({
      where: {
        organisationId: team.organisationId,
        userId: {
          not: owner.id,
        },
      },
      include: {
        user: true,
      },
    });

    const defaultRecipientUser = teamMembers[0].user;

    // Set up default recipients for the team
    await setTeamDefaultRecipients(team.id, [
      {
        email: defaultRecipientUser.email,
        name: defaultRecipientUser.name || defaultRecipientUser.email,
        role: RecipientRole.CC,
      },
    ]);

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

    // Upload document via UI - this triggers document creation with default recipients
    const [fileChooser] = await Promise.all([
      page.waitForEvent('filechooser'),
      page
        .locator('input[type=file]')
        .nth(1)
        .evaluate((e) => {
          if (e instanceof HTMLInputElement) {
            e.click();
          }
        }),
    ]);

    await fileChooser.setFiles(path.join(__dirname, '../../../../assets/example.pdf'));

    // Wait to be redirected to the edit page (v2 envelope editor)
    await page.waitForURL(new RegExp(`/t/${team.url}/documents/envelope_.*`));

    // Extract document ID from URL
    const urlParts = page.url().split('/');
    const documentId = urlParts.find((part) => part.startsWith('envelope_'));

    // Replace the default recipient with a regular signer
    await page.getByTestId('signer-email-input').first().fill('regular-signer@documenso.com');
    await page
      .getByPlaceholder(/Recipient/)
      .first()
      .fill('Regular Signer');

    // Wait for autosave to complete
    await page.waitForTimeout(3000);

    // Wait for recipients to be saved
    await expect(async () => {
      const envelope = await prisma.envelope.findFirstOrThrow({
        where: {
          id: documentId,
        },
        include: {
          recipients: true,
        },
      });
      expect(envelope.recipients.length).toBe(2);
    }).toPass();

    // Verify that the default recipient's remove button is disabled
    // In the v2 editor, default recipients should have a disabled remove button
    // Find the fieldset containing the default recipient's email and check if its remove button is disabled
    const defaultRecipientRow = page.locator('fieldset').filter({
      hasText: defaultRecipientUser.email,
    });

    // The default recipient row should exist and have a disabled remove button
    await expect(defaultRecipientRow).toHaveCount(1);
    const removeButton = defaultRecipientRow.getByTestId('remove-signer-button');
    await expect(removeButton).toBeDisabled();
  });

  test('[DEFAULT_RECIPIENTS]: documents created via API have default recipients', async ({ request }) => {
    const { team, owner } = await seedTeam({
      createTeamMembers: 2,
    });

    // Get a team member to set as default recipient
    const teamMembers = await prisma.organisationMember.findMany({
      where: {
        organisationId: team.organisationId,
        userId: {
          not: owner.id,
        },
      },
      include: {
        user: true,
      },
    });

    const defaultRecipientUser = teamMembers[0].user;

    // Set up default recipients for the team
    await setTeamDefaultRecipients(team.id, [
      {
        email: defaultRecipientUser.email,
        name: defaultRecipientUser.name || defaultRecipientUser.email,
        role: RecipientRole.CC,
      },
    ]);

    // Create API token
    const { token } = await createApiToken({
      userId: owner.id,
      teamId: team.id,
      tokenName: 'test-token',
      expiresIn: null,
    });

    // Create envelope via API
    const payload: TCreateEnvelopePayload = {
      type: EnvelopeType.DOCUMENT,
      title: 'Test Document with Default Recipients',
      recipients: [
        {
          email: 'api-recipient@documenso.com',
          name: 'API Recipient',
          role: RecipientRole.SIGNER,
        },
      ],
    };

    const formData = new FormData();
    formData.append('payload', JSON.stringify(payload));

    const pdfData = fs.readFileSync(path.join(__dirname, '../../../../assets/example.pdf'));
    formData.append('files', new File([pdfData], 'test.pdf', { type: 'application/pdf' }));

    const res = await request.post(`${baseUrl}/envelope/create`, {
      headers: { Authorization: `Bearer ${token}` },
      multipart: formData,
    });

    expect(res.ok()).toBeTruthy();
    expect(res.status()).toBe(200);

    const response = (await res.json()) as TCreateEnvelopeResponse;

    // Verify the envelope has both the API recipient and the default recipient
    const envelope = await prisma.envelope.findUniqueOrThrow({
      where: {
        id: response.id,
      },
      include: {
        recipients: true,
      },
    });

    expect(envelope.recipients.length).toBe(2);

    const apiRecipient = envelope.recipients.find((r) => r.email === 'api-recipient@documenso.com');
    expect(apiRecipient).toBeDefined();
    expect(apiRecipient?.role).toBe(RecipientRole.SIGNER);

    const defaultRecipient = envelope.recipients.find(
      (r) => r.email.toLowerCase() === defaultRecipientUser.email.toLowerCase(),
    );
    expect(defaultRecipient).toBeDefined();
    expect(defaultRecipient?.role).toBe(RecipientRole.CC);
  });

  test('[DEFAULT_RECIPIENTS]: documents created from template have default recipients', async ({ page }) => {
    const { team, owner } = await seedTeam({
      createTeamMembers: 2,
    });

    // Get a team member to set as default recipient
    const teamMembers = await prisma.organisationMember.findMany({
      where: {
        organisationId: team.organisationId,
        userId: {
          not: owner.id,
        },
      },
      include: {
        user: true,
      },
    });

    const defaultRecipientUser = teamMembers[0].user;

    // Set up default recipients for the team
    await setTeamDefaultRecipients(team.id, [
      {
        email: defaultRecipientUser.email,
        name: defaultRecipientUser.name || defaultRecipientUser.email,
        role: RecipientRole.CC,
      },
    ]);

    // Create a template
    const template = await seedBlankTemplate(owner, team.id);

    await apiSignin({
      page,
      email: owner.email,
      redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
    });

    // Set template title
    await page.getByLabel('Title').fill('Template with Default Recipients');

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

    // Add a template recipient
    await page.getByPlaceholder('Email').fill('template-recipient@documenso.com');
    await page.getByPlaceholder('Name').fill('Template Recipient');

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

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

    // Use template to create document
    await page.waitForURL(`/t/${team.url}/templates`);
    await page.getByRole('button', { name: 'Use Template' }).click();
    await page.getByRole('button', { name: 'Create as draft' }).click();

    // Wait for document to be created
    await page.waitForURL(new RegExp(`/t/${team.url}/documents/envelope_.*`));

    const documentId = page.url().split('/').pop();

    // Verify the document has both the template recipient and the default recipient
    const document = await prisma.envelope.findFirstOrThrow({
      where: {
        id: documentId,
      },
      include: {
        recipients: true,
      },
    });

    expect(document.recipients.length).toBe(2);

    const templateRecipient = document.recipients.find((r) => r.email === 'template-recipient@documenso.com');
    expect(templateRecipient).toBeDefined();

    const defaultRecipient = document.recipients.find(
      (r) => r.email.toLowerCase() === defaultRecipientUser.email.toLowerCase(),
    );
    expect(defaultRecipient).toBeDefined();
    expect(defaultRecipient?.role).toBe(RecipientRole.CC);
  });
});
