/**
 * Task 11 - backfill-document-folders integration tests.
 *
 * Five cases:
 *   1. Creates system roots and entity subfolders.
 *   2. Sets files.folder_id from entity FKs.
 *   3. Copies entity FKs from completed workflows onto signed files.
 *   4. Idempotent - second run produces the same result.
 *   5. Port isolation - does not touch other ports.
 */

import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
import { and, eq } from 'drizzle-orm';

import { db } from '@/lib/db';
import { documentFolders, files, documents } from '@/lib/db/schema/documents';
import { user } from '@/lib/db/schema/users';
import { runBackfill } from '../../scripts/backfill-document-folders';
import { makePort, makeClient } from '../helpers/factories';

let TEST_USER_ID = '';

beforeAll(async () => {
  const [u] = await db.select({ id: user.id }).from(user).limit(1);
  if (!u) throw new Error('No user available; run pnpm db:seed first');
  TEST_USER_ID = u.id;
});

describe('backfill-document-folders · runBackfill', () => {
  let portId: string;

  beforeEach(async () => {
    const port = await makePort();
    portId = port.id;
    // Clean up any folders left by a prior test on this port.
    await db.delete(documentFolders).where(eq(documentFolders.portId, portId));
  });

  // ── Test 1: Creates system roots and entity subfolders ─────────────────────

  it('creates the three system roots and a client entity subfolder', async () => {
    const client = await makeClient({ portId });

    // Insert a file linked to the client so the backfill creates the subfolder.
    await db.insert(files).values({
      portId,
      clientId: client.id,
      filename: 'test.pdf',
      originalName: 'test.pdf',
      storagePath: `${portId}/test.pdf`,
      storageBucket: 'crm-files',
      uploadedBy: TEST_USER_ID,
    });

    await runBackfill({ portId, systemUserId: TEST_USER_ID });

    const roots = await db
      .select()
      .from(documentFolders)
      .where(and(eq(documentFolders.portId, portId), eq(documentFolders.entityType, 'root')));

    expect(roots).toHaveLength(3);
    const rootNames = roots.map((r) => r.name).sort();
    expect(rootNames).toEqual(['Clients', 'Companies', 'Yachts']);

    const entityFolder = await db.query.documentFolders.findFirst({
      where: and(
        eq(documentFolders.portId, portId),
        eq(documentFolders.entityType, 'client'),
        eq(documentFolders.entityId, client.id),
      ),
    });
    expect(entityFolder).toBeDefined();
    expect(entityFolder?.entityType).toBe('client');
    expect(entityFolder?.entityId).toBe(client.id);
  });

  // ── Test 2: Sets files.folder_id from entity FKs ────────────────────────────

  it('sets files.folder_id for files that have entity FKs but no folder_id', async () => {
    const client = await makeClient({ portId });

    const [fileRow] = await db
      .insert(files)
      .values({
        portId,
        clientId: client.id,
        filename: 'contract.pdf',
        originalName: 'contract.pdf',
        storagePath: `${portId}/contract.pdf`,
        storageBucket: 'crm-files',
        uploadedBy: TEST_USER_ID,
        // folderId intentionally omitted → null
      })
      .returning();

    expect(fileRow!.folderId).toBeNull();

    await runBackfill({ portId, systemUserId: TEST_USER_ID });

    const updated = await db.query.files.findFirst({
      where: and(eq(files.id, fileRow!.id), eq(files.portId, portId)),
    });

    expect(updated!.folderId).not.toBeNull();

    // The assigned folder must be the client's entity subfolder.
    const entityFolder = await db.query.documentFolders.findFirst({
      where: and(
        eq(documentFolders.portId, portId),
        eq(documentFolders.entityType, 'client'),
        eq(documentFolders.entityId, client.id),
      ),
    });
    expect(updated!.folderId).toBe(entityFolder!.id);
  });

  // ── Test 3: Copies entity FKs from completed workflows onto signed files ────

  it('propagates entity FKs from completed workflow onto signed file and sets folder_id', async () => {
    const client = await makeClient({ portId });

    // File row with no entity FK (simulating a legacy completed-before-auto-deposit).
    const [signedFile] = await db
      .insert(files)
      .values({
        portId,
        // No clientId set - simulates legacy completion before entity FK auto-propagation.
        filename: 'signed-eoi.pdf',
        originalName: 'signed-eoi.pdf',
        storagePath: `${portId}/signed-eoi.pdf`,
        storageBucket: 'crm-files',
        uploadedBy: TEST_USER_ID,
      })
      .returning();

    expect(signedFile!.clientId).toBeNull();

    // Completed workflow document pointing at the orphaned file.
    await db.insert(documents).values({
      portId,
      documentType: 'eoi',
      title: 'EOI for client',
      status: 'completed',
      signedFileId: signedFile!.id,
      clientId: client.id,
      createdBy: TEST_USER_ID,
    });

    await runBackfill({ portId, systemUserId: TEST_USER_ID });

    const updatedFile = await db.query.files.findFirst({
      where: and(eq(files.id, signedFile!.id), eq(files.portId, portId)),
    });

    // The backfill should have set the clientId on the signed file.
    expect(updatedFile!.clientId).toBe(client.id);
    // And then assigned it to the client's entity subfolder.
    expect(updatedFile!.folderId).not.toBeNull();
  });

  // ── Test 4: Idempotent ────────────────────────────────────────────────────────

  it('is idempotent - running twice produces the same number of folder rows', async () => {
    const client = await makeClient({ portId });

    await db.insert(files).values({
      portId,
      clientId: client.id,
      filename: 'idempotent.pdf',
      originalName: 'idempotent.pdf',
      storagePath: `${portId}/idempotent.pdf`,
      storageBucket: 'crm-files',
      uploadedBy: TEST_USER_ID,
    });

    await runBackfill({ portId, systemUserId: TEST_USER_ID });

    const countAfterFirst = await db
      .select()
      .from(documentFolders)
      .where(eq(documentFolders.portId, portId));

    await runBackfill({ portId, systemUserId: TEST_USER_ID });

    const countAfterSecond = await db
      .select()
      .from(documentFolders)
      .where(eq(documentFolders.portId, portId));

    expect(countAfterSecond).toHaveLength(countAfterFirst.length);
  });

  // ── Test 5: Port isolation ────────────────────────────────────────────────────

  it('does not create folders for a different port when only portId is supplied', async () => {
    const otherPort = await makePort();
    await db.delete(documentFolders).where(eq(documentFolders.portId, otherPort.id));

    const otherClient = await makeClient({ portId: otherPort.id });

    await db.insert(files).values({
      portId: otherPort.id,
      clientId: otherClient.id,
      filename: 'other-port.pdf',
      originalName: 'other-port.pdf',
      storagePath: `${otherPort.id}/other-port.pdf`,
      storageBucket: 'crm-files',
      uploadedBy: TEST_USER_ID,
    });

    // Run backfill only for the main portId, NOT the otherPort.
    await runBackfill({ portId, systemUserId: TEST_USER_ID });

    const otherPortFolders = await db
      .select()
      .from(documentFolders)
      .where(eq(documentFolders.portId, otherPort.id));

    // The other port should have zero folders - the backfill was not run for it.
    expect(otherPortFolders).toHaveLength(0);
  });
});
