import fs from 'node:fs';
import 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 { seedPendingDocument } from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
import type {
  TCreateEnvelopePayload,
  TCreateEnvelopeResponse,
} from '@documenso/trpc/server/envelope-router/create-envelope.types';
import type { TDistributeEnvelopeRequest } from '@documenso/trpc/server/envelope-router/distribute-envelope.types';
import { expect, test } from '@playwright/test';

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

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

const examplePdf = fs.readFileSync(path.join(__dirname, '../../../../assets/example.pdf'));

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

test('[ENVELOPE_EXPIRATION]: sending document sets expiresAt on recipients', async ({ request }) => {
  const { user, team } = await seedUser();

  const { token } = await createApiToken({
    userId: user.id,
    teamId: team.id,
    tokenName: 'test-expiration-send',
    expiresIn: null,
  });

  const createPayload: TCreateEnvelopePayload = {
    type: EnvelopeType.DOCUMENT,
    title: '[TEST] Expiration Send Test',
    recipients: [
      {
        email: 'signer-expiry@test.documenso.com',
        name: 'Signer Expiry',
        role: RecipientRole.SIGNER,
        fields: [
          {
            type: 'SIGNATURE',
            page: 1,
            positionX: 10,
            positionY: 10,
            width: 10,
            height: 5,
            fieldMeta: { type: 'signature' },
          },
        ],
      },
    ],
  };

  const formData = new FormData();
  formData.append('payload', JSON.stringify(createPayload));
  formData.append('files', new File([examplePdf], 'example.pdf', { type: 'application/pdf' }));

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

  expect(createRes.ok()).toBeTruthy();

  const { id: envelopeId }: TCreateEnvelopeResponse = await createRes.json();

  const distributeRes = await request.post(`${baseUrl}/envelope/distribute`, {
    headers: { Authorization: `Bearer ${token}` },
    data: { envelopeId } satisfies TDistributeEnvelopeRequest,
  });

  expect(distributeRes.ok()).toBeTruthy();

  // Check that recipients now have expiresAt set.
  const recipients = await prisma.recipient.findMany({
    where: { envelopeId },
  });

  expect(recipients.length).toBe(1);
  expect(recipients[0].expiresAt).not.toBeNull();

  // The default expiration period is 3 months. Verify it's roughly correct.
  const expiresAt = recipients[0].expiresAt!;
  const now = new Date();
  const diffMs = expiresAt.getTime() - now.getTime();
  const diffDays = diffMs / (1000 * 60 * 60 * 24);

  // 3 months is roughly 89-92 days. Allow a generous range.
  expect(diffDays).toBeGreaterThan(80);
  expect(diffDays).toBeLessThan(100);
});

test('[ENVELOPE_EXPIRATION]: sending document with custom org expiration period', async ({ request }) => {
  const { user, organisation, team } = await seedUser();

  // Set org expiration to 7 days.
  await prisma.organisationGlobalSettings.update({
    where: { id: organisation.organisationGlobalSettingsId },
    data: { envelopeExpirationPeriod: { unit: 'day', amount: 7 } },
  });

  const { token } = await createApiToken({
    userId: user.id,
    teamId: team.id,
    tokenName: 'test-expiration-custom',
    expiresIn: null,
  });

  const createPayload: TCreateEnvelopePayload = {
    type: EnvelopeType.DOCUMENT,
    title: '[TEST] Custom Expiration Send Test',
    recipients: [
      {
        email: 'signer-custom@test.documenso.com',
        name: 'Signer Custom',
        role: RecipientRole.SIGNER,
        fields: [
          {
            type: 'SIGNATURE',
            page: 1,
            positionX: 10,
            positionY: 10,
            width: 10,
            height: 5,
            fieldMeta: { type: 'signature' },
          },
        ],
      },
    ],
  };

  const formData = new FormData();
  formData.append('payload', JSON.stringify(createPayload));
  formData.append('files', new File([examplePdf], 'example.pdf', { type: 'application/pdf' }));

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

  expect(createRes.ok()).toBeTruthy();

  const { id: envelopeId }: TCreateEnvelopeResponse = await createRes.json();

  const distributeRes = await request.post(`${baseUrl}/envelope/distribute`, {
    headers: { Authorization: `Bearer ${token}` },
    data: { envelopeId } satisfies TDistributeEnvelopeRequest,
  });

  expect(distributeRes.ok()).toBeTruthy();

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

  expect(recipients.length).toBe(1);
  expect(recipients[0].expiresAt).not.toBeNull();

  // 7 days expiration.
  const expiresAt = recipients[0].expiresAt!;
  const now = new Date();
  const diffMs = expiresAt.getTime() - now.getTime();
  const diffDays = diffMs / (1000 * 60 * 60 * 24);

  expect(diffDays).toBeGreaterThan(6);
  expect(diffDays).toBeLessThan(8);
});

test('[ENVELOPE_EXPIRATION]: sending document with expiration disabled', async ({ request }) => {
  const { user, organisation, team } = await seedUser();

  // Disable expiration at org level.
  await prisma.organisationGlobalSettings.update({
    where: { id: organisation.organisationGlobalSettingsId },
    data: { envelopeExpirationPeriod: { disabled: true } },
  });

  const { token } = await createApiToken({
    userId: user.id,
    teamId: team.id,
    tokenName: 'test-expiration-disabled',
    expiresIn: null,
  });

  const createPayload: TCreateEnvelopePayload = {
    type: EnvelopeType.DOCUMENT,
    title: '[TEST] Disabled Expiration Send Test',
    recipients: [
      {
        email: 'signer-disabled@test.documenso.com',
        name: 'Signer Disabled',
        role: RecipientRole.SIGNER,
        fields: [
          {
            type: 'SIGNATURE',
            page: 1,
            positionX: 10,
            positionY: 10,
            width: 10,
            height: 5,
            fieldMeta: { type: 'signature' },
          },
        ],
      },
    ],
  };

  const formData = new FormData();
  formData.append('payload', JSON.stringify(createPayload));
  formData.append('files', new File([examplePdf], 'example.pdf', { type: 'application/pdf' }));

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

  expect(createRes.ok()).toBeTruthy();

  const { id: envelopeId }: TCreateEnvelopeResponse = await createRes.json();

  const distributeRes = await request.post(`${baseUrl}/envelope/distribute`, {
    headers: { Authorization: `Bearer ${token}` },
    data: { envelopeId } satisfies TDistributeEnvelopeRequest,
  });

  expect(distributeRes.ok()).toBeTruthy();

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

  expect(recipients.length).toBe(1);
  expect(recipients[0].expiresAt).toBeNull();
});

test('[ENVELOPE_EXPIRATION]: resending refreshes expiresAt', async ({ page }) => {
  const { user, team } = await seedUser();

  const document = await seedPendingDocument(user, team.id, ['resend-target@test.documenso.com']);

  const recipient = document.recipients[0];

  // Set an initial expiresAt that's 1 day from now.
  const initialExpiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000);

  await prisma.recipient.update({
    where: { id: recipient.id },
    data: { expiresAt: initialExpiresAt },
  });

  await apiSignin({
    page,
    email: user.email,
    redirectPath: `/t/${team.url}/documents?status=PENDING`,
  });

  // Open the document action menu and click Resend.
  const actionBtn = page.getByTestId('document-table-action-btn').first();
  await expect(actionBtn).toBeAttached();
  await openDropdownMenu(page, actionBtn);
  await expect(page.getByRole('menuitem', { name: 'Resend' })).toBeVisible();
  await page.getByRole('menuitem', { name: 'Resend' }).click();

  // Select the recipient and send.
  await page.getByLabel('test.documenso.com').first().click();
  await page.getByRole('button', { name: 'Send reminder' }).click();

  await expect(page.getByText('Document resent', { exact: true })).toBeVisible({
    timeout: 10_000,
  });

  // Verify expiresAt was refreshed.
  await expect(async () => {
    const updatedRecipient = await prisma.recipient.findUniqueOrThrow({
      where: { id: recipient.id },
    });

    expect(updatedRecipient.expiresAt).not.toBeNull();
    expect(updatedRecipient.expiresAt!.getTime()).toBeGreaterThan(initialExpiresAt.getTime());
  }).toPass({ timeout: 10_000 });
});
