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 { seedBlankDocument } from '@documenso/prisma/seed/documents';
import { seedBlankFolder } from '@documenso/prisma/seed/folders';
import { seedUser } from '@documenso/prisma/seed/users';
import { expect, test } from '@playwright/test';
import type { Team, User } from '@prisma/client';
import { EnvelopeType, FolderType } from '@prisma/client';

const WEBAPP_BASE_URL = NEXT_PUBLIC_WEBAPP_URL();

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

// Todo: Remove skip once the API endpoints are released.
test.describe
  .skip('Envelope Bulk API V2', () => {
    let userA: User, teamA: Team, userB: User, teamB: Team, tokenA: string, tokenB: string;

    test.beforeEach(async () => {
      ({ user: userA, team: teamA } = await seedUser());
      ({ token: tokenA } = await createApiToken({
        userId: userA.id,
        teamId: teamA.id,
        tokenName: 'userA',
        expiresIn: null,
      }));

      ({ user: userB, team: teamB } = await seedUser());
      ({ token: tokenB } = await createApiToken({
        userId: userB.id,
        teamId: teamB.id,
        tokenName: 'userB',
        expiresIn: null,
      }));
    });

    test.describe('Envelope bulk move endpoint', () => {
      test('should block unauthorized access to envelope bulk move endpoint', async ({ request }) => {
        // Create a document owned by userA
        const doc = await seedBlankDocument(userA, teamA.id);

        // UserB tries to move userA's document
        const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/bulk/move`, {
          headers: { Authorization: `Bearer ${tokenB}` },
          data: {
            envelopeIds: [doc.id],
            envelopeType: EnvelopeType.DOCUMENT,
            folderId: null,
          },
        });

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

        const body = await res.json();
        expect(body.movedCount).toBe(0);

        // Verify in database that the document was not modified
        const docInDb = await prisma.envelope.findFirst({
          where: { id: doc.id },
        });

        expect(docInDb).not.toBeNull();
        expect(docInDb?.folderId).toBeNull();
      });

      test('should block moving envelopes to unauthorized folder', async ({ request }) => {
        // Create a document owned by userB
        const doc = await seedBlankDocument(userB, teamB.id);

        // Create a folder owned by userA
        const folderA = await seedBlankFolder(userA, teamA.id, {
          createFolderOptions: {
            name: 'UserA Folder',
            type: FolderType.DOCUMENT,
          },
        });

        // UserB tries to move their document to userA's folder
        const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/bulk/move`, {
          headers: { Authorization: `Bearer ${tokenB}` },
          data: {
            envelopeIds: [doc.id],
            envelopeType: EnvelopeType.DOCUMENT,
            folderId: folderA.id,
          },
        });

        expect(res.ok()).toBeFalsy();
        expect(res.status()).toBe(404);

        // Verify in database that the document was not modified
        const docInDb = await prisma.envelope.findFirst({
          where: { id: doc.id },
        });

        expect(docInDb).not.toBeNull();
        expect(docInDb?.folderId).toBeNull();
      });

      test('should allow authorized access to envelope bulk move endpoint', async ({ request }) => {
        // Create a document owned by userA
        const doc = await seedBlankDocument(userA, teamA.id);

        // Create a folder owned by userA
        const folderA = await seedBlankFolder(userA, teamA.id, {
          createFolderOptions: {
            name: 'UserA Folder',
            type: FolderType.DOCUMENT,
          },
        });

        // UserA moves their own document to their own folder
        const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/bulk/move`, {
          headers: { Authorization: `Bearer ${tokenA}` },
          data: {
            envelopeIds: [doc.id],
            envelopeType: EnvelopeType.DOCUMENT,
            folderId: folderA.id,
          },
        });

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

        const body = await res.json();
        expect(body.movedCount).toBe(1);

        // Verify in database that the document was moved to the folder
        const docInDb = await prisma.envelope.findFirst({
          where: { id: doc.id },
        });

        expect(docInDb).not.toBeNull();
        expect(docInDb?.folderId).toBe(folderA.id);
      });

      test('should only move authorized envelopes when given mixed array of envelope IDs', async ({ request }) => {
        // Create documents owned by userA
        const docA1 = await seedBlankDocument(userA, teamA.id);
        const docA2 = await seedBlankDocument(userA, teamA.id);

        // Create a document owned by userB
        const docB = await seedBlankDocument(userB, teamB.id);

        // Create a folder owned by userA
        const folderA = await seedBlankFolder(userA, teamA.id, {
          createFolderOptions: {
            name: 'UserA Folder',
            type: FolderType.DOCUMENT,
          },
        });

        // UserA tries to move a mix of their own documents and userB's document
        const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/bulk/move`, {
          headers: { Authorization: `Bearer ${tokenA}` },
          data: {
            envelopeIds: [docA1.id, docB.id, docA2.id],
            envelopeType: EnvelopeType.DOCUMENT,
            folderId: folderA.id,
          },
        });

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

        const body = await res.json();
        // Only userA's documents should be moved
        expect(body.movedCount).toBe(2);

        // Verify userA's documents were moved
        const docA1InDb = await prisma.envelope.findFirst({
          where: { id: docA1.id },
        });
        expect(docA1InDb).not.toBeNull();
        expect(docA1InDb?.folderId).toBe(folderA.id);

        const docA2InDb = await prisma.envelope.findFirst({
          where: { id: docA2.id },
        });
        expect(docA2InDb).not.toBeNull();
        expect(docA2InDb?.folderId).toBe(folderA.id);

        // Verify userB's document was NOT moved
        const docBInDb = await prisma.envelope.findFirst({
          where: { id: docB.id },
        });
        expect(docBInDb).not.toBeNull();
        expect(docBInDb?.folderId).toBeNull();
      });

      test('should move zero envelopes when all envelope IDs in array are unauthorized', async ({ request }) => {
        // Create documents owned by userB
        const docB1 = await seedBlankDocument(userB, teamB.id);
        const docB2 = await seedBlankDocument(userB, teamB.id);

        // Create a folder owned by userA
        const folderA = await seedBlankFolder(userA, teamA.id, {
          createFolderOptions: {
            name: 'UserA Folder',
            type: FolderType.DOCUMENT,
          },
        });

        // UserA tries to move userB's documents
        const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/bulk/move`, {
          headers: { Authorization: `Bearer ${tokenA}` },
          data: {
            envelopeIds: [docB1.id, docB2.id],
            envelopeType: EnvelopeType.DOCUMENT,
            folderId: folderA.id,
          },
        });

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

        const body = await res.json();
        expect(body.movedCount).toBe(0);

        // Verify userB's documents were NOT moved
        const docB1InDb = await prisma.envelope.findFirst({
          where: { id: docB1.id },
        });
        expect(docB1InDb).not.toBeNull();
        expect(docB1InDb?.folderId).toBeNull();

        const docB2InDb = await prisma.envelope.findFirst({
          where: { id: docB2.id },
        });
        expect(docB2InDb).not.toBeNull();
        expect(docB2InDb?.folderId).toBeNull();
      });
    });

    test.describe('Envelope bulk delete endpoint', () => {
      test('should block unauthorized access to envelope bulk delete endpoint', async ({ request }) => {
        // Create a document owned by userA
        const doc = await seedBlankDocument(userA, teamA.id);

        // UserB tries to delete userA's document
        const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/bulk/delete`, {
          headers: { Authorization: `Bearer ${tokenB}` },
          data: {
            envelopeIds: [doc.id],
          },
        });

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

        const body = await res.json();
        expect(body.deletedCount).toBe(0);
        // Unauthorized envelope ID should be in failedIds
        expect(body.failedIds).toEqual([doc.id]);

        // Verify in database that the document still exists
        const docInDb = await prisma.envelope.findFirst({
          where: { id: doc.id },
        });

        expect(docInDb).not.toBeNull();
        expect(docInDb?.id).toBe(doc.id);
        expect(docInDb?.deletedAt).toBeNull();
      });

      test('should allow authorized access to envelope bulk delete endpoint', async ({ request }) => {
        // Create a document owned by userA
        const doc = await seedBlankDocument(userA, teamA.id);

        // UserA deletes their own document
        const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/bulk/delete`, {
          headers: { Authorization: `Bearer ${tokenA}` },
          data: {
            envelopeIds: [doc.id],
          },
        });

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

        const body = await res.json();
        expect(body.deletedCount).toBe(1);
        expect(body.failedIds).toEqual([]);

        // Verify in database that the document no longer exists
        const docInDb = await prisma.envelope.findFirst({
          where: { id: doc.id },
        });

        expect(docInDb).toBeNull();
      });

      test('should only delete authorized envelopes when given mixed array of envelope IDs', async ({ request }) => {
        // Create documents owned by userA
        const docA1 = await seedBlankDocument(userA, teamA.id);
        const docA2 = await seedBlankDocument(userA, teamA.id);

        // Create a document owned by userB
        const docB = await seedBlankDocument(userB, teamB.id);

        // UserA tries to delete a mix of their own documents and userB's document
        const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/bulk/delete`, {
          headers: { Authorization: `Bearer ${tokenA}` },
          data: {
            envelopeIds: [docA1.id, docB.id, docA2.id],
          },
        });

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

        const body = await res.json();
        // Only userA's documents should be deleted
        expect(body.deletedCount).toBe(2);
        // Unauthorized envelope ID (docB) should be in failedIds
        expect(body.failedIds).toEqual([docB.id]);

        // Verify userA's documents were deleted
        const docA1InDb = await prisma.envelope.findFirst({
          where: { id: docA1.id },
        });
        expect(docA1InDb).toBeNull();

        const docA2InDb = await prisma.envelope.findFirst({
          where: { id: docA2.id },
        });
        expect(docA2InDb).toBeNull();

        // Verify userB's document was NOT deleted
        const docBInDb = await prisma.envelope.findFirst({
          where: { id: docB.id },
        });
        expect(docBInDb).not.toBeNull();
        expect(docBInDb?.id).toBe(docB.id);
        expect(docBInDb?.deletedAt).toBeNull();
      });

      test('should delete zero envelopes when all envelope IDs in array are unauthorized', async ({ request }) => {
        // Create documents owned by userB
        const docB1 = await seedBlankDocument(userB, teamB.id);
        const docB2 = await seedBlankDocument(userB, teamB.id);

        // UserA tries to delete userB's documents
        const res = await request.post(`${WEBAPP_BASE_URL}/api/v2-beta/envelope/bulk/delete`, {
          headers: { Authorization: `Bearer ${tokenA}` },
          data: {
            envelopeIds: [docB1.id, docB2.id],
          },
        });

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

        const body = await res.json();
        expect(body.deletedCount).toBe(0);
        // All unauthorized envelope IDs should be in failedIds
        expect(body.failedIds).toEqual(expect.arrayContaining([docB1.id, docB2.id]));
        expect(body.failedIds).toHaveLength(2);

        // Verify userB's documents were NOT deleted
        const docB1InDb = await prisma.envelope.findFirst({
          where: { id: docB1.id },
        });
        expect(docB1InDb).not.toBeNull();
        expect(docB1InDb?.id).toBe(docB1.id);
        expect(docB1InDb?.deletedAt).toBeNull();

        const docB2InDb = await prisma.envelope.findFirst({
          where: { id: docB2.id },
        });
        expect(docB2InDb).not.toBeNull();
        expect(docB2InDb?.id).toBe(docB2.id);
        expect(docB2InDb?.deletedAt).toBeNull();
      });
    });
  });
