import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token';
import type { TCheckboxFieldMeta, TRadioFieldMeta } from '@documenso/lib/types/field-meta';
import {
  mapDocumentIdToSecondaryId,
  mapSecondaryIdToDocumentId,
  mapSecondaryIdToTemplateId,
} from '@documenso/lib/utils/envelope';
import { prisma } from '@documenso/prisma';
import { FieldType, RecipientRole } from '@documenso/prisma/client';
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
import { seedUser } from '@documenso/prisma/seed/users';
import { expect, test } from '@playwright/test';

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

const WEBAPP_BASE_URL = NEXT_PUBLIC_WEBAPP_URL();

test.describe('Template Field Prefill API v1', () => {
  test('should create a document from template with prefilled fields', async ({ page, request }) => {
    // 1. Create a user
    const { user, team } = await seedUser();

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

    // 3. Create a template with seedBlankTemplate
    const template = await seedBlankTemplate(user, team.id, {
      createTemplateOptions: {
        title: 'Template with Advanced Fields',
      },
    });

    const firstEnvelopeItem = template.envelopeItems[0];

    // 4. Create a recipient for the template
    const recipient = await prisma.recipient.create({
      data: {
        envelopeId: template.id,
        email: 'recipient@example.com',
        name: 'Test Recipient',
        role: RecipientRole.SIGNER,
        token: 'test-token',
        readStatus: 'NOT_OPENED',
        sendStatus: 'NOT_SENT',
        signingStatus: 'NOT_SIGNED',
      },
    });

    // 5. Add fields to the template
    // Add TEXT field
    const textField = await prisma.field.create({
      data: {
        envelopeId: template.id,
        envelopeItemId: firstEnvelopeItem.id,
        recipientId: recipient.id,
        type: FieldType.TEXT,
        page: 1,
        positionX: 5,
        positionY: 5,
        width: 10,
        height: 5,
        customText: '',
        inserted: false,
        fieldMeta: {
          type: 'text',
          label: 'Text Field',
        },
      },
    });

    // Add NUMBER field
    const numberField = await prisma.field.create({
      data: {
        envelopeId: template.id,
        envelopeItemId: firstEnvelopeItem.id,
        recipientId: recipient.id,
        type: FieldType.NUMBER,
        page: 1,
        positionX: 5,
        positionY: 15,
        width: 10,
        height: 5,
        customText: '',
        inserted: false,
        fieldMeta: {
          type: 'number',
          label: 'Number Field',
        },
      },
    });

    // Add RADIO field
    const radioField = await prisma.field.create({
      data: {
        envelopeId: template.id,
        envelopeItemId: firstEnvelopeItem.id,
        recipientId: recipient.id,
        type: FieldType.RADIO,
        page: 1,
        positionX: 5,
        positionY: 25,
        width: 10,
        height: 5,
        customText: '',
        inserted: false,
        fieldMeta: {
          type: 'radio',
          label: 'Radio Field',
          values: [
            { id: 1, value: 'Option A', checked: false },
            { id: 2, value: 'Option B', checked: false },
          ],
        },
      },
    });

    // Add CHECKBOX field
    const checkboxField = await prisma.field.create({
      data: {
        envelopeId: template.id,
        envelopeItemId: firstEnvelopeItem.id,
        recipientId: recipient.id,
        type: FieldType.CHECKBOX,
        page: 1,
        positionX: 5,
        positionY: 35,
        width: 10,
        height: 5,
        customText: '',
        inserted: false,
        fieldMeta: {
          type: 'checkbox',
          label: 'Checkbox Field',
          values: [
            { id: 1, value: 'Check A', checked: false },
            { id: 2, value: 'Check B', checked: false },
          ],
        },
      },
    });

    // Add DROPDOWN field
    const dropdownField = await prisma.field.create({
      data: {
        envelopeId: template.id,
        envelopeItemId: firstEnvelopeItem.id,
        recipientId: recipient.id,
        type: FieldType.DROPDOWN,
        page: 1,
        positionX: 5,
        positionY: 45,
        width: 10,
        height: 5,
        customText: '',
        inserted: false,
        fieldMeta: {
          type: 'dropdown',
          label: 'Dropdown Field',
          values: [{ value: 'Select A' }, { value: 'Select B' }],
        },
      },
    });

    // Add SIGNATURE field (required for distribution)
    await prisma.field.create({
      data: {
        envelopeId: template.id,
        envelopeItemId: firstEnvelopeItem.id,
        recipientId: recipient.id,
        type: FieldType.SIGNATURE,
        page: 1,
        positionX: 1,
        positionY: 1,
        width: 1,
        height: 1,
        customText: '',
        inserted: false,
        fieldMeta: { type: 'signature', fontSize: 14 },
      },
    });

    // 6. Sign in as the user
    await apiSignin({
      page,
      email: user.email,
    });

    // 7. Navigate to the template
    await page.goto(`${WEBAPP_BASE_URL}/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`);

    // 8. Create a document from the template with prefilled fields
    const response = await request.post(
      `${WEBAPP_BASE_URL}/api/v1/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}/generate-document`,
      {
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
        data: {
          title: 'Document with Prefilled Fields',
          recipients: [
            {
              id: recipient.id,
              email: 'recipient@example.com',
              name: 'Test Recipient',
              role: 'SIGNER',
            },
          ],
          prefillFields: [
            {
              id: textField.id,
              type: 'text',
              label: 'Prefilled Text',
              value: 'This is prefilled text',
            },
            {
              id: numberField.id,
              type: 'number',
              label: 'Prefilled Number',
              value: '98765',
            },
            {
              id: radioField.id,
              type: 'radio',
              label: 'Prefilled Radio',
              value: 'Option A',
            },
            {
              id: checkboxField.id,
              type: 'checkbox',
              label: 'Prefilled Checkbox',
              value: ['Check A', 'Check B'],
            },
            {
              id: dropdownField.id,
              type: 'dropdown',
              label: 'Prefilled Dropdown',
              value: 'Select B',
            },
          ],
        },
      },
    );
    const responseData = await response.json();

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

    expect(responseData.documentId).toBeDefined();

    // 9. Verify the document was created with prefilled fields
    const document = await prisma.envelope.findUnique({
      where: {
        secondaryId: mapDocumentIdToSecondaryId(responseData.documentId),
      },
      include: {
        fields: true,
      },
    });

    expect(document).not.toBeNull();

    if (!document) {
      throw new Error('Document not found');
    }

    // 10. Verify each field has the correct prefilled values
    const documentTextField = document?.fields.find(
      (field) => field.type === FieldType.TEXT && field.fieldMeta?.type === 'text',
    );
    expect(documentTextField?.fieldMeta).toMatchObject({
      type: 'text',
      label: 'Prefilled Text',
      text: 'This is prefilled text',
    });

    const documentNumberField = document?.fields.find(
      (field) => field.type === FieldType.NUMBER && field.fieldMeta?.type === 'number',
    );
    expect(documentNumberField?.fieldMeta).toMatchObject({
      type: 'number',
      label: 'Prefilled Number',
      value: '98765',
    });

    const documentRadioField = document?.fields.find(
      (field) => field.type === FieldType.RADIO && field.fieldMeta?.type === 'radio',
    );
    expect(documentRadioField?.fieldMeta).toMatchObject({
      type: 'radio',
      label: 'Prefilled Radio',
    });
    // Check that the correct radio option is selected
    const radioValues = (documentRadioField?.fieldMeta as TRadioFieldMeta)?.values || [];
    const selectedRadioOption = radioValues.find((option) => option.checked);
    expect(selectedRadioOption?.value).toBe('Option A');

    const documentCheckboxField = document?.fields.find(
      (field) => field.type === FieldType.CHECKBOX && field.fieldMeta?.type === 'checkbox',
    );
    expect(documentCheckboxField?.fieldMeta).toMatchObject({
      type: 'checkbox',
      label: 'Prefilled Checkbox',
    });
    // Check that the correct checkbox options are selected
    const checkboxValues = (documentCheckboxField?.fieldMeta as TCheckboxFieldMeta)?.values || [];
    const checkedOptions = checkboxValues.filter((option) => option.checked);
    expect(checkedOptions.length).toBe(2);
    expect(checkedOptions.map((option) => option.value)).toContain('Check A');
    expect(checkedOptions.map((option) => option.value)).toContain('Check B');

    const documentDropdownField = document?.fields.find(
      (field) => field.type === FieldType.DROPDOWN && field.fieldMeta?.type === 'dropdown',
    );
    expect(documentDropdownField?.fieldMeta).toMatchObject({
      type: 'dropdown',
      label: 'Prefilled Dropdown',
      defaultValue: 'Select B',
    });

    // 11. Sign in as the recipient and verify the prefilled fields are visible
    const documentRecipient = await prisma.recipient.findFirst({
      where: {
        envelopeId: document?.id,
        email: 'recipient@example.com',
      },
    });

    // Send the document to the recipient
    const sendResponse = await request.post(
      `${WEBAPP_BASE_URL}/api/v1/documents/${mapSecondaryIdToDocumentId(document.secondaryId)}/send`,
      {
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
        data: {
          sendEmail: false,
        },
      },
    );

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

    expect(documentRecipient).not.toBeNull();

    // Visit the signing page
    await page.goto(`${WEBAPP_BASE_URL}/sign/${documentRecipient?.token}`);

    // Verify the prefilled fields are visible with correct values
    // Text field
    await expect(page.getByText('This is prefilled')).toBeVisible();

    // Number field
    await expect(page.getByText('98765', { exact: true })).toBeVisible();

    // Radio field
    await expect(page.getByText('Option A')).toBeVisible();
    await expect(page.getByRole('radio', { name: 'Option A' })).toBeChecked();

    // Checkbox field
    await expect(page.getByText('Check A')).toBeVisible();
    await expect(page.getByText('Check B')).toBeVisible();
    await expect(page.getByRole('checkbox', { name: 'Check A' })).toBeChecked();
    await expect(page.getByRole('checkbox', { name: 'Check B' })).toBeChecked();

    // Dropdown field
    await expect(page.getByText('Select B')).toBeVisible();
  });

  test('should create a document from template without prefilled fields', async ({ page, request }) => {
    // 1. Create a user
    const { user, team } = await seedUser();

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

    // 3. Create a template with seedBlankTemplate
    const template = await seedBlankTemplate(user, team.id, {
      createTemplateOptions: {
        title: 'Template with Default Fields',
      },
    });

    const firstEnvelopeItem = template.envelopeItems[0];

    // 4. Create a recipient for the template
    const recipient = await prisma.recipient.create({
      data: {
        envelopeId: template.id,
        email: 'recipient@example.com',
        name: 'Test Recipient',
        role: RecipientRole.SIGNER,
        token: 'test-token',
        readStatus: 'NOT_OPENED',
        sendStatus: 'NOT_SENT',
        signingStatus: 'NOT_SIGNED',
      },
    });

    // 5. Add fields to the template
    // Add TEXT field
    await prisma.field.create({
      data: {
        envelopeId: template.id,
        envelopeItemId: firstEnvelopeItem.id,
        recipientId: recipient.id,
        type: FieldType.TEXT,
        page: 1,
        positionX: 5,
        positionY: 5,
        width: 10,
        height: 5,
        customText: '',
        inserted: false,
        fieldMeta: {
          type: 'text',
          label: 'Default Text Field',
        },
      },
    });

    // Add NUMBER field
    await prisma.field.create({
      data: {
        envelopeId: template.id,
        envelopeItemId: firstEnvelopeItem.id,
        recipientId: recipient.id,
        type: FieldType.NUMBER,
        page: 1,
        positionX: 5,
        positionY: 15,
        width: 10,
        height: 5,
        customText: '',
        inserted: false,
        fieldMeta: {
          type: 'number',
          label: 'Default Number Field',
        },
      },
    });

    // Add SIGNATURE field (required for distribution)
    await prisma.field.create({
      data: {
        envelopeId: template.id,
        envelopeItemId: firstEnvelopeItem.id,
        recipientId: recipient.id,
        type: FieldType.SIGNATURE,
        page: 1,
        positionX: 1,
        positionY: 1,
        width: 1,
        height: 1,
        customText: '',
        inserted: false,
        fieldMeta: { type: 'signature', fontSize: 14 },
      },
    });

    // 6. Sign in as the user
    await apiSignin({
      page,
      email: user.email,
    });

    // 7. Navigate to the template
    await page.goto(`${WEBAPP_BASE_URL}/t/${team.url}/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}`);

    // 8. Create a document from the template without prefilled fields
    const response = await request.post(
      `${WEBAPP_BASE_URL}/api/v1/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}/generate-document`,
      {
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
        data: {
          title: 'Document with Default Fields',
          recipients: [
            {
              id: recipient.id,
              email: 'recipient@example.com',
              name: 'Test Recipient',
              role: 'SIGNER',
            },
          ],
        },
      },
    );

    const responseData = await response.json();

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

    expect(responseData.documentId).toBeDefined();

    // 9. Verify the document was created with default fields
    const document = await prisma.envelope.findUnique({
      where: {
        secondaryId: mapDocumentIdToSecondaryId(responseData.documentId),
      },
      include: {
        fields: true,
      },
    });

    expect(document).not.toBeNull();

    if (!document) {
      throw new Error('Document not found');
    }

    // 10. Verify fields have their default values
    const documentTextField = document?.fields.find((field) => field.type === FieldType.TEXT);
    expect(documentTextField?.fieldMeta).toMatchObject({
      type: 'text',
      label: 'Default Text Field',
    });

    const documentNumberField = document?.fields.find((field) => field.type === FieldType.NUMBER);
    expect(documentNumberField?.fieldMeta).toMatchObject({
      type: 'number',
      label: 'Default Number Field',
    });

    // 11. Sign in as the recipient and verify the default fields are visible
    const documentRecipient = await prisma.recipient.findFirst({
      where: {
        envelopeId: document?.id,
        email: 'recipient@example.com',
      },
    });

    expect(documentRecipient).not.toBeNull();

    const sendResponse = await request.post(
      `${WEBAPP_BASE_URL}/api/v1/documents/${mapSecondaryIdToDocumentId(document.secondaryId)}/send`,
      {
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
        data: {
          sendEmail: false,
        },
      },
    );

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

    // Visit the signing page
    await page.goto(`${WEBAPP_BASE_URL}/sign/${documentRecipient?.token}`);

    // Verify the default fields are visible with correct labels
    await expect(page.getByText('Default Text Field')).toBeVisible();
    await expect(page.getByText('Default Number Field')).toBeVisible();
  });

  test('should handle invalid field prefill values', async ({ request }) => {
    // 1. Create a user
    const { user, team } = await seedUser();

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

    // 3. Create a template using seedBlankTemplate
    const template = await seedBlankTemplate(user, team.id, {
      createTemplateOptions: {
        title: 'Template for Invalid Test',
        visibility: 'EVERYONE',
      },
    });

    const firstEnvelopeItem = template.envelopeItems[0];

    // 4. Create a recipient for the template
    const recipient = await prisma.recipient.create({
      data: {
        envelopeId: template.id,
        email: 'recipient@example.com',
        name: 'Test Recipient',
        role: RecipientRole.SIGNER,
        token: 'test-token',
        readStatus: 'NOT_OPENED',
        sendStatus: 'NOT_SENT',
        signingStatus: 'NOT_SIGNED',
      },
    });

    // 5. Add a field to the template
    const field = await prisma.field.create({
      data: {
        envelopeId: template.id,
        envelopeItemId: firstEnvelopeItem.id,
        recipientId: recipient.id,
        type: FieldType.RADIO,
        page: 1,
        positionX: 100,
        positionY: 100,
        width: 100,
        height: 50,
        customText: '',
        inserted: false,
        fieldMeta: {
          type: 'radio',
          label: 'Radio Field',
          values: [
            { id: 1, value: 'Option A', checked: false },
            { id: 2, value: 'Option B', checked: false },
          ],
        },
      },
    });

    // 6. Try to create a document with invalid prefill value
    const response = await request.post(
      `${WEBAPP_BASE_URL}/api/v1/templates/${mapSecondaryIdToTemplateId(template.secondaryId)}/generate-document`,
      {
        headers: {
          Authorization: `Bearer ${token}`,
          'Content-Type': 'application/json',
        },
        data: {
          title: 'Document with Invalid Prefill',
          recipients: [
            {
              id: recipient.id,
              email: 'recipient@example.com',
              name: 'Test Recipient',
              role: 'SIGNER',
            },
          ],
          prefillFields: [
            {
              id: field.id,
              type: 'radio',
              label: 'Invalid Radio',
              value: 'Non-existent Option', // This option doesn't exist
            },
          ],
        },
      },
    );

    // 7. Verify the request fails with appropriate error
    expect(response.ok()).toBeFalsy();
    expect(response.status()).toBe(400);

    const errorData = await response.json();
    expect(errorData.message).toContain('not found in options for RADIO field');
  });
});
