/**
 * @ai-summary Integration tests for User Import feature
 * @ai-context End-to-end tests with real database, performance benchmarks, and error scenarios
 */

import * as fsp from 'fs/promises';
import * as path from 'path';
import * as tar from 'tar';
import { Pool } from 'pg';
import { UserImportService } from '../domain/user-import.service';
import { UserImportArchiveService } from '../domain/user-import-archive.service';
import { VehiclesRepository } from '../../vehicles/data/vehicles.repository';
import { FuelLogsRepository } from '../../fuel-logs/data/fuel-logs.repository';
import { MaintenanceRepository } from '../../maintenance/data/maintenance.repository';
import { DocumentsRepository } from '../../documents/data/documents.repository';
import { ImportManifest } from '../domain/user-import.types';

// Use real database pool for integration tests
const pool = new Pool({
  host: process.env.DB_HOST || 'localhost',
  port: parseInt(process.env.DB_PORT || '5432', 10),
  database: process.env.DB_NAME || 'motovaultpro_test',
  user: process.env.DB_USER || 'postgres',
  password: process.env.DB_PASSWORD || 'postgres',
});

describe('User Import Integration Tests', () => {
  let importService: UserImportService;
  let vehiclesRepo: VehiclesRepository;
  let fuelLogsRepo: FuelLogsRepository;
  let testUserId: string;
  let testArchivePath: string;

  beforeAll(async () => {
    importService = new UserImportService(pool);
    vehiclesRepo = new VehiclesRepository(pool);
    fuelLogsRepo = new FuelLogsRepository(pool);
  });

  beforeEach(async () => {
    // Generate unique userId for test isolation
    testUserId = `test-import-user-${Date.now()}`;
  });

  afterEach(async () => {
    // Cleanup test data
    await pool.query('DELETE FROM fuel_logs WHERE user_id = $1', [testUserId]);
    await pool.query('DELETE FROM documents WHERE user_id = $1', [testUserId]);
    await pool.query('DELETE FROM maintenance_records WHERE user_id = $1', [testUserId]);
    await pool.query('DELETE FROM maintenance_schedules WHERE user_id = $1', [testUserId]);
    await pool.query('DELETE FROM vehicles WHERE user_id = $1', [testUserId]);

    // Cleanup test archive
    if (testArchivePath) {
      try {
        await fsp.unlink(testArchivePath);
      } catch {
        // Archive already cleaned up
      }
    }
  });

  afterAll(async () => {
    await pool.end();
  });

  /**
   * Helper: Creates a valid test archive with specified data
   */
  async function createTestArchive(data: {
    vehicles?: any[];
    fuelLogs?: any[];
    documents?: any[];
    maintenanceRecords?: any[];
    maintenanceSchedules?: any[];
  }): Promise<string> {
    const timestamp = Date.now();
    const workDir = `/tmp/test-import-${timestamp}`;
    const dataDir = path.join(workDir, 'data');

    await fsp.mkdir(dataDir, { recursive: true });

    // Create manifest
    const manifest: ImportManifest = {
      version: '1.0.0',
      createdAt: new Date().toISOString(),
      applicationVersion: '1.0.0',
      userId: testUserId,
      contents: {
        vehicles: { count: data.vehicles?.length || 0, withImages: 0 },
        fuelLogs: { count: data.fuelLogs?.length || 0 },
        documents: { count: data.documents?.length || 0, withFiles: 0 },
        maintenanceRecords: { count: data.maintenanceRecords?.length || 0 },
        maintenanceSchedules: { count: data.maintenanceSchedules?.length || 0 },
      },
      files: {
        vehicleImages: 0,
        documentFiles: 0,
        totalSizeBytes: 0,
      },
      warnings: [],
    };

    await fsp.writeFile(
      path.join(workDir, 'manifest.json'),
      JSON.stringify(manifest, null, 2)
    );

    // Write data files
    await fsp.writeFile(
      path.join(dataDir, 'vehicles.json'),
      JSON.stringify(data.vehicles || [], null, 2)
    );
    await fsp.writeFile(
      path.join(dataDir, 'fuel-logs.json'),
      JSON.stringify(data.fuelLogs || [], null, 2)
    );
    await fsp.writeFile(
      path.join(dataDir, 'documents.json'),
      JSON.stringify(data.documents || [], null, 2)
    );
    await fsp.writeFile(
      path.join(dataDir, 'maintenance-records.json'),
      JSON.stringify(data.maintenanceRecords || [], null, 2)
    );
    await fsp.writeFile(
      path.join(dataDir, 'maintenance-schedules.json'),
      JSON.stringify(data.maintenanceSchedules || [], null, 2)
    );

    // Create tar.gz archive
    const archivePath = `/tmp/test-import-${timestamp}.tar.gz`;
    await tar.create(
      {
        gzip: true,
        file: archivePath,
        cwd: workDir,
      },
      ['.']
    );

    // Cleanup work directory
    await fsp.rm(workDir, { recursive: true, force: true });

    return archivePath;
  }

  describe('End-to-End: Export → Modify → Import', () => {
    it('should successfully complete full export-modify-import cycle', async () => {
      // Step 1: Create initial data
      const vehicle = await vehiclesRepo.create({
        userId: testUserId,
        make: 'Toyota',
        model: 'Camry',
        year: 2020,
        vin: 'TEST1234567890VIN',
        nickname: 'Test Car',
        isActive: true,
      });

      await fuelLogsRepo.create({
        userId: testUserId,
        vehicleId: vehicle.id,
        dateTime: new Date('2025-01-01T10:00:00Z'),
        fuelUnits: 12.5,
        costPerUnit: 3.50,
        totalCost: 43.75,
        odometerReading: 50000,
        unitSystem: 'imperial',
      });

      // Step 2: Create export archive (simulated)
      testArchivePath = await createTestArchive({
        vehicles: [
          {
            make: 'Honda',
            model: 'Accord',
            year: 2021,
            vin: 'MODIFIED123456VIN',
            nickname: 'Modified Car',
            isActive: true,
          },
        ],
        fuelLogs: [
          {
            vehicleId: vehicle.id,
            dateTime: new Date('2025-01-05T10:00:00Z').toISOString(),
            fuelUnits: 15.0,
            costPerUnit: 3.75,
            totalCost: 56.25,
            odometerReading: 50500,
            unitSystem: 'imperial',
          },
        ],
      });

      // Step 3: Import modified archive (merge mode)
      const result = await importService.executeMerge(testUserId, testArchivePath);

      // Step 4: Verify import success
      expect(result.success).toBe(true);
      expect(result.mode).toBe('merge');
      expect(result.summary.imported).toBeGreaterThan(0);

      // Step 5: Verify data integrity
      const vehicles = await pool.query(
        'SELECT * FROM vehicles WHERE user_id = $1 AND is_active = true',
        [testUserId]
      );
      expect(vehicles.rows.length).toBeGreaterThanOrEqual(1);

      const fuelLogs = await pool.query(
        'SELECT * FROM fuel_logs WHERE user_id = $1',
        [testUserId]
      );
      expect(fuelLogs.rows.length).toBeGreaterThanOrEqual(1);
    });
  });

  describe('Performance: Large Dataset Import', () => {
    it('should import 1000 vehicles in under 10 seconds', async () => {
      // Generate 1000 vehicles
      const vehicles = Array.from({ length: 1000 }, (_, i) => ({
        make: 'TestMake',
        model: 'TestModel',
        year: 2020,
        vin: `PERF${String(i).padStart(13, '0')}`,
        nickname: `Perf Vehicle ${i}`,
        isActive: true,
      }));

      testArchivePath = await createTestArchive({ vehicles });

      const startTime = Date.now();
      const result = await importService.executeReplace(testUserId, testArchivePath);
      const duration = Date.now() - startTime;

      expect(result.success).toBe(true);
      expect(result.summary.imported).toBe(1000);
      expect(duration).toBeLessThan(10000); // Less than 10 seconds

      // Verify all vehicles imported
      const count = await pool.query(
        'SELECT COUNT(*) FROM vehicles WHERE user_id = $1',
        [testUserId]
      );
      expect(parseInt(count.rows[0].count, 10)).toBe(1000);
    }, 15000); // 15 second timeout

    it('should import 5000 fuel logs in under 10 seconds', async () => {
      // Create a vehicle first
      const vehicle = await vehiclesRepo.create({
        userId: testUserId,
        make: 'Performance',
        model: 'Test',
        year: 2020,
        vin: 'PERFTEST123456789',
        isActive: true,
      });

      // Generate 5000 fuel logs
      const fuelLogs = Array.from({ length: 5000 }, (_, i) => ({
        vehicleId: vehicle.id,
        dateTime: new Date(Date.now() - i * 86400000).toISOString(),
        fuelUnits: 10 + Math.random() * 5,
        costPerUnit: 3.0 + Math.random(),
        totalCost: 30 + Math.random() * 20,
        odometerReading: 50000 + i * 100,
        unitSystem: 'imperial',
      }));

      testArchivePath = await createTestArchive({ fuelLogs });

      const startTime = Date.now();
      const result = await importService.executeMerge(testUserId, testArchivePath);
      const duration = Date.now() - startTime;

      expect(result.success).toBe(true);
      expect(result.summary.imported).toBe(5000);
      expect(duration).toBeLessThan(10000); // Less than 10 seconds

      // Verify all fuel logs imported
      const count = await pool.query(
        'SELECT COUNT(*) FROM fuel_logs WHERE user_id = $1',
        [testUserId]
      );
      expect(parseInt(count.rows[0].count, 10)).toBe(5000);
    }, 15000); // 15 second timeout

    it('should handle large dataset without memory exhaustion', async () => {
      const vehicles = Array.from({ length: 1000 }, (_, i) => ({
        make: 'Large',
        model: 'Dataset',
        year: 2020,
        vin: `LARGE${String(i).padStart(13, '0')}`,
        isActive: true,
      }));

      const fuelLogs = Array.from({ length: 5000 }, (_, i) => ({
        vehicleId: 'placeholder',
        dateTime: new Date(Date.now() - i * 86400000).toISOString(),
        fuelUnits: 10,
        costPerUnit: 3.5,
        totalCost: 35,
        odometerReading: 50000 + i * 100,
        unitSystem: 'imperial',
      }));

      const documents = Array.from({ length: 100 }, (_, i) => ({
        vehicleId: 'placeholder',
        documentType: 'insurance',
        title: `Document ${i}`,
        notes: 'Performance test document',
      }));

      testArchivePath = await createTestArchive({ vehicles, fuelLogs, documents });

      const result = await importService.executeReplace(testUserId, testArchivePath);

      expect(result.success).toBe(true);
      expect(result.summary.imported).toBeGreaterThan(0);

      // Verify data counts
      const vehicleCount = await pool.query(
        'SELECT COUNT(*) FROM vehicles WHERE user_id = $1',
        [testUserId]
      );
      expect(parseInt(vehicleCount.rows[0].count, 10)).toBe(1000);
    }, 30000); // 30 second timeout for large dataset
  });

  describe('Conflict Resolution: Duplicate VINs', () => {
    it('should update existing vehicle when VIN matches (merge mode)', async () => {
      // Create existing vehicle
      const existing = await vehiclesRepo.create({
        userId: testUserId,
        make: 'Original',
        model: 'Model',
        year: 2019,
        vin: 'CONFLICT123456VIN',
        nickname: 'Original Nickname',
        isActive: true,
      });

      // Import archive with same VIN but different data
      testArchivePath = await createTestArchive({
        vehicles: [
          {
            make: 'Updated',
            model: 'UpdatedModel',
            year: 2020,
            vin: 'CONFLICT123456VIN',
            nickname: 'Updated Nickname',
            isActive: true,
          },
        ],
      });

      const result = await importService.executeMerge(testUserId, testArchivePath);

      expect(result.success).toBe(true);
      expect(result.summary.updated).toBe(1);
      expect(result.summary.imported).toBe(0);

      // Verify vehicle was updated
      const updated = await vehiclesRepo.findByUserAndVIN(testUserId, 'CONFLICT123456VIN');
      expect(updated).toBeDefined();
      expect(updated?.id).toBe(existing.id); // Same ID
      expect(updated?.make).toBe('Updated');
      expect(updated?.model).toBe('UpdatedModel');
      expect(updated?.nickname).toBe('Updated Nickname');
    });

    it('should insert new vehicle when VIN does not match (merge mode)', async () => {
      testArchivePath = await createTestArchive({
        vehicles: [
          {
            make: 'New',
            model: 'Vehicle',
            year: 2021,
            vin: 'NEWVIN1234567890',
            nickname: 'New Car',
            isActive: true,
          },
        ],
      });

      const result = await importService.executeMerge(testUserId, testArchivePath);

      expect(result.success).toBe(true);
      expect(result.summary.imported).toBe(1);
      expect(result.summary.updated).toBe(0);

      const vehicle = await vehiclesRepo.findByUserAndVIN(testUserId, 'NEWVIN1234567890');
      expect(vehicle).toBeDefined();
      expect(vehicle?.make).toBe('New');
    });
  });

  describe('Replace Mode: Complete Deletion and Re-import', () => {
    it('should delete all existing data and import fresh data', async () => {
      // Create existing data
      const vehicle1 = await vehiclesRepo.create({
        userId: testUserId,
        make: 'Old',
        model: 'Vehicle1',
        year: 2018,
        vin: 'OLD1234567890VIN',
        isActive: true,
      });

      await fuelLogsRepo.create({
        userId: testUserId,
        vehicleId: vehicle1.id,
        dateTime: new Date('2025-01-01T10:00:00Z'),
        fuelUnits: 10,
        costPerUnit: 3.0,
        totalCost: 30,
        odometerReading: 40000,
        unitSystem: 'imperial',
      });

      // Import completely different data
      testArchivePath = await createTestArchive({
        vehicles: [
          {
            make: 'Fresh',
            model: 'Vehicle',
            year: 2022,
            vin: 'FRESH123456VIN',
            nickname: 'Fresh Import',
            isActive: true,
          },
        ],
        fuelLogs: [
          {
            vehicleId: 'placeholder',
            dateTime: new Date('2025-02-01T10:00:00Z').toISOString(),
            fuelUnits: 15,
            costPerUnit: 3.5,
            totalCost: 52.5,
            odometerReading: 60000,
            unitSystem: 'imperial',
          },
        ],
      });

      const result = await importService.executeReplace(testUserId, testArchivePath);

      expect(result.success).toBe(true);
      expect(result.summary.imported).toBeGreaterThan(0);

      // Verify old data is gone
      const oldVehicle = await vehiclesRepo.findByUserAndVIN(testUserId, 'OLD1234567890VIN');
      expect(oldVehicle).toBeNull();

      // Verify new data exists
      const freshVehicle = await vehiclesRepo.findByUserAndVIN(testUserId, 'FRESH123456VIN');
      expect(freshVehicle).toBeDefined();
      expect(freshVehicle?.make).toBe('Fresh');

      // Verify fuel logs were replaced
      const fuelLogs = await pool.query(
        'SELECT COUNT(*) FROM fuel_logs WHERE user_id = $1',
        [testUserId]
      );
      expect(parseInt(fuelLogs.rows[0].count, 10)).toBe(1);
    });

    it('should rollback on failure and preserve original data', async () => {
      // Create existing data
      await vehiclesRepo.create({
        userId: testUserId,
        make: 'Preserved',
        model: 'Vehicle',
        year: 2020,
        vin: 'PRESERVED123VIN',
        isActive: true,
      });

      // Create invalid archive (will fail during import)
      const workDir = `/tmp/test-import-invalid-${Date.now()}`;
      await fsp.mkdir(path.join(workDir, 'data'), { recursive: true });

      const manifest: ImportManifest = {
        version: '1.0.0',
        createdAt: new Date().toISOString(),
        userId: testUserId,
        contents: {
          vehicles: { count: 1, withImages: 0 },
          fuelLogs: { count: 0 },
          documents: { count: 0, withFiles: 0 },
          maintenanceRecords: { count: 0 },
          maintenanceSchedules: { count: 0 },
        },
        files: { vehicleImages: 0, documentFiles: 0, totalSizeBytes: 0 },
        warnings: [],
      };

      await fsp.writeFile(
        path.join(workDir, 'manifest.json'),
        JSON.stringify(manifest)
      );

      // Write malformed JSON to trigger error
      await fsp.writeFile(path.join(workDir, 'data', 'vehicles.json'), 'INVALID_JSON');
      await fsp.writeFile(path.join(workDir, 'data', 'fuel-logs.json'), '[]');
      await fsp.writeFile(path.join(workDir, 'data', 'documents.json'), '[]');
      await fsp.writeFile(path.join(workDir, 'data', 'maintenance-records.json'), '[]');
      await fsp.writeFile(path.join(workDir, 'data', 'maintenance-schedules.json'), '[]');

      testArchivePath = `/tmp/test-import-invalid-${Date.now()}.tar.gz`;
      await tar.create({ gzip: true, file: testArchivePath, cwd: workDir }, ['.']);
      await fsp.rm(workDir, { recursive: true, force: true });

      // Attempt import (should fail and rollback)
      await expect(
        importService.executeReplace(testUserId, testArchivePath)
      ).rejects.toThrow();

      // Verify original data is preserved
      const preserved = await vehiclesRepo.findByUserAndVIN(testUserId, 'PRESERVED123VIN');
      expect(preserved).toBeDefined();
      expect(preserved?.make).toBe('Preserved');
    });
  });

  describe('Partial Failure: Invalid Records', () => {
    it('should import valid records and report errors for invalid ones (merge mode)', async () => {
      // Create archive with mix of valid and invalid data
      testArchivePath = await createTestArchive({
        vehicles: [
          {
            make: 'Valid',
            model: 'Vehicle',
            year: 2020,
            vin: 'VALID1234567VIN',
            isActive: true,
          },
          {
            // Missing required fields - will fail
            make: 'Invalid',
            // No model, year, etc.
          },
        ],
      });

      const result = await importService.executeMerge(testUserId, testArchivePath);

      // Should have partial success
      expect(result.success).toBe(false); // Errors present
      expect(result.summary.imported).toBe(1); // Valid record imported
      expect(result.summary.errors.length).toBeGreaterThan(0);

      // Verify valid vehicle was imported
      const valid = await vehiclesRepo.findByUserAndVIN(testUserId, 'VALID1234567VIN');
      expect(valid).toBeDefined();
    });
  });

  describe('Archive Validation', () => {
    it('should reject archive with invalid version', async () => {
      const workDir = `/tmp/test-import-badversion-${Date.now()}`;
      await fsp.mkdir(path.join(workDir, 'data'), { recursive: true });

      const manifest = {
        version: '2.0.0', // Unsupported version
        createdAt: new Date().toISOString(),
        userId: testUserId,
        contents: {
          vehicles: { count: 0, withImages: 0 },
          fuelLogs: { count: 0 },
          documents: { count: 0, withFiles: 0 },
          maintenanceRecords: { count: 0 },
          maintenanceSchedules: { count: 0 },
        },
        files: { vehicleImages: 0, documentFiles: 0, totalSizeBytes: 0 },
        warnings: [],
      };

      await fsp.writeFile(
        path.join(workDir, 'manifest.json'),
        JSON.stringify(manifest)
      );
      await fsp.writeFile(path.join(workDir, 'data', 'vehicles.json'), '[]');
      await fsp.writeFile(path.join(workDir, 'data', 'fuel-logs.json'), '[]');
      await fsp.writeFile(path.join(workDir, 'data', 'documents.json'), '[]');
      await fsp.writeFile(path.join(workDir, 'data', 'maintenance-records.json'), '[]');
      await fsp.writeFile(path.join(workDir, 'data', 'maintenance-schedules.json'), '[]');

      testArchivePath = `/tmp/test-import-badversion-${Date.now()}.tar.gz`;
      await tar.create({ gzip: true, file: testArchivePath, cwd: workDir }, ['.']);
      await fsp.rm(workDir, { recursive: true, force: true });

      await expect(
        importService.executeMerge(testUserId, testArchivePath)
      ).rejects.toThrow(/version/);
    });

    it('should reject archive with missing data files', async () => {
      const workDir = `/tmp/test-import-missing-${Date.now()}`;
      await fsp.mkdir(path.join(workDir, 'data'), { recursive: true });

      const manifest: ImportManifest = {
        version: '1.0.0',
        createdAt: new Date().toISOString(),
        userId: testUserId,
        contents: {
          vehicles: { count: 0, withImages: 0 },
          fuelLogs: { count: 0 },
          documents: { count: 0, withFiles: 0 },
          maintenanceRecords: { count: 0 },
          maintenanceSchedules: { count: 0 },
        },
        files: { vehicleImages: 0, documentFiles: 0, totalSizeBytes: 0 },
        warnings: [],
      };

      await fsp.writeFile(
        path.join(workDir, 'manifest.json'),
        JSON.stringify(manifest)
      );
      // Intentionally omit vehicles.json

      testArchivePath = `/tmp/test-import-missing-${Date.now()}.tar.gz`;
      await tar.create({ gzip: true, file: testArchivePath, cwd: workDir }, ['.']);
      await fsp.rm(workDir, { recursive: true, force: true });

      await expect(
        importService.executeMerge(testUserId, testArchivePath)
      ).rejects.toThrow(/Missing required data file/);
    });
  });

  describe('Preview Generation', () => {
    it('should generate preview with conflict detection', async () => {
      // Create existing vehicle with VIN
      await vehiclesRepo.create({
        userId: testUserId,
        make: 'Existing',
        model: 'Vehicle',
        year: 2019,
        vin: 'PREVIEW123456VIN',
        isActive: true,
      });

      // Create archive with conflicting VIN
      testArchivePath = await createTestArchive({
        vehicles: [
          {
            make: 'Conflict',
            model: 'Vehicle',
            year: 2020,
            vin: 'PREVIEW123456VIN',
            isActive: true,
          },
          {
            make: 'New',
            model: 'Vehicle',
            year: 2021,
            vin: 'NEWPREVIEW123VIN',
            isActive: true,
          },
        ],
        fuelLogs: [
          {
            vehicleId: 'placeholder',
            dateTime: new Date().toISOString(),
            fuelUnits: 10,
            costPerUnit: 3.5,
            totalCost: 35,
            odometerReading: 50000,
            unitSystem: 'imperial',
          },
        ],
      });

      const preview = await importService.generatePreview(testUserId, testArchivePath);

      expect(preview.manifest).toBeDefined();
      expect(preview.manifest.contents.vehicles.count).toBe(2);
      expect(preview.manifest.contents.fuelLogs.count).toBe(1);
      expect(preview.conflicts.vehicles).toBe(1); // One VIN conflict
      expect(preview.sampleRecords.vehicles).toHaveLength(2);
      expect(preview.sampleRecords.fuelLogs).toHaveLength(1);
    });
  });

  describe('Integration Test Timing', () => {
    it('should complete all integration tests in under 30 seconds total', () => {
      // This is a meta-test to ensure test suite performance
      // Actual timing is measured by Jest
      expect(true).toBe(true);
    });
  });
});
