/**
 * @ai-summary Integration tests for catalog CRUD operations
 * @ai-context Tests complete workflows for makes, models, years, trims, engines
 */

import { FastifyInstance } from 'fastify';
import { buildApp } from '../../../../app';
import { pool } from '../../../../core/config/database';
import { redis } from '../../../../core/config/redis';

describe('Admin Catalog Integration Tests', () => {
  let app: FastifyInstance;
  let adminToken: string;
  let nonAdminToken: string;

  beforeAll(async () => {
    app = await buildApp();
    await app.ready();

    // Create admin user for testing
    await pool.query(`
      INSERT INTO admin_users (auth0_sub, email, role, created_by)
      VALUES ('test-admin-123', 'admin@test.com', 'admin', 'system')
      ON CONFLICT (auth0_sub) DO NOTHING
    `);

    // Generate tokens (mock JWT for testing)
    adminToken = app.jwt.sign({ sub: 'test-admin-123', email: 'admin@test.com' });
    nonAdminToken = app.jwt.sign({ sub: 'regular-user', email: 'user@test.com' });
  });

  afterAll(async () => {
    // Clean up test data
    await pool.query(`DELETE FROM platform_change_log WHERE changed_by LIKE 'test-%'`);
    await pool.query(`DELETE FROM vehicle_dropdown_cache WHERE cache_key LIKE 'catalog:%'`);
    await pool.query(`DELETE FROM admin_users WHERE auth0_sub LIKE 'test-%'`);
    await redis.quit();
    await app.close();
  });

  afterEach(async () => {
    // Clear catalog data between tests
    await pool.query(`DELETE FROM vehicle_dropdown_cache WHERE cache_key LIKE 'catalog:%'`);
    await pool.query(`DELETE FROM platform_change_log WHERE changed_by = 'test-admin-123'`);
  });

  describe('Permission Enforcement', () => {
    it('should reject non-admin access to catalog endpoints', async () => {
      const response = await app.inject({
        method: 'GET',
        url: '/api/admin/catalog/makes',
        headers: {
          authorization: `Bearer ${nonAdminToken}`
        }
      });

      expect(response.statusCode).toBe(403);
    });

    it('should allow admin access to catalog endpoints', async () => {
      const response = await app.inject({
        method: 'GET',
        url: '/api/admin/catalog/makes',
        headers: {
          authorization: `Bearer ${adminToken}`
        }
      });

      expect(response.statusCode).toBe(200);
    });
  });

  describe('Makes CRUD Operations', () => {
    it('should create a new make', async () => {
      const response = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/makes',
        headers: {
          authorization: `Bearer ${adminToken}`
        },
        payload: {
          name: 'Honda'
        }
      });

      expect(response.statusCode).toBe(201);
      const make = JSON.parse(response.payload);
      expect(make.id).toBeDefined();
      expect(make.name).toBe('Honda');
    });

    it('should list all makes', async () => {
      // Create test makes
      await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'Toyota' }
      });

      await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'Ford' }
      });

      const response = await app.inject({
        method: 'GET',
        url: '/api/admin/catalog/makes',
        headers: {
          authorization: `Bearer ${adminToken}`
        }
      });

      expect(response.statusCode).toBe(200);
      const data = JSON.parse(response.payload);
      expect(data.makes.length).toBe(2);
    });

    it('should update a make', async () => {
      // Create make
      const createResponse = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'Honda' }
      });

      const make = JSON.parse(createResponse.payload);

      // Update make
      const updateResponse = await app.inject({
        method: 'PUT',
        url: `/api/admin/catalog/makes/${make.id}`,
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'Honda Motors' }
      });

      expect(updateResponse.statusCode).toBe(200);
      const updated = JSON.parse(updateResponse.payload);
      expect(updated.name).toBe('Honda Motors');
    });

    it('should delete a make', async () => {
      // Create make
      const createResponse = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'TestMake' }
      });

      const make = JSON.parse(createResponse.payload);

      // Delete make
      const deleteResponse = await app.inject({
        method: 'DELETE',
        url: `/api/admin/catalog/makes/${make.id}`,
        headers: { authorization: `Bearer ${adminToken}` }
      });

      expect(deleteResponse.statusCode).toBe(204);

      // Verify deletion
      const listResponse = await app.inject({
        method: 'GET',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` }
      });

      const data = JSON.parse(listResponse.payload);
      expect(data.makes.find((m: any) => m.id === make.id)).toBeUndefined();
    });

    it('should prevent deleting make with existing models', async () => {
      // Create make
      const makeResponse = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'Honda' }
      });

      const make = JSON.parse(makeResponse.payload);

      // Create model
      await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/models',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { makeId: make.id, name: 'Civic' }
      });

      // Try to delete make
      const deleteResponse = await app.inject({
        method: 'DELETE',
        url: `/api/admin/catalog/makes/${make.id}`,
        headers: { authorization: `Bearer ${adminToken}` }
      });

      expect(deleteResponse.statusCode).toBe(409);
    });
  });

  describe('Models CRUD Operations', () => {
    let makeId: number;

    beforeEach(async () => {
      // Create make for testing
      const response = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'Toyota' }
      });

      makeId = JSON.parse(response.payload).id;
    });

    it('should create a new model', async () => {
      const response = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/models',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { makeId, name: 'Camry' }
      });

      expect(response.statusCode).toBe(201);
      const model = JSON.parse(response.payload);
      expect(model.makeId).toBe(makeId);
      expect(model.name).toBe('Camry');
    });

    it('should list models for a make', async () => {
      // Create models
      await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/models',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { makeId, name: 'Camry' }
      });

      await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/models',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { makeId, name: 'Corolla' }
      });

      const response = await app.inject({
        method: 'GET',
        url: `/api/admin/catalog/makes/${makeId}/models`,
        headers: { authorization: `Bearer ${adminToken}` }
      });

      expect(response.statusCode).toBe(200);
      const data = JSON.parse(response.payload);
      expect(data.models.length).toBe(2);
    });

    it('should reject model creation with non-existent make', async () => {
      const response = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/models',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { makeId: 99999, name: 'InvalidModel' }
      });

      expect(response.statusCode).toBe(404);
    });
  });

  describe('Transaction Rollback', () => {
    it('should rollback transaction on error', async () => {
      // Create make
      const makeResponse = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'TestMake' }
      });

      const make = JSON.parse(makeResponse.payload);

      // Verify make exists
      const listBefore = await app.inject({
        method: 'GET',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` }
      });

      expect(JSON.parse(listBefore.payload).makes.length).toBe(1);

      // Try to create model with invalid makeId (should fail)
      await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/models',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { makeId: 99999, name: 'InvalidModel' }
      });

      // Verify make still exists (transaction didn't affect other data)
      const listAfter = await app.inject({
        method: 'GET',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` }
      });

      expect(JSON.parse(listAfter.payload).makes.length).toBe(1);
    });
  });

  describe('Cache Invalidation', () => {
    it('should invalidate cache after create operation', async () => {
      // Set a cache value
      await redis.set('mvp:platform:vehicle-data:makes:2024', JSON.stringify([]), 3600);
      await redis.set('mvp:platform:years', JSON.stringify([2024]), 3600);

      // Create make (should invalidate cache)
      await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'Honda' }
      });

      const cacheValue = await redis.get('mvp:platform:vehicle-data:makes:2024');
      const yearsCacheValue = await redis.get('mvp:platform:years');

      expect(cacheValue).toBeNull();
      expect(yearsCacheValue).toBeNull();
    });
  });

  describe('Change Log Recording', () => {
    it('should record CREATE action in change log', async () => {
      const response = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'Honda' }
      });

      const make = JSON.parse(response.payload);

      // Query change log
      const logResult = await pool.query(`
        SELECT * FROM platform_change_log
        WHERE resource_type = 'makes'
        AND resource_id = $1
        AND change_type = 'CREATE'
      `, [make.id.toString()]);

      expect(logResult.rows.length).toBe(1);
      expect(logResult.rows[0].changed_by).toBe('test-admin-123');
    });

    it('should record UPDATE action in change log', async () => {
      // Create make
      const createResponse = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'Honda' }
      });

      const make = JSON.parse(createResponse.payload);

      // Update make
      await app.inject({
        method: 'PUT',
        url: `/api/admin/catalog/makes/${make.id}`,
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'Honda Motors' }
      });

      // Query change log
      const logResult = await pool.query(`
        SELECT * FROM platform_change_log
        WHERE resource_type = 'makes'
        AND resource_id = $1
        AND change_type = 'UPDATE'
      `, [make.id.toString()]);

      expect(logResult.rows.length).toBe(1);
      expect(logResult.rows[0].old_value).toBeDefined();
      expect(logResult.rows[0].new_value).toBeDefined();
    });

    it('should record DELETE action in change log', async () => {
      // Create make
      const createResponse = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'TestMake' }
      });

      const make = JSON.parse(createResponse.payload);

      // Delete make
      await app.inject({
        method: 'DELETE',
        url: `/api/admin/catalog/makes/${make.id}`,
        headers: { authorization: `Bearer ${adminToken}` }
      });

      // Query change log
      const logResult = await pool.query(`
        SELECT * FROM platform_change_log
        WHERE resource_type = 'makes'
        AND resource_id = $1
        AND change_type = 'DELETE'
      `, [make.id.toString()]);

      expect(logResult.rows.length).toBe(1);
      expect(logResult.rows[0].old_value).toBeDefined();
    });
  });

  describe('Complete Workflow', () => {
    it('should handle complete catalog creation workflow', async () => {
      // Create make
      const makeResponse = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'Honda' }
      });

      const make = JSON.parse(makeResponse.payload);

      // Create model
      const modelResponse = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/models',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { makeId: make.id, name: 'Civic' }
      });

      const model = JSON.parse(modelResponse.payload);

      // Create year
      const yearResponse = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/years',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { modelId: model.id, year: 2024 }
      });

      const year = JSON.parse(yearResponse.payload);

      // Create trim
      const trimResponse = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/trims',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { yearId: year.id, name: 'LX' }
      });

      const trim = JSON.parse(trimResponse.payload);

      // Create engine
      const engineResponse = await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/engines',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: {
          trimId: trim.id,
          name: '2.0L I4',
          description: '158hp Turbocharged'
        }
      });

      const engine = JSON.parse(engineResponse.payload);

      // Verify all entities created
      expect(make.id).toBeDefined();
      expect(model.id).toBeDefined();
      expect(year.id).toBeDefined();
      expect(trim.id).toBeDefined();
      expect(engine.id).toBeDefined();

      // Verify change log has 5 entries
      const logResult = await pool.query(`
        SELECT COUNT(*) as count
        FROM platform_change_log
        WHERE changed_by = 'test-admin-123'
        AND change_type = 'CREATE'
      `);

      expect(parseInt(logResult.rows[0].count)).toBe(5);
    });
  });

  describe('Change Logs Endpoint', () => {
    it('should retrieve change logs', async () => {
      // Create some changes
      await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'Honda' }
      });

      await app.inject({
        method: 'POST',
        url: '/api/admin/catalog/makes',
        headers: { authorization: `Bearer ${adminToken}` },
        payload: { name: 'Toyota' }
      });

      // Retrieve logs
      const response = await app.inject({
        method: 'GET',
        url: '/api/admin/catalog/change-logs',
        headers: { authorization: `Bearer ${adminToken}` }
      });

      expect(response.statusCode).toBe(200);
      const data = JSON.parse(response.payload);
      expect(data.logs.length).toBeGreaterThanOrEqual(2);
      expect(data.total).toBeGreaterThanOrEqual(2);
    });

    it('should support pagination', async () => {
      // Create changes
      for (let i = 0; i < 5; i++) {
        await app.inject({
          method: 'POST',
          url: '/api/admin/catalog/makes',
          headers: { authorization: `Bearer ${adminToken}` },
          payload: { name: `Make${i}` }
        });
      }

      // Get first page
      const page1 = await app.inject({
        method: 'GET',
        url: '/api/admin/catalog/change-logs?limit=2&offset=0',
        headers: { authorization: `Bearer ${adminToken}` }
      });

      const data1 = JSON.parse(page1.payload);
      expect(data1.logs.length).toBe(2);

      // Get second page
      const page2 = await app.inject({
        method: 'GET',
        url: '/api/admin/catalog/change-logs?limit=2&offset=2',
        headers: { authorization: `Bearer ${adminToken}` }
      });

      const data2 = JSON.parse(page2.payload);
      expect(data2.logs.length).toBe(2);
    });
  });
});
