import { test, expect } from '@playwright/test';
import { login, navigateTo, apiHeaders } from './helpers';
import path from 'path';
import fs from 'fs';

/**
 * Smoke spec - Upload a file into an entity folder (Wave 11.B)
 *
 * Strategy: Option B (API-fixture approach).
 *
 * The EntityFolderView's upload button is not wired into the new hub UI -
 * the hub focuses on aggregated read + signing. We therefore drive the
 * upload path via the API directly (`page.request`) and then assert that
 * the file surfaces in the entity folder view.
 *
 * This is the principled approach: it tests the behaviour (file appears in
 * entity view after upload) rather than a specific UI affordance.
 */

test.describe('Documents hub - upload into entity folder', () => {
  test.beforeEach(async ({ page }) => {
    await login(page, 'super_admin');
  });

  test('file uploaded with clientId appears in the client entity folder view', async ({ page }) => {
    const headers = await apiHeaders(page);

    // 1. Create a client.
    // page.request shares the browser context cookie jar (session cookie from
    // login()) - the bare `request` fixture is an isolated API context that
    // does not carry the session cookie and would 401.
    const clientRes = await page.request.post('/api/v1/clients', {
      headers,
      data: {
        firstName: 'UploadSmoke',
        lastName: `Test${Date.now()}`,
        email: `uploadsmoke${Date.now()}@e2e.test`,
      },
    });
    // Playwright doesn't expose vitest's `expect.fail`; throw to fail loud.
    if (!clientRes.ok()) {
      throw new Error(
        `Client create returned ${clientRes.status()} ${await clientRes.text()} - upload test cannot proceed`,
      );
    }
    const { data: client } = (await clientRes.json()) as {
      data: { id: string; firstName: string; lastName: string };
    };

    // 2. Upload a file associated with this client via the multipart upload endpoint.
    //    The upload validator accepts `clientId` to associate the file with the client.
    //    ensureEntityFolder is called by the files service after upload to guarantee
    //    the folder exists.
    const fixturePath = path.resolve('tests/e2e/fixtures/test-document.txt');
    const fileBuffer = fs.readFileSync(fixturePath);

    const uploadForm = new FormData();
    uploadForm.append('file', new Blob([fileBuffer], { type: 'text/plain' }), 'test-document.txt');
    uploadForm.append('filename', 'test-document.txt');
    uploadForm.append('clientId', client.id);

    // Use fetch directly because Playwright's request doesn't yet have great
    // multipart support for Buffer payloads.  The cookie is carried by the
    // browser context's cookie jar automatically when we use `page.request`.
    const uploadRes = await page.request.fetch('/api/v1/files/upload', {
      method: 'POST',
      headers: {
        'X-Port-Id': headers['X-Port-Id']!,
      },
      multipart: {
        file: {
          name: 'test-document.txt',
          mimeType: 'text/plain',
          buffer: Buffer.from(fileBuffer),
        },
        filename: 'test-document.txt',
        clientId: client.id,
      },
    });

    expect(uploadRes.ok(), `Upload failed: ${uploadRes.status()} ${await uploadRes.text()}`).toBe(
      true,
    );
    const uploadBody = (await uploadRes.json()) as { data: { id: string; filename: string } };
    const uploadedFileId = uploadBody.data.id;
    expect(uploadedFileId).toBeTruthy();

    // 3. Navigate to the documents hub.
    await navigateTo(page, '/documents');
    await page.waitForLoadState('networkidle');

    // 4. Expand the Clients system root.
    const expandBtn = page.locator('aside').getByRole('button', { name: 'Expand' }).first();
    await expect(expandBtn).toBeVisible({ timeout: 10_000 });
    await expandBtn.click();

    // 5. Click the entity folder for the client we created.
    const entityFolderBtn = page
      .locator('aside button')
      .filter({ hasText: `${client.firstName} ${client.lastName}` })
      .first();
    await expect(entityFolderBtn).toBeVisible({ timeout: 10_000 });
    await entityFolderBtn.click();

    // 6. The EntityFolderView should show the Files section with at least one row.
    const filesSection = page.getByText('Files').first();
    await expect(filesSection).toBeVisible({ timeout: 10_000 });

    // The file we uploaded should appear somewhere in the entity view.
    // AggregatedSection renders filenames in <span> elements inside <li> rows.
    await expect(page.getByText('test-document.txt')).toBeVisible({ timeout: 10_000 });
  });

  test('file uploaded with folderId (entity folder) auto-sets client_id', async ({ page }) => {
    const headers = await apiHeaders(page);

    // 1. Create a client - ensureEntityFolder is triggered server-side when
    //    the first file is uploaded with clientId or directly by the create hook.
    // page.request shares the browser context cookie jar (session cookie from
    // login()) - the bare `request` fixture is an isolated API context that
    // does not carry the session cookie and would 401.
    const clientRes = await page.request.post('/api/v1/clients', {
      headers,
      data: {
        firstName: 'FolderIdSmoke',
        lastName: `Test${Date.now()}`,
        email: `folderidsmoke${Date.now()}@e2e.test`,
      },
    });
    if (!clientRes.ok()) {
      throw new Error(
        `Client create returned ${clientRes.status()} ${await clientRes.text()} - folderId test cannot proceed`,
      );
    }
    const { data: client } = (await clientRes.json()) as {
      data: { id: string; firstName: string; lastName: string };
    };

    // 2. Obtain the entity folder id for this client by uploading once with
    //    clientId to trigger ensureEntityFolder, then listing files.
    const seedUpload = await page.request.fetch('/api/v1/files/upload', {
      method: 'POST',
      headers: { 'X-Port-Id': headers['X-Port-Id']! },
      multipart: {
        file: {
          name: 'seed.txt',
          mimeType: 'text/plain',
          buffer: Buffer.from('seed'),
        },
        filename: 'seed.txt',
        clientId: client.id,
      },
    });
    // Seed upload failing means the files API is broken - fail loud so the
    // infra regression surfaces in CI instead of staying green-skipped.
    if (!seedUpload.ok()) {
      throw new Error(
        `Seed upload returned ${seedUpload.status()} ${await seedUpload.text()} - folderId test cannot proceed`,
      );
    }

    // 3. List files for this client to discover the folder id.
    const listRes = await page.request.get(
      `/api/v1/files?entityType=client&entityId=${client.id}`,
      {
        headers,
      },
    );
    if (!listRes.ok()) {
      throw new Error(
        `File list returned ${listRes.status()} ${await listRes.text()} - folderId test cannot proceed`,
      );
    }

    // 4. Navigate and verify - folder view shows the client entity sections.
    await navigateTo(page, '/documents');
    await page.waitForLoadState('networkidle');

    // Expand Clients and click entity folder.
    const expandBtn = page.locator('aside').getByRole('button', { name: 'Expand' }).first();
    await expect(expandBtn).toBeVisible({ timeout: 10_000 });
    await expandBtn.click();

    const entityFolderBtn = page
      .locator('aside button')
      .filter({ hasText: `${client.firstName} ${client.lastName}` })
      .first();
    await expect(entityFolderBtn).toBeVisible({ timeout: 10_000 });
    await entityFolderBtn.click();

    // Entity folder view is rendered (Signing + Files sections).
    await expect(page.getByText('Signing in progress')).toBeVisible({ timeout: 10_000 });
    await expect(page.getByText('Files')).toBeVisible({ timeout: 5_000 });

    // The seeded file appears.
    await expect(page.getByText('seed.txt')).toBeVisible({ timeout: 10_000 });
  });
});
