Source: contentSizeManager.js

const path = require('path');
const fs = require('fs').promises;

/**
 * Manages content size and file limitations for a project.
 * @class
 * @classdesc Handles file processing decisions based on size limits, file count limits, and allowed extensions.
 * Provides functionality to estimate project size and track statistics of processed files.
 *
 * @param {?number} maxSize - Maximum allowed size per file in KB. Null means no limit.
 * @param {?number} maxFiles - Maximum number of files to process. Null means no limit.
 * @param {string[]} extensions - Array of allowed file extensions (e.g., ['.js', '.txt'])
 *
 * @property {number} currentSize - Current total size of processed files in KB
 * @property {number} fileCount - Current count of processed files
 * @property {Set<string>} allowedExtensions - Set of allowed file extensions
 * @property {Object} stats - Statistics tracking object
 * @property {number} stats.totalFiles - Total number of processed files
 * @property {number} stats.totalSize - Total size of processed files in KB
 * @property {number} stats.skippedFiles - Number of files skipped
 * @property {number} stats.skippedSize - Total size of skipped files in KB
 */
class ContentSizeManager {
  constructor(maxSize = null, maxFiles = null, extensions = []) {
    this.maxSize = maxSize;
    this.maxFiles = maxFiles;
    this.currentSize = 0;
    this.fileCount = 0;
    this.allowedExtensions = new Set(extensions);
    this.stats = {
      totalFiles: 0,
      totalSize: 0,
      skippedFiles: 0,
      skippedSize: 0,
    };
  }

  async estimateProjectSize(rootDir, ignoreProcessor) {
    const estimate = {
      totalFiles: 0,
      totalSize: 0,
      estimatedProcessingTime: 0,
    };

    async function traverseDirectory(currentDir) {
      const entries = await fs.readdir(currentDir, { withFileTypes: true });

      for (const entry of entries) {
        const fullPath = path.join(currentDir, entry.name);
        const relativePath = path.relative(rootDir, fullPath);

        if (ignoreProcessor && ignoreProcessor.isIgnored(relativePath)) {
          continue;
        }

        if (entry.isDirectory()) {
          await traverseDirectory(fullPath);
        } else if (entry.isFile()) {
          const stats = await fs.stat(fullPath);
          estimate.totalFiles++;
          estimate.totalSize += stats.size;
        }
      }
    }

    await traverseDirectory(rootDir);
    estimate.estimatedProcessingTime = Math.ceil(estimate.totalSize / (1024 * 1024));
    estimate.totalSize = Math.ceil(estimate.totalSize / 1024); // Convert to KB

    return estimate;
  }

  shouldProcessFile(filePath, fileSize) {
    const fileSizeKB = fileSize / 1024;
    const ext = path.extname(filePath);

    const result = {
      shouldProcess: true,
      shouldCompact: false,
      reason: null,
    };

    if (this.allowedExtensions.size > 0 && !this.allowedExtensions.has(ext)) {
      result.shouldProcess = false;
      result.reason = 'Unsupported file extension';
      return result;
    }

    if (this.maxFiles !== null && this.fileCount >= this.maxFiles) {
      result.shouldProcess = false;
      result.reason = 'Maximum file limit reached';
      return result;
    }

    if (this.maxSize !== null && fileSizeKB > this.maxSize) {
      result.shouldProcess = true;
      result.shouldCompact = true;
      result.reason = 'File exceeds size limit, will be compacted';
    }

    if (result.shouldProcess) {
      this.fileCount++;
      this.currentSize += fileSizeKB;
    }

    return result;
  }

  updateStats(fileSize, skipped) {
    const fileSizeKB = fileSize / 1024;
    if (skipped) {
      this.stats.skippedFiles++;
      this.stats.skippedSize += fileSizeKB;
    } else {
      this.stats.totalFiles++;
      this.stats.totalSize += fileSizeKB;
    }
  }

  getStats() {
    return { ...this.stats };
  }
}

module.exports = ContentSizeManager;