/**
 * Example: Batch Upload with Error Handling
 * 
 * This example demonstrates PRODUCTION-READY upload patterns:
 * - Uploading multiple directories/files in separate batches
 * - Automatic retry logic with exponential backoff (up to 3 retries)
 * - Graceful error handling and recovery
 * - Performance metrics and upload speed tracking
 * 
 * KEY DIFFERENCES from FileSystemUpload example:
 * - Retry Logic: Auto-retries failed batches (production resilience)
 * - Batch Processing: Handles large uploads by splitting into manageable chunks
 * - Error Recovery: Continues uploading even if some batches fail
 * 
 * USE THIS APPROACH WHEN:
 * - Uploading to production environments (needs retry logic)
 * - Uploading large numbers of files (1000+)
 * - Network reliability is a concern
 * - You need detailed progress tracking per batch
 */

const { FileSystemUpload, FileSystemUploadOptions } = require('@adobe/aem-upload');
const fs = require('fs');
const path = require('path');
const { validateConfig, getUploadOptions, getTargetFolder } = require('../src/config');
const { 
  logSuccess, 
  logError, 
  logInfo,
  logWarning, 
  createSpinner,
  formatBytes,
  formatTime,
  getAemAssetsUrl
} = require('../src/utils');
const { 
  analyzeUploadResult, 
  displayUploadSummary, 
  displayErrorSummary, 
  logException 
} = require('../src/errorHandler');

/**
 * Uploads paths (files or directories) in batches with retry logic
 * @param {Array<string>} paths - Array of file or directory paths
 * @param {Object} options - Upload options
 * @param {string} targetFolder - Target folder in AEM
 * @param {number} batchSize - Number of items per batch (default: 2 for demo, use 10-50 for production)
 */
async function uploadInBatches(paths, options, targetFolder, batchSize = 2) {
  const allResults = [];
  const totalPaths = paths.length;
  const totalBatches = Math.ceil(totalPaths / batchSize);

  logInfo(`Processing ${totalPaths} item(s) in ${totalBatches} batch(es)`);

  for (let i = 0; i < totalPaths; i += batchSize) {
    const batchNumber = Math.floor(i / batchSize) + 1;
    const batch = paths.slice(i, i + batchSize);
    
    console.log(`\n${'='.repeat(50)}`);
    logInfo(`Batch ${batchNumber}/${totalBatches} - Uploading ${batch.length} item(s)`);
    console.log('='.repeat(50));

    const batchStartTime = Date.now();
    let retryCount = 0;
    const maxRetries = 3;
    let batchResults = null;

    // Retry logic for failed batches
    while (retryCount <= maxRetries) {
      try {
        // Create a fresh upload instance for each retry to avoid duplicate event listeners
        const upload = new FileSystemUpload();
        
        const fullUrl = `${options.url}${targetFolder}`;
        
        const uploadOptions = new FileSystemUploadOptions()
          .withUrl(fullUrl)
          .withDeepUpload(true);  // Enable recursive upload of subdirectories
        
        // Add HTTP options including headers (auth is already in headers from config)
        uploadOptions.withHttpOptions({
          headers: {
            ...options.headers,
            'X-Upload-Source': 'Batch-Upload-Example',
            'X-Batch-Number': batchNumber
          }
        });

        // Track progress - attach listeners to upload instance
        upload.on('foldercreated', (data) => {
          logSuccess(`Created folder: ${data.folderName} at ${data.targetFolder}`);
        });
        
        let currentFile = '';
        upload.on('filestart', (data) => {
          currentFile = data.fileName;
          logInfo(`Starting: ${currentFile}`);
        });

        upload.on('fileprogress', (data) => {
          const percentage = ((data.transferred / data.fileSize) * 100).toFixed(1);
          process.stdout.write(
            `\r  Progress: ${percentage}% - ${formatBytes(data.transferred)}/${formatBytes(data.fileSize)}`
          );
        });

        upload.on('fileend', (data) => {
          process.stdout.write('\n');
          logSuccess(`Completed: ${data.fileName}`);
        });

        upload.on('fileerror', (data) => {
          // Only show in DEBUG mode (may be retries)
          if (process.env.DEBUG === 'true') {
            process.stdout.write('\n');
            const errorMsg = data.error?.message || data.message || 'Unknown error';
            logWarning(`Error (may retry): ${data.fileName} - ${errorMsg}`);
          }
        });

        // Perform upload and wait for batch completion
        const uploadResult = await upload.upload(uploadOptions, batch);
        
        const batchEndTime = Date.now();
        const batchTime = batchEndTime - batchStartTime;
        
        logSuccess(`Batch ${batchNumber} completed in ${formatTime(batchTime)}`);
        
        // Extract detailed results from the upload result
        batchResults = uploadResult.detailedResult || [];
        break; // Success, exit retry loop

      } catch (error) {
        retryCount++;
        if (retryCount <= maxRetries) {
          logWarning(`Batch ${batchNumber} failed. Retry ${retryCount}/${maxRetries}...`);
          await new Promise(resolve => setTimeout(resolve, 2000 * retryCount)); // Exponential backoff
        } else {
          logError(`Batch ${batchNumber} failed after ${maxRetries} retries: ${error.message}`);
          // Mark all files in batch as failed
          batchResults = batch.map(file => ({
            fileName: path.basename(file),
            error: error,
            success: false
          }));
        }
      }
    }

    if (batchResults) {
      allResults.push(...batchResults);
    }
  }

  return allResults;
}

/**
 * Main function
 */
async function main() {
  try {
    // Validate configuration
    validateConfig();
    logSuccess('Configuration validated successfully');

    // Get upload options (now async to support service credentials)
    const options = await getUploadOptions();
    const targetFolder = getTargetFolder();

    logInfo(`AEM URL: ${options.url}`);
    logInfo(`Target folder: ${targetFolder}`);

    // Get sample files/directories to upload
    const sampleAssetsDir = path.join(__dirname, '../sample-assets');
    
    if (!fs.existsSync(sampleAssetsDir)) {
      logError(`Sample assets directory not found: ${sampleAssetsDir}`);
      process.exit(1);
    }

    // Get subdirectories and root files to upload in batches
    // This demonstrates batching while maintaining folder structure
    // Each subdirectory is treated as a batch item
    function getUploadPaths(dir) {
      const entries = fs.readdirSync(dir, { withFileTypes: true });
      const paths = [];
      
      for (const entry of entries) {
        if (entry.name.startsWith('.')) continue; // Skip hidden files/folders
        
        const fullPath = path.join(dir, entry.name);
        if (entry.isDirectory()) {
          // Add subdirectories (they'll be uploaded with their contents)
          paths.push(fullPath);
        } else if (entry.isFile()) {
          // Add root-level files
          paths.push(fullPath);
        }
      }
      
      return paths;
    }

    const uploadPaths = getUploadPaths(sampleAssetsDir);
    
    if (uploadPaths.length === 0) {
      logError('No files or directories found in sample-assets directory');
      process.exit(1);
    }

    logInfo(`Source directory: ${sampleAssetsDir}`);
    logInfo(`Found ${uploadPaths.length} item(s) to upload in batches (directories + files)`);
    logInfo(`Batch size: 2 (small for demo, use 10-50 for production)`);
    logInfo(`Retry logic: 3 attempts with exponential backoff`);

    console.log('\n--- Starting Upload ---\n');

    const startTime = Date.now();

    // Upload in batches
    // Batch size of 2 is intentionally small to demonstrate batching with our sample data
    // Each batch can contain directories (uploaded recursively) or individual files
    // For production use with many items, increase to 10-50 for optimal performance
    const batchSize = 2;
    const results = await uploadInBatches(uploadPaths, options, targetFolder, batchSize);

    const totalTime = Date.now() - startTime;

    // Analyze results using shared function
    // Convert batch results to uploadResult format
    const uploadResult = { detailedResult: results };
    const analysis = analyzeUploadResult(uploadResult);

    // Display summary
    displayUploadSummary(analysis, totalTime);

    // Check for errors
    if (analysis.hasErrors) {
      // Show error details
      displayErrorSummary(analysis.errorSummary);
      logError('\nBatch upload failed. Please fix the errors above and try again.');
      console.log('');
      process.exit(1);
    } else {
      logSuccess('\nAll files uploaded successfully!');
      
      // Display uploaded file paths in AEM with clickable folder URL
      if (analysis.successfulFiles.length > 0) {
        const folderUrl = getAemAssetsUrl(options.url, targetFolder);
        console.log(`\n✅ Successfully uploaded to AEM: ${folderUrl}`);
        analysis.successfulFiles.forEach(fileInfo => {
          // Extract the relative path from targetFile
          if (fileInfo.targetFile) {
            const relativePath = fileInfo.targetFile.replace(`${targetFolder}/`, '');
            console.log(`  → ${relativePath}`);
          } else {
            const fileName = fileInfo.fileName || 'Unknown';
            console.log(`  → ${fileName}`);
          }
        });
      }
      console.log('');
      process.exit(0);
    }

  } catch (error) {
    logException(error, 'Batch upload failed');
    process.exit(1);
  }
}

// Run the example
if (require.main === module) {
  console.log('\n=== Batch Upload Example ===\n');
  main();
}

module.exports = { main };

