/**
 * @ai-summary Catalog CSV import/export service
 * @ai-context Handles bulk import with preview and export of vehicle catalog data
 */

import { Pool } from 'pg';
import { logger } from '../../../core/logging/logger';
import { PlatformCacheService } from '../../platform/domain/platform-cache.service';

export interface ImportRow {
  year: number;
  make: string;
  model: string;
  trim: string;
  engineName: string | null;
  transmissionType: string | null;
}

export interface ImportError {
  row: number;
  error: string;
}

export interface ImportPreviewResult {
  previewId: string;
  toCreate: ImportRow[];
  toUpdate: ImportRow[];
  errors: ImportError[];
  valid: boolean;
}

export interface ImportApplyResult {
  created: number;
  updated: number;
  errors: ImportError[];
}

export interface ExportRow {
  year: number;
  make: string;
  model: string;
  trim: string;
  engineName: string | null;
  transmissionType: string | null;
}

// In-memory preview cache (expires after 15 minutes)
const previewCache = new Map<string, { data: ImportPreviewResult; expiresAt: number }>();

export class CatalogImportService {
  constructor(
    private pool: Pool,
    private platformCacheService?: PlatformCacheService
  ) {}

  /**
   * Parse CSV content and validate without applying changes
   */
  async previewImport(csvContent: string): Promise<ImportPreviewResult> {
    const previewId = uuidv4();
    const toCreate: ImportRow[] = [];
    const toUpdate: ImportRow[] = []; // Kept for interface compatibility (will be empty)
    const errors: ImportError[] = [];

    const lines = csvContent.trim().split('\n');
    if (lines.length < 2) {
      return {
        previewId,
        toCreate,
        toUpdate,
        errors: [{ row: 0, error: 'CSV must have a header row and at least one data row' }],
        valid: false,
      };
    }

    // Parse header row
    const header = this.parseCSVLine(lines[0]);
    const headerLower = header.map(h => h.toLowerCase().trim());

    // Validate required headers (no action column required - matches export format)
    const requiredHeaders = ['year', 'make', 'model', 'trim'];
    for (const required of requiredHeaders) {
      if (!headerLower.includes(required)) {
        return {
          previewId,
          toCreate,
          toUpdate,
          errors: [{ row: 1, error: `Missing required header: ${required}` }],
          valid: false,
        };
      }
    }

    // Find column indices
    const colIndices = {
      year: headerLower.indexOf('year'),
      make: headerLower.indexOf('make'),
      model: headerLower.indexOf('model'),
      trim: headerLower.indexOf('trim'),
      engineName: headerLower.indexOf('engine_name'),
      transmissionType: headerLower.indexOf('transmission_type'),
    };

    // Parse data rows
    for (let i = 1; i < lines.length; i++) {
      const line = lines[i].trim();
      if (!line) continue;

      const values = this.parseCSVLine(line);
      const rowNum = i + 1;

      try {
        const year = parseInt(values[colIndices.year], 10);
        const make = values[colIndices.make]?.trim();
        const model = values[colIndices.model]?.trim();
        const trim = values[colIndices.trim]?.trim();
        const engineName = colIndices.engineName >= 0 ? values[colIndices.engineName]?.trim() || null : null;
        const transmissionType = colIndices.transmissionType >= 0 ? values[colIndices.transmissionType]?.trim() || null : null;

        // Validate year
        if (isNaN(year) || year < 1900 || year > 2100) {
          errors.push({ row: rowNum, error: `Invalid year: ${values[colIndices.year]}` });
          continue;
        }

        // Validate required fields
        if (!make) {
          errors.push({ row: rowNum, error: 'Make is required' });
          continue;
        }
        if (!model) {
          errors.push({ row: rowNum, error: 'Model is required' });
          continue;
        }
        if (!trim) {
          errors.push({ row: rowNum, error: 'Trim is required' });
          continue;
        }

        const row: ImportRow = {
          year,
          make,
          model,
          trim,
          engineName,
          transmissionType,
        };

        // All rows will be inserted with ON CONFLICT handling (proper upsert)
        toCreate.push(row);
      } catch (error: any) {
        errors.push({ row: rowNum, error: error.message || 'Parse error' });
      }
    }

    const result: ImportPreviewResult = {
      previewId,
      toCreate,
      toUpdate,
      errors,
      valid: errors.length === 0,
    };

    // Cache preview for 15 minutes
    previewCache.set(previewId, {
      data: result,
      expiresAt: Date.now() + 15 * 60 * 1000,
    });

    // Clean up expired previews
    this.cleanupExpiredPreviews();

    return result;
  }

  /**
   * Apply a previously previewed import
   */
  async applyImport(previewId: string, changedBy: string): Promise<ImportApplyResult> {
    const cached = previewCache.get(previewId);
    if (!cached || cached.expiresAt < Date.now()) {
      throw new Error('Preview expired or not found. Please upload the file again.');
    }

    const preview = cached.data;
    if (!preview.valid) {
      throw new Error('Cannot apply import with validation errors');
    }

    const result: ImportApplyResult = {
      created: 0,
      updated: 0,
      errors: [],
    };

    const client = await this.pool.connect();
    try {
      await client.query('BEGIN');

      // Process creates
      for (const row of preview.toCreate) {
        try {
          // Get or create engine
          let engineId: number | null = null;
          if (row.engineName) {
            const engineResult = await client.query(
              `INSERT INTO engines (name, fuel_type)
               VALUES ($1, 'Gas')
               ON CONFLICT ((lower(name))) DO UPDATE SET name = EXCLUDED.name
               RETURNING id`,
              [row.engineName]
            );
            engineId = engineResult.rows[0].id;
          }

          // Get or create transmission
          let transmissionId: number | null = null;
          if (row.transmissionType) {
            const transResult = await client.query(
              `INSERT INTO transmissions (type)
               VALUES ($1)
               ON CONFLICT ((lower(type))) DO UPDATE SET type = EXCLUDED.type
               RETURNING id`,
              [row.transmissionType]
            );
            transmissionId = transResult.rows[0].id;
          }

          // Upsert vehicle option (insert or update if exists)
          const upsertResult = await client.query(
            `INSERT INTO vehicle_options (year, make, model, trim, engine_id, transmission_id)
             VALUES ($1, $2, $3, $4, $5, $6)
             ON CONFLICT (year, make, model, trim, engine_id, transmission_id)
             DO UPDATE SET updated_at = NOW()
             RETURNING (xmax = 0) AS inserted`,
            [row.year, row.make, row.model, row.trim, engineId, transmissionId]
          );

          // Check if this was an insert (xmax=0) or update (xmax!=0)
          const wasInserted = upsertResult.rows[0].inserted;
          if (wasInserted) {
            result.created++;
          } else {
            result.updated++;
          }
        } catch (error: any) {
          result.errors.push({ row: 0, error: `Failed to upsert ${row.year} ${row.make} ${row.model} ${row.trim}: ${error.message}` });
        }
      }

      // Note: Separate "Process updates" loop removed - ON CONFLICT handles both INSERT and UPDATE

      await client.query('COMMIT');

      // Remove preview from cache
      previewCache.delete(previewId);

      // Invalidate vehicle data cache so dropdowns reflect new data
      if (this.platformCacheService) {
        await this.platformCacheService.invalidateVehicleData();
        logger.debug('Vehicle data cache invalidated after import');
      }

      // Log completion with appropriate level
      if (result.errors.length > 0) {
        logger.warn('Catalog import completed with errors', {
          previewId,
          created: result.created,
          updated: result.updated,
          errors: result.errors.length,
          changedBy,
        });
      } else {
        logger.info('Catalog import completed successfully', {
          previewId,
          created: result.created,
          updated: result.updated,
          changedBy,
        });
      }

      return result;
    } catch (error) {
      await client.query('ROLLBACK');
      throw error;
    } finally {
      client.release();
    }
  }

  /**
   * Export all vehicle options as CSV
   */
  async exportCatalog(): Promise<string> {
    const result = await this.pool.query(`
      SELECT
        vo.year,
        vo.make,
        vo.model,
        vo.trim,
        e.name AS engine_name,
        t.type AS transmission_type
      FROM vehicle_options vo
      LEFT JOIN engines e ON vo.engine_id = e.id
      LEFT JOIN transmissions t ON vo.transmission_id = t.id
      ORDER BY vo.year DESC, vo.make ASC, vo.model ASC, vo.trim ASC
    `);

    // Build CSV
    const header = 'year,make,model,trim,engine_name,transmission_type';
    const rows = result.rows.map(row => {
      return [
        row.year,
        this.escapeCSVField(row.make),
        this.escapeCSVField(row.model),
        this.escapeCSVField(row.trim),
        this.escapeCSVField(row.engine_name || ''),
        this.escapeCSVField(row.transmission_type || ''),
      ].join(',');
    });

    return [header, ...rows].join('\n');
  }

  /**
   * Parse a single CSV line, handling quoted fields
   */
  private parseCSVLine(line: string): string[] {
    const result: string[] = [];
    let current = '';
    let inQuotes = false;

    for (let i = 0; i < line.length; i++) {
      const char = line[i];
      const nextChar = line[i + 1];

      if (inQuotes) {
        if (char === '"' && nextChar === '"') {
          current += '"';
          i++; // Skip next quote
        } else if (char === '"') {
          inQuotes = false;
        } else {
          current += char;
        }
      } else {
        if (char === '"') {
          inQuotes = true;
        } else if (char === ',') {
          result.push(current);
          current = '';
        } else {
          current += char;
        }
      }
    }

    result.push(current);
    return result;
  }

  /**
   * Escape a field for CSV output
   */
  private escapeCSVField(value: string): string {
    if (!value) return '';
    if (value.includes(',') || value.includes('"') || value.includes('\n')) {
      return `"${value.replace(/"/g, '""')}"`;
    }
    return value;
  }

  /**
   * Clean up expired preview cache entries
   */
  private cleanupExpiredPreviews(): void {
    const now = Date.now();
    for (const [id, entry] of previewCache.entries()) {
      if (entry.expiresAt < now) {
        previewCache.delete(id);
      }
    }
  }
}

// Simple UUID generation without external dependency
function uuidv4(): string {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
    const r = Math.random() * 16 | 0;
    const v = c === 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}
