import { describe, it, expect, vi, beforeEach } from 'vitest';

// Validation-path tests for uploadDocumentForSigning. The
// heavy-integration paths (storage put, Documenso round-trip, signer
// updates) are exercised by Playwright realapi specs; these tests
// pin the input-validation contract so a regression in the
// recipient/field/PDF guards is caught at unit-test time.

// Stub the heavy dependencies BEFORE importing the service so its
// module-level imports resolve to the stubs.
vi.mock('@/lib/db', () => ({
  db: {
    query: {
      interests: {
        findFirst: vi.fn().mockResolvedValue({ id: 'int-1', portId: 'port-1', clientId: 'c-1' }),
      },
      ports: { findFirst: vi.fn().mockResolvedValue({ id: 'port-1', name: 'Test Port' }) },
    },
  },
}));

vi.mock('@/lib/services/berth-pdf-parser', () => ({
  isPdfMagic: (b: Buffer) => b.slice(0, 5).toString() === '%PDF-',
}));

import {
  uploadDocumentForSigning,
  type CustomDocumentRecipient,
} from '@/lib/services/custom-document-upload.service';

const PDF_HEADER = Buffer.from('%PDF-1.7\n');
const NON_PDF = Buffer.from('this is not a PDF');

const baseArgs = {
  interestId: 'int-1',
  portId: 'port-1',
  portSlug: 'test-port',
  documentType: 'contract' as const,
  title: 'Sales Contract',
  pdfBuffer: PDF_HEADER,
  filename: 'contract.pdf',
  recipients: [
    { name: 'Buyer', email: 'buyer@example.com', role: 'SIGNER', signingOrder: 1 },
    { name: 'Seller', email: 'seller@example.com', role: 'SIGNER', signingOrder: 2 },
  ] satisfies CustomDocumentRecipient[],
  fields: [
    {
      recipientIndex: 0,
      type: 'SIGNATURE' as const,
      pageNumber: 1,
      pageX: 10,
      pageY: 80,
      pageWidth: 30,
      pageHeight: 5,
    },
  ],
  meta: {
    userId: 'user-1',
    portId: 'port-1',
    ipAddress: '127.0.0.1',
    userAgent: 'test',
  },
};

describe('uploadDocumentForSigning validation', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('rejects empty recipient list', async () => {
    await expect(uploadDocumentForSigning({ ...baseArgs, recipients: [] })).rejects.toThrow(
      /at least one recipient/i,
    );
  });

  it('rejects empty field list', async () => {
    await expect(uploadDocumentForSigning({ ...baseArgs, fields: [] })).rejects.toThrow(
      /at least one field/i,
    );
  });

  it('rejects empty PDF buffer', async () => {
    await expect(
      uploadDocumentForSigning({ ...baseArgs, pdfBuffer: Buffer.alloc(0) }),
    ).rejects.toThrow(/PDF buffer is empty/);
  });

  it('rejects oversized PDF', async () => {
    const oversized = Buffer.alloc(51 * 1024 * 1024, 0x20);
    oversized.write('%PDF-1.7', 0);
    await expect(uploadDocumentForSigning({ ...baseArgs, pdfBuffer: oversized })).rejects.toThrow(
      /exceeds.*MB cap/i,
    );
  });

  it('rejects non-PDF magic bytes', async () => {
    await expect(uploadDocumentForSigning({ ...baseArgs, pdfBuffer: NON_PDF })).rejects.toThrow(
      /not a PDF/,
    );
  });

  it('rejects out-of-range recipientIndex on a field', async () => {
    await expect(
      uploadDocumentForSigning({
        ...baseArgs,
        fields: [{ ...baseArgs.fields[0]!, recipientIndex: 5 }],
      }),
    ).rejects.toThrow(/out of range/);
  });

  it('rejects negative recipientIndex on a field', async () => {
    await expect(
      uploadDocumentForSigning({
        ...baseArgs,
        fields: [{ ...baseArgs.fields[0]!, recipientIndex: -1 }],
      }),
    ).rejects.toThrow(/out of range/);
  });

  it('rejects duplicate signingOrder across recipients', async () => {
    await expect(
      uploadDocumentForSigning({
        ...baseArgs,
        recipients: [
          { name: 'A', email: 'a@x.com', role: 'SIGNER', signingOrder: 1 },
          { name: 'B', email: 'b@x.com', role: 'SIGNER', signingOrder: 1 },
        ],
      }),
    ).rejects.toThrow(/Duplicate signingOrder/);
  });

  // P1.2 — pre-flight validation extends to recipient email + name
  // shape. Service mirrors the dialog's pre-Submit checks so direct API
  // hits also reject early.

  it('rejects recipient with blank email', async () => {
    await expect(
      uploadDocumentForSigning({
        ...baseArgs,
        recipients: [
          { name: 'Buyer', email: '', role: 'SIGNER', signingOrder: 1 },
          { name: 'Seller', email: 'seller@example.com', role: 'SIGNER', signingOrder: 2 },
        ],
      }),
    ).rejects.toThrow(/missing an email/i);
  });

  it('rejects recipient with whitespace-only email', async () => {
    await expect(
      uploadDocumentForSigning({
        ...baseArgs,
        recipients: [
          { name: 'Buyer', email: '   ', role: 'SIGNER', signingOrder: 1 },
          { name: 'Seller', email: 'seller@example.com', role: 'SIGNER', signingOrder: 2 },
        ],
      }),
    ).rejects.toThrow(/missing an email/i);
  });

  it('rejects recipient with malformed email', async () => {
    await expect(
      uploadDocumentForSigning({
        ...baseArgs,
        recipients: [
          { name: 'Buyer', email: 'not-an-email', role: 'SIGNER', signingOrder: 1 },
          { name: 'Seller', email: 'seller@example.com', role: 'SIGNER', signingOrder: 2 },
        ],
      }),
    ).rejects.toThrow(/invalid email/i);
  });

  it('rejects recipient with blank name', async () => {
    await expect(
      uploadDocumentForSigning({
        ...baseArgs,
        recipients: [
          { name: '', email: 'buyer@example.com', role: 'SIGNER', signingOrder: 1 },
          { name: 'Seller', email: 'seller@example.com', role: 'SIGNER', signingOrder: 2 },
        ],
      }),
    ).rejects.toThrow(/missing a name/i);
  });

  it('accepts duplicate emails across recipients (Documenso dedupes by email)', async () => {
    // The validation guard does NOT reject same-email recipients — at
    // the field-placement step the email→recipientId map collapses them
    // to a single Documenso recipientId by design. Other guards (PDF,
    // recipient row insert, Documenso round-trip) prevent this test
    // from reaching success in unit-mode; we only assert that the
    // recipient-validation block does NOT throw early.
    await expect(
      uploadDocumentForSigning({
        ...baseArgs,
        recipients: [
          { name: 'Buyer One', email: 'shared@example.com', role: 'SIGNER', signingOrder: 1 },
          { name: 'Buyer Two', email: 'shared@example.com', role: 'SIGNER', signingOrder: 2 },
        ],
      }),
    ).rejects.not.toThrow(/missing an email|invalid email|missing a name/i);
  });
});
