/**
 * @ai-summary Integration tests for vehicles API endpoints
 * @ai-context Tests complete request/response cycle with test database
 */

import request from 'supertest';
import pool from '../../../../core/config/database';
import { cacheService } from '../../../../core/config/redis';
import { readFileSync } from 'fs';
import { join } from 'path';
import fastifyPlugin from 'fastify-plugin';

// Mock auth plugin to bypass JWT validation in tests
jest.mock('../../../../core/plugins/auth.plugin', () => {
  return {
    default: fastifyPlugin(async function(fastify: any) {
      fastify.decorate('authenticate', async function(request: any, _reply: any) {
        request.user = { sub: 'test-user-123' };
      });
    }, { name: 'auth-plugin' })
  };
});

// Import app after mocking
import app from '../../../../app';


describe('Vehicles Integration Tests', () => {
  beforeAll(async () => {
    // Run the vehicles migration directly using the migration file
    const migrationFile = join(__dirname, '../../migrations/001_create_vehicles_tables.sql');
    const migrationSQL = readFileSync(migrationFile, 'utf-8');
    await pool.query(migrationSQL);
  });

  afterAll(async () => {
    // Clean up test database
    await pool.query('DROP TABLE IF EXISTS vehicles CASCADE');
    await pool.query('DROP TABLE IF EXISTS vin_cache CASCADE');
    await pool.query('DROP FUNCTION IF EXISTS update_updated_at_column() CASCADE');
    await pool.end();
  });

  beforeEach(async () => {
    // Clean up test data before each test - more thorough cleanup
    await pool.query('DELETE FROM vehicles WHERE user_id = $1', ['test-user-123']);
    await pool.query('DELETE FROM vin_cache WHERE vin LIKE $1', ['1HGBH41JXMN%']);
    
    // Clear Redis cache for the test user
    try {
      await cacheService.del('vehicles:user:test-user-123');
    } catch (error) {
      // Ignore cache cleanup errors in tests
      console.warn('Failed to clear Redis cache in test:', error);
    }
  });

  describe('POST /api/vehicles', () => {
    it('should create a new vehicle with VIN', async () => {
      const vehicleData = {
        vin: '1HGBH41JXMN109186',
        nickname: 'My Test Car',
        color: 'Blue',
        odometerReading: 50000
      };

      const response = await request(app)
        .post('/api/vehicles')
        .send(vehicleData)
        .expect(201);

      expect(response.body).toMatchObject({
        id: expect.any(String),
        userId: 'test-user-123',
        vin: '1HGBH41JXMN109186',
        nickname: 'My Test Car',
        color: 'Blue',
        odometerReading: 50000,
        isActive: true,
        createdAt: expect.any(String),
        updatedAt: expect.any(String)
      });
    });

    it('should reject invalid VIN', async () => {
      const vehicleData = {
        vin: 'INVALID',
        nickname: 'Test Car'
      };

      const response = await request(app)
        .post('/api/vehicles')
        .send(vehicleData)
        .expect(400);

      expect(response.body.error).toMatch(/VIN/);
    });

    it('should reject duplicate VIN for same user', async () => {
      const vehicleData = {
        vin: '1HGBH41JXMN109186',
        nickname: 'First Car',
        licensePlate: 'ABC123'
      };

      // Create first vehicle
      await request(app)
        .post('/api/vehicles')
        .send(vehicleData)
        .expect(201);

      // Try to create duplicate
      const response = await request(app)
        .post('/api/vehicles')
        .send({ ...vehicleData, nickname: 'Duplicate Car' })
        .expect(400);

      expect(response.body.message).toContain('already exists');
    });
  });

  describe('GET /api/vehicles', () => {
    it('should return user vehicles', async () => {
      // Create test vehicles
      await pool.query(`
        INSERT INTO vehicles (user_id, vin, make, model, year, nickname)
        VALUES 
        ($1, $2, $3, $4, $5, $6),
        ($7, $8, $9, $10, $11, $12)
      `, [
        'test-user-123', '1HGBH41JXMN109186', 'Honda', 'Civic', 2021, 'Car 1',
        'test-user-123', '1HGBH41JXMN109187', 'Toyota', 'Camry', 2020, 'Car 2'
      ]);

      const response = await request(app)
        .get('/api/vehicles')
        .expect(200);

      expect(response.body).toHaveLength(2);
      expect(response.body[0]).toMatchObject({
        userId: 'test-user-123',
        vin: expect.any(String),
        nickname: expect.any(String)
      });
    });

    it('should return empty array for user with no vehicles', async () => {
      const response = await request(app)
        .get('/api/vehicles')
        .expect(200);

      expect(response.body).toEqual([]);
    });
  });

  describe('GET /api/vehicles/:id', () => {
    it('should return specific vehicle', async () => {
      // Create test vehicle
      const result = await pool.query(`
        INSERT INTO vehicles (user_id, vin, make, model, year, nickname)
        VALUES ($1, $2, $3, $4, $5, $6)
        RETURNING id
      `, ['test-user-123', '1HGBH41JXMN109186', 'Honda', 'Civic', 2021, 'Test Car']);

      const vehicleId = result.rows[0].id;

      const response = await request(app)
        .get(`/api/vehicles/${vehicleId}`)
        .expect(200);

      expect(response.body).toMatchObject({
        id: vehicleId,
        userId: 'test-user-123',
        vin: '1HGBH41JXMN109186',
        make: 'Honda',
        nickname: 'Test Car'
      });
    });

    it('should return 404 for non-existent vehicle', async () => {
      const fakeId = '550e8400-e29b-41d4-a716-446655440000';
      
      const response = await request(app)
        .get(`/api/vehicles/${fakeId}`)
        .expect(404);

      expect(response.body.error).toBe('Vehicle not found');
    });

    it('should return 400 for invalid UUID format', async () => {
      const response = await request(app)
        .get('/api/vehicles/invalid-id')
        .expect(400);

      expect(response.body).toHaveProperty('error');
    });
  });

  describe('PUT /api/vehicles/:id', () => {
    it('should update vehicle', async () => {
      // Create test vehicle
      const result = await pool.query(`
        INSERT INTO vehicles (user_id, vin, nickname, color)
        VALUES ($1, $2, $3, $4)
        RETURNING id
      `, ['test-user-123', '1HGBH41JXMN109186', 'Old Name', 'Blue']);

      const vehicleId = result.rows[0].id;

      const updateData = {
        nickname: 'Updated Name',
        color: 'Red',
        odometerReading: 75000
      };

      const response = await request(app)
        .put(`/api/vehicles/${vehicleId}`)
        .send(updateData)
        .expect(200);

      expect(response.body).toMatchObject({
        id: vehicleId,
        nickname: 'Updated Name',
        color: 'Red',
        odometerReading: 75000
      });
    });

    it('should return 404 for non-existent vehicle', async () => {
      const fakeId = '550e8400-e29b-41d4-a716-446655440000';
      
      const response = await request(app)
        .put(`/api/vehicles/${fakeId}`)
        .send({ nickname: 'Updated' })
        .expect(404);

      expect(response.body.error).toBe('Vehicle not found');
    });
  });

  describe('DELETE /api/vehicles/:id', () => {
    it('should soft delete vehicle', async () => {
      // Create test vehicle
      const result = await pool.query(`
        INSERT INTO vehicles (user_id, vin, nickname)
        VALUES ($1, $2, $3)
        RETURNING id
      `, ['test-user-123', '1HGBH41JXMN109186', 'Test Car']);

      const vehicleId = result.rows[0].id;

      await request(app)
        .delete(`/api/vehicles/${vehicleId}`)
        .expect(204);

      // Verify vehicle is soft deleted
      const checkResult = await pool.query(
        'SELECT is_active, deleted_at FROM vehicles WHERE id = $1',
        [vehicleId]
      );

      expect(checkResult.rows[0].is_active).toBe(false);
      expect(checkResult.rows[0].deleted_at).toBeTruthy();
    });

    it('should return 404 for non-existent vehicle', async () => {
      const fakeId = '550e8400-e29b-41d4-a716-446655440000';

      const response = await request(app)
        .delete(`/api/vehicles/${fakeId}`)
        .expect(404);

      expect(response.body.error).toBe('Vehicle not found');
    });
  });

  describe('Vehicle Limit Enforcement', () => {
    beforeEach(async () => {
      // Ensure user_profiles table exists and has test user
      await pool.query(`
        CREATE TABLE IF NOT EXISTS user_profiles (
          id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
          auth0_sub VARCHAR(255) NOT NULL UNIQUE,
          email VARCHAR(255) NOT NULL,
          display_name VARCHAR(255),
          subscription_tier VARCHAR(50) NOT NULL DEFAULT 'free',
          created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
          updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
      `);

      // Create or update test user with free tier
      await pool.query(`
        INSERT INTO user_profiles (auth0_sub, email, subscription_tier)
        VALUES ($1, $2, $3)
        ON CONFLICT (auth0_sub) DO UPDATE SET subscription_tier = EXCLUDED.subscription_tier
      `, ['test-user-123', 'test@example.com', 'free']);
    });

    it('should enforce free tier limit (2 vehicles)', async () => {
      // Create 2 vehicles (at limit)
      await pool.query(`
        INSERT INTO vehicles (user_id, year, make, model, license_plate)
        VALUES
          ($1, 2020, 'Honda', 'Civic', 'ABC123'),
          ($1, 2021, 'Toyota', 'Camry', 'XYZ789')
      `, ['test-user-123']);

      // Attempt to create a 3rd vehicle
      const response = await request(app)
        .post('/api/vehicles')
        .send({
          year: 2022,
          make: 'Ford',
          model: 'F-150',
          licensePlate: 'TEST123'
        })
        .expect(403);

      expect(response.body).toMatchObject({
        error: 'TIER_REQUIRED',
        currentTier: 'free',
        requiredTier: 'pro',
        feature: 'vehicle.addBeyondLimit',
        context: {
          limit: 2,
          count: 2
        }
      });
      expect(response.body.upgradePrompt).toContain('Free tier is limited to 2 vehicles');
    });

    it('should allow free tier user to add vehicle when below limit', async () => {
      // Create 1 vehicle (below limit)
      await pool.query(`
        INSERT INTO vehicles (user_id, year, make, model, license_plate)
        VALUES ($1, 2020, 'Honda', 'Civic', 'ABC123')
      `, ['test-user-123']);

      // Should be able to add 2nd vehicle
      const response = await request(app)
        .post('/api/vehicles')
        .send({
          year: 2021,
          make: 'Toyota',
          model: 'Camry',
          licensePlate: 'XYZ789'
        })
        .expect(201);

      expect(response.body).toMatchObject({
        year: 2021,
        make: 'Toyota',
        model: 'Camry'
      });
    });

    it('should enforce pro tier limit (5 vehicles)', async () => {
      // Update user to pro tier
      await pool.query(`
        UPDATE user_profiles SET subscription_tier = 'pro' WHERE auth0_sub = $1
      `, ['test-user-123']);

      // Create 5 vehicles (at limit)
      for (let i = 1; i <= 5; i++) {
        await pool.query(`
          INSERT INTO vehicles (user_id, year, make, model, license_plate)
          VALUES ($1, 2020, 'Honda', 'Civic', $2)
        `, ['test-user-123', `PLATE${i}`]);
      }

      // Attempt to create a 6th vehicle
      const response = await request(app)
        .post('/api/vehicles')
        .send({
          year: 2022,
          make: 'Ford',
          model: 'F-150',
          licensePlate: 'TEST123'
        })
        .expect(403);

      expect(response.body).toMatchObject({
        error: 'TIER_REQUIRED',
        currentTier: 'pro',
        requiredTier: 'enterprise',
        context: {
          limit: 5,
          count: 5
        }
      });
    });

    it('should allow enterprise tier unlimited vehicles', async () => {
      // Update user to enterprise tier
      await pool.query(`
        UPDATE user_profiles SET subscription_tier = 'enterprise' WHERE auth0_sub = $1
      `, ['test-user-123']);

      // Create 10 vehicles (well beyond free/pro limits)
      for (let i = 1; i <= 10; i++) {
        await pool.query(`
          INSERT INTO vehicles (user_id, year, make, model, license_plate)
          VALUES ($1, 2020, 'Honda', 'Civic', $2)
        `, ['test-user-123', `PLATE${i}`]);
      }

      // Should still be able to add another vehicle
      const response = await request(app)
        .post('/api/vehicles')
        .send({
          year: 2022,
          make: 'Ford',
          model: 'F-150',
          licensePlate: 'ENTERPRISE1'
        })
        .expect(201);

      expect(response.body).toMatchObject({
        year: 2022,
        make: 'Ford',
        model: 'F-150'
      });
    });

    it('should only count active vehicles towards limit', async () => {
      // Create 1 active vehicle and 1 deleted vehicle
      await pool.query(`
        INSERT INTO vehicles (user_id, year, make, model, license_plate)
        VALUES ($1, 2020, 'Honda', 'Civic', 'ABC123')
      `, ['test-user-123']);

      await pool.query(`
        INSERT INTO vehicles (user_id, year, make, model, license_plate, is_active, deleted_at)
        VALUES ($1, 2019, 'Toyota', 'Corolla', 'OLD123', false, NOW())
      `, ['test-user-123']);

      // Should be able to add 2nd active vehicle (deleted one doesn't count)
      const response = await request(app)
        .post('/api/vehicles')
        .send({
          year: 2021,
          make: 'Toyota',
          model: 'Camry',
          licensePlate: 'XYZ789'
        })
        .expect(201);

      expect(response.body).toMatchObject({
        year: 2021,
        make: 'Toyota'
      });
    });
  });
});