/**
 * Error handling utilities for AEM Asset Upload
 * Provides consistent error reporting across all upload examples
 */

const { logError } = require('./utils');

/**
 * Extracts detailed error information from various error structures
 * @param {Object|string} err - Error object or string
 * @returns {Object} Normalized error information
 */
function extractErrorDetails(err) {
  const details = {
    fileName: 'Unknown file',
    message: 'Unknown error',
    statusCode: null
  };

  // Handle null/undefined errors
  if (!err) {
    details.message = 'Error object is null or undefined';
    return details;
  }

  // Extract file name from various possible locations
  if (err.fileName) {
    details.fileName = err.fileName;
  } else if (err.targetFile) {
    details.fileName = err.targetFile;
  } else if (err.name && err.name !== 'Error') {
    // Only use 'name' if it's not the default Error name
    details.fileName = err.name;
  }

  // Extract error message from various structures
  if (typeof err === 'string') {
    details.message = err;
  } else if (err.message) {
    // Direct message property (handles non-enumerable properties)
    details.message = String(err.message);
    
    // Add error code if available for more context
    if (err.code) {
      details.message = `[${err.code}] ${details.message}`;
    }
  } else if (err.error) {
    // Nested error object
    if (typeof err.error === 'string') {
      details.message = err.error;
    } else if (err.error.message) {
      details.message = String(err.error.message);
    } else {
      // Try to stringify with all properties including non-enumerable
      try {
        details.message = JSON.stringify(err.error, Object.getOwnPropertyNames(err.error), 2);
      } catch (e) {
        details.message = String(err.error);
      }
    }
  } else {
    // Last resort: try multiple approaches to get error info
    try {
      // First try with all property names (including non-enumerable)
      const allProps = Object.getOwnPropertyNames(err);
      if (allProps.length > 0) {
        details.message = JSON.stringify(err, allProps, 2);
      } else {
        details.message = JSON.stringify(err, null, 2);
      }
    } catch (stringifyError) {
      // If all else fails, convert to string
      try {
        details.message = String(err);
      } catch (toStringError) {
        details.message = `[Error object could not be converted: ${toStringError.message}]`;
      }
    }
  }

  // Extract HTTP status code from various possible locations
  if (err.statusCode) {
    details.statusCode = err.statusCode;
  } else if (err.status) {
    details.statusCode = err.status;
  } else if (err.error) {
    if (err.error.statusCode) {
      details.statusCode = err.error.statusCode;
    } else if (err.error.status) {
      details.statusCode = err.error.status;
    }
  }

  return details;
}

/**
 * Logs a single error with appropriate formatting
 * @param {Object|string} err - Error object or string
 */
function logUploadError(err) {
  try {
    const { fileName, message, statusCode } = extractErrorDetails(err);
    
    if (statusCode) {
      logError(`${fileName}: HTTP ${statusCode} - ${message}`);
    } else {
      logError(`${fileName}: ${message}`);
    }
  } catch (extractError) {
    // If error extraction fails, show raw error
    logError(`Error processing error object: ${extractError.message}`);
    if (process.env.DEBUG === 'true') {
      console.error('Failed to process error:', err);
      console.error('Extract error:', extractError.stack);
    }
  }
}

/**
 * Logs multiple upload errors
 * @param {Array} errors - Array of error objects
 */
function logUploadErrors(errors) {
  if (!errors || errors.length === 0) {
    return;
  }

  console.log('\n❌ Errors encountered:');
  
  // In DEBUG mode, show detailed error structure
  if (process.env.DEBUG === 'true') {
    console.log('\n🔍 Debug: Raw error structure:');
    try {
      // For each error, show all properties including non-enumerable ones
      errors.forEach((err, index) => {
        console.log(`\nError ${index + 1}:`);
        if (err && typeof err === 'object') {
          // Get all property names including non-enumerable
          const allProps = Object.getOwnPropertyNames(err);
          console.log('  All properties:', allProps.join(', '));
          
          // Show each property value
          allProps.forEach(prop => {
            try {
              const value = err[prop];
              console.log(`  ${prop}:`, value);
            } catch (e) {
              console.log(`  ${prop}: [Cannot access: ${e.message}]`);
            }
          });
        } else {
          console.log('  Value:', err);
        }
      });
    } catch (e) {
      console.log('Could not display error structure:', e.message);
    }
    console.log('');
  }
  
  errors.forEach((err, index) => {
    logUploadError(err);
  });
  console.log('');
}

/**
 * Logs a caught exception with full details
 * @param {Error} error - Caught exception
 * @param {string} context - Context description (e.g., "Upload failed")
 */
function logException(error, context = 'Operation failed') {
  logError(`${context}: ${error.message || error}`);
  
  // Show HTTP status code if available
  if (error.statusCode || error.status) {
    console.error(`HTTP Status: ${error.statusCode || error.status}`);
  }
  
  // Show response body if available (contains detailed error info)
  if (error.response) {
    console.error('Response:', JSON.stringify(error.response, null, 2));
  }
  
  // Show full error object in debug mode
  if (process.env.DEBUG === 'true') {
    console.error('\nFull error object:');
    console.error(JSON.stringify(error, Object.getOwnPropertyNames(error), 2));
  }
  
  // Show stack trace
  if (error.stack) {
    console.error('\nStack trace:');
    console.error(error.stack);
  }
}

/**
 * Checks if upload result has errors and handles them
 * @param {Object} uploadResult - Upload result from aem-upload library
 * @returns {boolean} True if there are errors, false otherwise
 */
function hasUploadErrors(uploadResult) {
  const errors = uploadResult.errors || [];
  return errors.length > 0;
}

/**
 * Analyzes upload result and categorizes files by success/failure
 * @param {Object} uploadResult - Upload result from aem-upload library
 * @returns {Object} Analysis with successfulFiles, failedFiles, and errorSummary
 */
function analyzeUploadResult(uploadResult) {
  const detailedResults = uploadResult.detailedResult || [];
  const successfulFiles = [];
  const failedFiles = [];
  const errorSummary = {};

  detailedResults.forEach(item => {
    const fileInfo = item.result || item;
    const fileErrors = fileInfo.errors || [];
    
    if (fileErrors.length > 0) {
      // File failed
      failedFiles.push({
        fileName: fileInfo.fileName || fileInfo.targetFile,
        targetFile: fileInfo.targetFile,
        errors: fileErrors
      });
      
      // Collect unique error types
      fileErrors.forEach(err => {
        const errorKey = `${err.code || 'UNKNOWN'}: ${err.message || 'Unknown error'}`;
        if (!errorSummary[errorKey]) {
          errorSummary[errorKey] = {
            code: err.code,
            message: err.message,
            count: 0,
            files: []
          };
        }
        errorSummary[errorKey].count++;
        errorSummary[errorKey].files.push(fileInfo.fileName || fileInfo.targetFile);
      });
    } else {
      // File succeeded
      successfulFiles.push(fileInfo);
    }
  });

  return {
    totalFiles: detailedResults.length,
    successfulFiles,
    failedFiles,
    errorSummary,
    hasErrors: failedFiles.length > 0
  };
}

/**
 * Displays upload error summary in a user-friendly format
 * @param {Object} errorSummary - Error summary from analyzeUploadResult
 */
function displayErrorSummary(errorSummary) {
  const chalk = require('chalk');
  
  console.log('\n❌ Upload Errors:');
  console.log(chalk.gray('─'.repeat(50)));
  
  Object.values(errorSummary).forEach(error => {
    console.log(`\n${chalk.red('●')} ${chalk.bold(error.code || 'ERROR')}`);
    console.log(`  Message: ${error.message}`);
    console.log(`  Affected files (${error.count}):`);
    error.files.forEach(file => {
      console.log(`    → ${file}`);
    });
  });
  
  console.log('\n' + chalk.gray('─'.repeat(50)));
}

/**
 * Displays upload summary with success/failure counts
 * @param {Object} analysis - Analysis result from analyzeUploadResult
 * @param {number} totalTime - Total time in milliseconds
 */
function displayUploadSummary(analysis, totalTime) {
  const chalk = require('chalk');
  const { formatTime } = require('./utils');
  
  console.log('\n' + chalk.bold('Upload Summary:'));
  console.log(chalk.gray('─'.repeat(50)));
  console.log(`Total files: ${chalk.cyan(analysis.totalFiles)}`);
  console.log(`Successful: ${chalk.green(analysis.successfulFiles.length)}`);
  console.log(`Failed: ${chalk.red(analysis.failedFiles.length)}`);
  console.log(`Total time: ${chalk.yellow(formatTime(totalTime))}`);
  console.log(chalk.gray('─'.repeat(50)));
}

module.exports = {
  extractErrorDetails,
  logUploadError,
  logUploadErrors,
  logException,
  hasUploadErrors,
  analyzeUploadResult,
  displayErrorSummary,
  displayUploadSummary
};

