// src/tests/e2e/flyer-upload.e2e.test.ts
import { describe, it, expect, afterAll } from 'vitest';
import crypto from 'crypto';
import * as apiClient from '../../services/apiClient';
import { getPool } from '../../services/db/connection.db';
import path from 'path';
import fs from 'fs';
import { cleanupDb } from '../utils/cleanup';
import { poll } from '../utils/poll';

/**
 * @vitest-environment node
 */

describe('E2E Flyer Upload and Processing Workflow', () => {
  const uniqueId = Date.now();
  const userEmail = `e2e-uploader-${uniqueId}@example.com`;
  const userPassword = 'StrongPassword123!';

  let authToken: string;
  let userId: string | null = null;
  let flyerId: number | null = null;
  let storeId: number | null = null;

  afterAll(async () => {
    // Use the centralized cleanup utility for robustness.
    await cleanupDb({
      userIds: [userId],
      flyerIds: [flyerId],
      storeIds: [storeId],
    });
  });

  it('should allow a user to upload a flyer and wait for processing to complete', async () => {
    // 1. Register a new user
    const registerResponse = await apiClient.registerUser(
      userEmail,
      userPassword,
      'E2E Flyer Uploader',
    );
    expect(registerResponse.status).toBe(201);

    // 2. Login to get the access token
    const loginResponse = await apiClient.loginUser(userEmail, userPassword, false);
    expect(loginResponse.status).toBe(200);
    const loginResponseBody = await loginResponse.json();
    authToken = loginResponseBody.data.token;
    userId = loginResponseBody.data.userprofile.user.user_id;
    expect(authToken).toBeDefined();

    // 3. Prepare the flyer file
    // We try to use the existing test asset if available, otherwise create a dummy buffer.
    // Note: In a real E2E scenario against a live AI service, a valid image is required.
    // If the AI service is mocked or stubbed in this environment, a dummy buffer might suffice.
    let fileBuffer: Buffer;
    const fileName = `e2e-test-flyer-${uniqueId}.jpg`;

    const assetPath = path.resolve(__dirname, '../assets/test-flyer-image.jpg');
    if (fs.existsSync(assetPath)) {
      const rawBuffer = fs.readFileSync(assetPath);
      // Append unique ID to ensure unique checksum for every test run
      fileBuffer = Buffer.concat([rawBuffer, Buffer.from(uniqueId.toString())]);
    } else {
      // Fallback to a minimal valid JPEG header + random data if asset is missing
      // (This might fail if the backend does strict image validation/processing)
      fileBuffer = Buffer.concat([
        Buffer.from([0xff, 0xd8, 0xff, 0xe0]), // JPEG Start of Image
        Buffer.from(uniqueId.toString()),
      ]);
    }

    // Create a File object for the apiClient
    // FIX: The Node.js `Buffer` type can be incompatible with the web `File` API's
    // expected `BlobPart` type in some TypeScript configurations. Explicitly creating
    // a `Uint8Array` from the buffer ensures compatibility and resolves the type error.
    // `Uint8Array` is a valid `BufferSource`, which is a valid `BlobPart`.
    const flyerFile = new File([new Uint8Array(fileBuffer)], fileName, { type: 'image/jpeg' });

    // Calculate checksum (required by the API)
    const checksum = crypto.createHash('sha256').update(fileBuffer).digest('hex');

    // 4. Upload the flyer
    const uploadResponse = await apiClient.uploadAndProcessFlyer(flyerFile, checksum, authToken);

    expect(uploadResponse.status).toBe(202);
    const uploadResponseBody = await uploadResponse.json();
    const jobId = uploadResponseBody.data.jobId;
    expect(jobId).toBeDefined();

    // 5. Poll for job completion using the new utility
    const jobStatusResponse = await poll(
      async () => {
        const statusResponse = await apiClient.getJobStatus(jobId, authToken);
        return statusResponse.json();
      },
      (responseBody) =>
        responseBody.data.state === 'completed' || responseBody.data.state === 'failed',
      { timeout: 180000, interval: 3000, description: 'flyer processing job completion' },
    );

    const jobStatus = jobStatusResponse.data;
    if (jobStatus.state === 'failed') {
      // Log the failure reason for easier debugging in CI/CD environments.
      console.error('E2E flyer processing job failed. Reason:', jobStatus.failedReason);
    }

    expect(jobStatus.state).toBe('completed');
    flyerId = jobStatus.returnValue?.flyerId;
    expect(flyerId).toBeTypeOf('number');

    // Fetch the store_id associated with the created flyer for robust cleanup
    if (flyerId) {
      const flyerRes = await getPool().query(
        'SELECT store_id FROM public.flyers WHERE flyer_id = $1',
        [flyerId],
      );
      if (flyerRes.rows.length > 0) {
        storeId = flyerRes.rows[0].store_id;
      }
    }
  }, 240000); // Extended timeout for AI processing
});
