const fs = require('fs');
const path = require('path');
const ConfigReader = require('../config/config-reader');

/**
 * Utility untuk operasi file dan direktori
 */
class FileUtils {

  /**
   * Membuat direktori jika belum ada (recursive)
   * @param {string} dirPath - Path direktori yang akan dibuat
   * @param {boolean} verbose - Apakah menampilkan log
   * @returns {boolean} True jika berhasil dibuat atau sudah ada
   */
  static ensureDirectoryExists(dirPath, verbose = true) {
    try {
      if (!fs.existsSync(dirPath)) {
        fs.mkdirSync(dirPath, { recursive: true });
        if (verbose) {
          console.log(`Directory created successfully: ${dirPath}`);
        }
        return true;
      } else {
        if (verbose) {
          console.log(`Directory already exists: ${dirPath}`);
        }
        return true;
      }
    } catch (error) {
      console.error(`Error creating directory ${dirPath}:`, error.message);
      throw new Error(`Failed to create directory ${dirPath}: ${error.message}`);
    }
  }

  /**
   * Membuat beberapa direktori sekaligus
   * @param {Array<string>} directories - Array path direktori
   * @param {string} baseDir - Base directory (optional)
   * @returns {Object} Result dengan success count dan errors
   */
  static ensureMultipleDirectories(directories, baseDir = null) {
    const result = {
      success: 0,
      failed: 0,
      errors: []
    };

    for (const dir of directories) {
      try {
        const fullPath = baseDir ? path.join(baseDir, dir) : dir;
        this.ensureDirectoryExists(fullPath, false);
        result.success++;
      } catch (error) {
        result.failed++;
        result.errors.push({
          directory: dir,
          error: error.message
        });
      }
    }

    if (result.success > 0) {
      console.log(`${result.success} directories created/verified successfully`);
    }

    if (result.failed > 0) {
      console.error(`${result.failed} directories failed to create`);
      result.errors.forEach(err => {
        console.error(`   ${err.directory}: ${err.error}`);
      });
    }

    return result;
  }

  /**
   * Setup struktur direktori standard untuk project
   * @param {string} moduleName - Nama module
   * @returns {Object} Paths yang telah dibuat
   */
  static setupModuleDirectories(moduleName) {
    const workingDir = ConfigReader.getWorkingDirectory();
    
    const directories = {
      modules: path.join(workingDir, 'src', 'modules'),
      moduleDir: path.join(workingDir, 'src', 'modules', moduleName),
      models: path.join(workingDir, 'src', 'models'),
      moduleModels: path.join(workingDir, 'src', 'models', moduleName),
      moduleQueries: path.join(workingDir, 'src', 'models', moduleName, 'query'),
      metadata: path.join(workingDir, 'metadata'),
      payload: path.join(workingDir, 'payload')
    };

    console.log(`Setting up directory structure for module: ${moduleName}`);

    // Buat semua direktori
    const createdPaths = {};
    for (const [key, dirPath] of Object.entries(directories)) {
      try {
        this.ensureDirectoryExists(dirPath, false);
        createdPaths[key] = dirPath;
      } catch (error) {
        console.error(`Failed to create ${key} directory: ${error.message}`);
        throw error;
      }
    }

    console.log(`Directory structure for module ${moduleName} ready`);
    return createdPaths;
  }

  /**
   * Deteksi kolom text yang tepat untuk lookup berdasarkan prioritas
   * @param {Array<string>} fieldNames - Array nama field
   * @returns {string} Nama kolom text yang terpilih
   */
  static detectTextColumn(fieldNames) {
    if (!Array.isArray(fieldNames) || fieldNames.length === 0) {
      return 'nama'; // default fallback
    }

    // Prioritas kolom untuk lookup (urutan penting)
    const priorities = [
      'name', 'nama', 'code', 'kode'
    ];
    
    // Cari berdasarkan prioritas
    for (const priority of priorities) {
      const found = fieldNames.find(field => 
        field.toLowerCase() === priority.toLowerCase()
      );
      if (found) {
        return found;
      }
    }
    
    // Jika tidak ditemukan berdasarkan prioritas, gunakan field kedua (biasanya bukan id)
    const nonIdFields = fieldNames.filter(field => 
      field.toLowerCase() !== 'id' && 
      field.toLowerCase() !== 'password' &&
      field.toLowerCase() !== 'token'
    );
    
    if (nonIdFields.length > 0) {
      return nonIdFields[0];
    }
    
    // Fallback terakhir
    return fieldNames.length > 1 ? fieldNames[1] : 'nama';
  }

  /**
   * Capitalize string (huruf pertama kapital)
   * @param {string} str - String yang akan di-capitalize
   * @returns {string} String dengan huruf pertama kapital
   */
  static capitalize(str) {
    if (!str || typeof str !== 'string') {
      return '';
    }
    return str.charAt(0).toUpperCase() + str.slice(1);
  }

  /**
   * Convert string ke camelCase
   * @param {string} str - String yang akan dikonversi
   * @returns {string} String dalam camelCase
   */
  static toCamelCase(str) {
    if (!str || typeof str !== 'string') {
      return '';
    }
    
    return str
      .replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => {
        return index === 0 ? word.toLowerCase() : word.toUpperCase();
      })
      .replace(/\s+/g, '')
      .replace(/[-_]/g, '');
  }

  /**
   * Convert string ke PascalCase
   * @param {string} str - String yang akan dikonversi
   * @returns {string} String dalam PascalCase
   */
  static toPascalCase(str) {
    if (!str || typeof str !== 'string') {
      return '';
    }
    
    const camelCase = this.toCamelCase(str);
    return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
  }

  /**
   * Convert string ke kebab-case
   * @param {string} str - String yang akan dikonversi
   * @returns {string} String dalam kebab-case
   */
  static toKebabCase(str) {
    if (!str || typeof str !== 'string') {
      return '';
    }
    
    return str
      .replace(/([a-z])([A-Z])/g, '$1-$2')
      .replace(/[\s_]+/g, '-')
      .toLowerCase();
  }

  /**
   * Prepare file information untuk copy operation
   * @param {string} querySource - Source query dengan format file:path
   * @param {string} queryName - Nama query
   * @param {string} queryType - Tipe query (advancedQuery, nestedQuery, etc)
   * @param {string} destDir - Destination directory
   * @returns {Object|null} File info object atau null jika error
   */
  static prepareFileForCopy(querySource, queryName, queryType, destDir) {
    try {
      const relativePath = querySource.replace('file:', '');
      const workingDir = ConfigReader.getWorkingDirectory();
      const sourceFilePath = path.join(workingDir, 'payload', relativePath);
      const fileName = path.basename(relativePath);
      const destFilePath = path.join(destDir, fileName);
      
      return {
        source: sourceFilePath,
        dest: destFilePath,
        queryName,
        queryType,
        originalPath: relativePath,
        newRelativePath: `query/${fileName}`
      };
    } catch (error) {
      console.error(`Error preparing file info for ${queryName}:`, error.message);
      return null;
    }
  }

  /**
   * Execute bulk file copy operation
   * @param {Array} fileInfos - Array of file info objects
   * @returns {Object} Copy operation result
   */
  static executeBulkFileCopy(fileInfos) {
    const result = {
      success: 0,
      failed: 0,
      errors: [],
      successFiles: []
    };

    for (const fileInfo of fileInfos) {
      try {
        const copySuccess = this.copyFileWithValidation(fileInfo);
        if (copySuccess) {
          result.success++;
          result.successFiles.push(fileInfo);
        } else {
          result.failed++;
          result.errors.push({
            file: fileInfo.queryName,
            message: 'Copy operation returned false'
          });
        }
      } catch (error) {
        result.failed++;
        result.errors.push({
          file: fileInfo.queryName,
          message: error.message
        });
      }
    }

    return result;
  }

  /**
   * Copy file dengan validasi
   * @param {Object} fileInfo - File information object
   * @returns {boolean} True jika berhasil
   * @throws {Error} Jika copy gagal
   */
  static copyFileWithValidation(fileInfo) {
    if (!fs.existsSync(fileInfo.source)) {
      throw new Error(`Source file not found: ${fileInfo.source}`);
    }

    console.log(`Copying SQL file: ${fileInfo.originalPath} -> ${path.basename(fileInfo.dest)}`);
    
    try {
      // Copy file
      fs.copyFileSync(fileInfo.source, fileInfo.dest);
      
      // Verifikasi copy result
      if (!fs.existsSync(fileInfo.dest)) {
        throw new Error('Destination file was not created');
      }
      
      const sourceStats = fs.statSync(fileInfo.source);
      const destStats = fs.statSync(fileInfo.dest);
      
      if (destStats.size === 0) {
        throw new Error('Destination file is empty after copy');
      }
      
      if (sourceStats.size !== destStats.size) {
        console.warn(`File size berbeda: source ${sourceStats.size} vs dest ${destStats.size} bytes`);
      }
      
      console.log(`SQL file ${fileInfo.queryName} copied successfully (${destStats.size} bytes)`);
      return true;
      
    } catch (error) {
      throw new Error(`Copy operation failed: ${error.message}`);
    }
  }

  /**
   * Membaca file dengan error handling yang baik
   * @param {string} filePath - Path file yang akan dibaca
   * @param {string} encoding - Encoding file (default: utf8)
   * @returns {string} Isi file
   * @throws {Error} Jika file tidak bisa dibaca
   */
  static readFileWithErrorHandling(filePath, encoding = 'utf8') {
    try {
      if (!fs.existsSync(filePath)) {
        throw new Error(`File not found: ${filePath}`);
      }

      const stats = fs.statSync(filePath);
      if (stats.size === 0) {
        console.warn(`Warning: File is empty: ${filePath}`);
        return '';
      }

      if (stats.size > 10 * 1024 * 1024) { // 10MB limit
        throw new Error(`File terlalu besar (${Math.round(stats.size / 1024 / 1024)}MB): ${filePath}`);
      }

      const content = fs.readFileSync(filePath, encoding);
      return content;
    } catch (error) {
      throw new Error(`Error reading file ${filePath}: ${error.message}`);
    }
  }

  /**
   * Menulis file dengan backup dan error handling
   * @param {string} filePath - Path file yang akan ditulis
   * @param {string} content - Konten yang akan ditulis
   * @param {boolean} createBackup - Apakah membuat backup jika file sudah ada
   * @returns {boolean} True jika berhasil
   */
  static writeFileWithBackup(filePath, content, createBackup = true) {
    try {
      // Buat backup jika file sudah ada dan createBackup = true
      if (createBackup && fs.existsSync(filePath)) {
        const backupPath = `${filePath}.backup.${Date.now()}`;
        fs.copyFileSync(filePath, backupPath);
        console.log(`Backup dibuat: ${path.basename(backupPath)}`);
      }

      // Pastikan direktori ada
      const dir = path.dirname(filePath);
      this.ensureDirectoryExists(dir, false);

      // Tulis file
      fs.writeFileSync(filePath, content, 'utf8');

      // Verifikasi
      if (!fs.existsSync(filePath)) {
        throw new Error('File was not created after the write operation');
      }

      const stats = fs.statSync(filePath);
      console.log(`File written successfully: ${path.basename(filePath)} (${stats.size} bytes)`);
      
      return true;
    } catch (error) {
      throw new Error(`Error writing file ${filePath}: ${error.message}`);
    }
  }

  /**
   * Membersihkan old backup files
   * @param {string} directory - Directory yang akan dibersihkan
   * @param {number} maxAge - Maximum age dalam hari (default: 7)
   * @returns {number} Jumlah file yang dihapus
   */
  static cleanupOldBackups(directory, maxAge = 7) {
    try {
      if (!fs.existsSync(directory)) {
        return 0;
      }

      const files = fs.readdirSync(directory);
      const backupFiles = files.filter(file => file.includes('.backup.'));
      
      const maxAgeMs = maxAge * 24 * 60 * 60 * 1000;
      const now = Date.now();
      let deletedCount = 0;

      for (const file of backupFiles) {
        const filePath = path.join(directory, file);
        const stats = fs.statSync(filePath);
        
        if (now - stats.mtime.getTime() > maxAgeMs) {
          fs.unlinkSync(filePath);
          deletedCount++;
        }
      }

      if (deletedCount > 0) {
        console.log(`🗑️  Cleaned up ${deletedCount} old backup files from ${directory}`);
      }

      return deletedCount;
    } catch (error) {
      console.warn(`Warning: Error cleaning up backups in ${directory}:`, error.message);
      return 0;
    }
  }

  /**
   * Get file info dengan detail
   * @param {string} filePath - Path file
   * @returns {Object|null} File info object atau null jika file tidak ada
   */
  static getFileInfo(filePath) {
    try {
      if (!fs.existsSync(filePath)) {
        return null;
      }

      const stats = fs.statSync(filePath);
      
      return {
        path: filePath,
        name: path.basename(filePath),
        extension: path.extname(filePath),
        size: stats.size,
        sizeHuman: this.formatFileSize(stats.size),
        created: stats.birthtime,
        modified: stats.mtime,
        isFile: stats.isFile(),
        isDirectory: stats.isDirectory()
      };
    } catch (error) {
      console.warn(`Warning: Error getting file info for ${filePath}:`, error.message);
      return null;
    }
  }

  /**
   * Format file size ke human readable
   * @param {number} bytes - Ukuran dalam bytes
   * @returns {string} Formatted size
   */
  static formatFileSize(bytes) {
    if (bytes === 0) return '0 Bytes';
    
    const k = 1024;
    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }
}

module.exports = FileUtils;