import { Command, CommandRunner, Option } from 'nest-commander';
import { Injectable, Logger } from '@nestjs/common';
import { CsvPopulateService } from 'modules/feature/can/populate-db-csv/csv-populate.service';
import * as fs from 'fs';
import * as path from 'path';
import * as glob from 'glob';
import { DataSource } from 'typeorm';
import { ImportCSVService } from 'modules/feature/can/csv-import/csv-import.service';

// Define allowed file types
export enum CsvFileType {
  IPV4 = 'ipv4',
  IPV6 = 'ipv6',
  ASN = 'asn',
  GEONAME = 'geoname',
  GENERIC = 'generic',
}

interface CsvImportCommandOptions {
  files: string[];
  directory?: string;
  pattern?: string;
  batchSize?: number;
  source?: string;
  verbose?: boolean;
  type?: CsvFileType;
}

@Injectable()
@Command({ name: 'import-csv', description: 'Import CSV files containing breach data' })
export class CsvImportCommand extends CommandRunner {
  private readonly logger = new Logger(CsvImportCommand.name);

  constructor(
    private readonly csvPopulateService: CsvPopulateService,
    private readonly importCSVService: ImportCSVService,
    private readonly dataSource: DataSource,
  ) {
    super();
  }

  async run(passedParams: string[], options?: CsvImportCommandOptions): Promise<void> {
    console.log('Passed parameters: ', passedParams);
    console.log('Options: ', options);

    try {
      // Initialize database connection
      if (!this.dataSource.isInitialized) {
        await this.dataSource.initialize();
        console.log('Database connection established');
      }
      console.log('Optional parameters: ', options);

      const filesToProcess = await this.resolveFiles(options);

      if (filesToProcess.length === 0) {
        console.warn('No CSV files found to process.');
        return;
      }

      console.log(`Processing ${filesToProcess.length} CSV files`);
      console.debug('Files: ' + filesToProcess.join(', '));

      const batchSize = options?.batchSize || 1000;
      const source = options?.source || path.basename(filesToProcess[0], '.csv');
      const fileType = options?.type || CsvFileType.GENERIC;

      console.log(`Starting import with batch size ${batchSize}, source "${source}", type "${fileType}"`);

      for (const file of filesToProcess) {
        console.log(`Processing file: ${file}`);

        // Call the import service with the detected type
        await this.processFile({
          filePath: file,
          fileType: fileType,
          batchSize,
          source,
        });
      }
      // await this.csvImportService.importCsvData(filesToProcess, batchSize, source);
      console.log('CSV import completed successfully');
    } catch (error) {
      console.log('Import failed: ' + error.message);
      process.exit(1);
    } finally {
      console.log('Cleaning up...');
      if (this.dataSource.isInitialized) {
        await this.dataSource.destroy();
      }
    }
  }

  private async processFile(options: { filePath: string; fileType: CsvFileType; batchSize: number; source: string }): Promise<void> {
    try {
      switch (options.fileType) {
        case CsvFileType.IPV4:
          await this.importCSVService.importCityBlocksIpv4(options.filePath);
          break;
        case CsvFileType.IPV6:
          await this.importCSVService.importCityBlocksIpv6(options.filePath);
          break;
        case CsvFileType.ASN:
          await this.importCSVService.importAsn(options.filePath);
          break;
        case CsvFileType.GEONAME:
          await this.importCSVService.importGeoNames(options.filePath);
          break;
        default:
          // Fall back to the generic import method
          await this.csvPopulateService.importCsvData([options.filePath], options.batchSize, options.source);
          break;
      }
    } catch (error) {
      console.error(`Error processing file ${options.fileType}: ${error.message}`);
      throw error;
    }
  }

  @Option({
    flags: '-f, --files [files...]',
    description: 'Specific CSV files to import (comma-separated)',
  })
  parseFiles(val: string): string[] {
    console.log(`Files to import: ${val}`);
    return val.split(',').map(file => file.trim());
  }

  @Option({
    flags: '-d, --directory [directory]',
    description: 'Directory containing CSV files to import',
  })
  parseDirectory(val: string): string {
    return val;
  }

  @Option({
    flags: '-p, --pattern [pattern]',
    description: 'Glob pattern for CSV files (e.g., "*.csv")',
    defaultValue: '*.csv',
  })
  parsePattern(val: string): string {
    return val;
  }

  @Option({
    flags: '-b, --batch-size [batchSize]',
    description: 'Number of records to process in a single batch',
    defaultValue: '1000',
  })
  parseBatchSize(val: string): number {
    return parseInt(val, 10);
  }

  @Option({
    flags: '-s, --source [source]',
    description: 'Source name for the breach data',
    defaultValue: 'CSV Import',
  })
  parseSource(val: string): string {
    return val;
  }

  @Option({
    flags: '-t, --type [type]',
    description: 'Type of CSV data (cityblockIpv4, cityblockIpv6, asn, geoname,)',
  })
  parseType(val: string): string {
    console.log(`Type of data: ${val}`);
    return val;
  }

  @Option({
    flags: '-v, --verbose',
    description: 'Enable verbose logging',
  })
  parseVerbose(val: boolean): boolean {
    return val;
  }

  private async resolveFiles(options: CsvImportCommandOptions): Promise<string[]> {
    const files: string[] = [];

    if (options?.files?.length) {
      for (const file of options.files) {
        const fullPath = path.resolve(file);
        if (fs.existsSync(fullPath) && fullPath.toLowerCase().endsWith('.csv')) {
          files.push(fullPath);
        } else {
          console.warn(`Skipping invalid file: ${fullPath}`);
        }
      }
    }

    if (options?.directory) {
      const pattern = options?.pattern || '*.csv';
      try {
        const foundFiles = glob.sync(path.join(options.directory, pattern));
        files.push(...foundFiles.filter(f => f.toLowerCase().endsWith('.csv')));
      } catch (error) {
        console.error(`File search error: ${error.message}`);
      }
    }

    return [...new Set(files)];
  }
}

/**
 * # Import a single file
npm run import-csv -- --files csv/file01.csv

* # Import by file type
npm run import-csv -- --files csv/GeoLite2-City-Blocks-IPv6.csv --type ipv6

# Import all CSV files from a directory
npm run import-csv -- --directory path/to/breach-data
`
# Import with custom source and batch size
npm run import-csv -- --files path/to/file.csv --source "XYZ Breach" --batch-size 500
 */
