// src/tests/integration/ai.integration.test.ts
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import supertest from 'supertest';
import app from '../../../server';
import fs from 'node:fs/promises';
import path from 'path';
import { createAndLoginUser } from '../utils/testHelpers';
import { cleanupDb } from '../utils/cleanup';
import { cleanupFiles } from '../utils/cleanupFiles';

/**
 * @vitest-environment node
 */

const request = supertest(app);

interface TestGeolocationCoordinates {
  latitude: number;
  longitude: number;
  accuracy: number;
  altitude: number | null;
  altitudeAccuracy: number | null;
  heading: number | null;
  speed: number | null;
  toJSON(): object;
}

describe('AI API Routes Integration Tests', () => {
  let authToken: string;
  let testUserId: string;

  beforeAll(async () => {
    // Create and log in as a new user for authenticated tests.
    const { token, user } = await createAndLoginUser({ fullName: 'AI Tester', request });
    authToken = token;
    testUserId = user.user.user_id;
  });

  afterAll(async () => {
    // 1. Clean up database records
    await cleanupDb({ userIds: [testUserId] });

    // 2. Safeguard: Clean up any leftover files from failed tests.
    // The routes themselves should clean up on success, but this handles interruptions.
    const uploadDir = path.resolve(__dirname, '../../../flyer-images');
    try {
      const allFiles = await fs.readdir(uploadDir);
      const testFiles = allFiles
        .filter((f) => f.startsWith('image-') || f.startsWith('images-'))
        .map((f) => path.join(uploadDir, f));

      if (testFiles.length > 0) {
        await cleanupFiles(testFiles);
      }
    } catch (error) {
      if (error instanceof Error && (error as NodeJS.ErrnoException).code !== 'ENOENT') {
        console.error('Error during AI integration test file cleanup:', error);
      }
    }
  });

  it('POST /api/ai/check-flyer should return a boolean', async () => {
    const response = await request
      .post('/api/ai/check-flyer')
      .set('Authorization', `Bearer ${authToken}`)
      .attach('image', Buffer.from('content'), 'test.jpg');
    const result = response.body;
    expect(response.status).toBe(200);
    // The backend is stubbed to always return true for this check
    expect(result.is_flyer).toBe(true);
  });

  it('POST /api/ai/extract-address should return a stubbed address', async () => {
    const response = await request
      .post('/api/ai/extract-address')
      .set('Authorization', `Bearer ${authToken}`)
      .attach('image', Buffer.from('content'), 'test.jpg');
    const result = response.body;
    expect(response.status).toBe(200);
    expect(result.address).toBe('not identified');
  });

  it('POST /api/ai/extract-logo should return a stubbed response', async () => {
    const response = await request
      .post('/api/ai/extract-logo')
      .set('Authorization', `Bearer ${authToken}`)
      .attach('images', Buffer.from('content'), 'test.jpg');
    const result = response.body;
    expect(response.status).toBe(200);
    expect(result).toEqual({ store_logo_base_64: null });
  });

  it('POST /api/ai/quick-insights should return a stubbed insight', async () => {
    const response = await request
      .post('/api/ai/quick-insights')
      .set('Authorization', `Bearer ${authToken}`)
      .send({ items: [{ item: 'test' }] });
    const result = response.body;
    // DEBUG: Log response if it fails expectation
    if (response.status !== 200 || !result.text) {
      console.log('[DEBUG] POST /api/ai/quick-insights response:', response.status, response.body);
    }
    expect(response.status).toBe(200);
    expect(result.text).toBe('This is a server-generated quick insight: buy the cheap stuff!');
  });

  it('POST /api/ai/deep-dive should return a stubbed analysis', async () => {
    const response = await request
      .post('/api/ai/deep-dive')
      .set('Authorization', `Bearer ${authToken}`)
      .send({ items: [{ item: 'test' }] });
    const result = response.body;
    // DEBUG: Log response if it fails expectation
    if (response.status !== 200 || !result.text) {
      console.log('[DEBUG] POST /api/ai/deep-dive response:', response.status, response.body);
    }
    expect(response.status).toBe(200);
    expect(result.text).toBe('This is a server-generated deep dive analysis. It is very detailed.');
  });

  it('POST /api/ai/search-web should return a stubbed search result', async () => {
    const response = await request
      .post('/api/ai/search-web')
      .set('Authorization', `Bearer ${authToken}`)
      .send({ query: 'test query' });
    const result = response.body;
    // DEBUG: Log response if it fails expectation
    if (response.status !== 200 || !result.text) {
      console.log('[DEBUG] POST /api/ai/search-web response:', response.status, response.body);
    }
    expect(response.status).toBe(200);
    expect(result).toEqual({ text: 'The web says this is good.', sources: [] });
  });

  it('POST /api/ai/plan-trip should return an error as the feature is disabled', async () => {
    // The GeolocationCoordinates type requires more than just lat/lng.
    // We create a complete mock object to satisfy the type.
    const mockLocation: TestGeolocationCoordinates = {
      latitude: 48.4284,
      longitude: -123.3656,
      accuracy: 100,
      altitude: null,
      altitudeAccuracy: null,
      heading: null,
      speed: null,
      toJSON: function () {
        return {
          latitude: this.latitude,
          longitude: this.longitude,
          accuracy: this.accuracy,
          altitude: this.altitude,
          altitudeAccuracy: this.altitudeAccuracy,
          heading: this.heading,
          speed: this.speed,
        };
      },
    };
    const mockStore = {
      name: 'Test Store for Trip',
      store_id: 1,
      created_at: new Date().toISOString(),
      updated_at: new Date().toISOString(),
    };
    const response = await request
      .post('/api/ai/plan-trip')
      .set('Authorization', `Bearer ${authToken}`)
      .send({ items: [], store: mockStore, userLocation: mockLocation });
    // The service for this endpoint is disabled and throws an error, which results in a 500.
    // DEBUG: Log response if it fails expectation
    if (response.status !== 500) {
      console.log('[DEBUG] POST /api/ai/plan-trip response:', response.status, response.body);
    }
    expect(response.status).toBe(500);
    const errorResult = response.body;
    expect(errorResult.message).toContain('planTripWithMaps');
  });

  it('POST /api/ai/generate-image should reject because it is not implemented', async () => {
    // The backend for this is not stubbed and will throw an error.
    // This test confirms that the endpoint is protected and responds as expected to a failure.
    const response = await request
      .post('/api/ai/generate-image')
      .set('Authorization', `Bearer ${authToken}`)
      .send({ prompt: 'a test prompt' });
    expect(response.status).toBe(501);
  });

  it('POST /api/ai/generate-speech should reject because it is not implemented', async () => {
    // The backend for this is not stubbed and will throw an error.
    const response = await request
      .post('/api/ai/generate-speech')
      .set('Authorization', `Bearer ${authToken}`)
      .send({ text: 'a test prompt' });
    expect(response.status).toBe(501);
  });
});
