const fs = require('fs-extra');
const path = require('path');
const { getDestinationDecision, getCadInfo } = require('./router');
const { buildDestinationIndex } = require('./destinationIndex');
const { buildExistingCadKeyIndex, toCadKey } = require('./duplicateIndex');
const { prepareUnroutedTarget, prepareDuplicateTarget } = require('./unrouted');

function parseNumericVersion(version) {
  const rawVersion = String(version || '').trim();
  if (!rawVersion || !/^\d+$/.test(rawVersion)) {
    return null;
  }

  try {
    return BigInt(rawVersion);
  } catch {
    return null;
  }
}

function buildHighestNumericVersionByCadKey(files) {
  const index = new Map();

  for (const row of files) {
    const cadInfo = getCadInfo(row.fileName);
    if (!cadInfo) {
      continue;
    }

    const version = parseNumericVersion(cadInfo.version);
    if (version === null) {
      continue;
    }

    const key = toCadKey(cadInfo);
    const current = index.get(key);
    if (current === undefined || version > current) {
      index.set(key, version);
    }
  }

  return index;
}

async function collectFilesRecursively(rootDir) {
  const files = [];

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

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

      if (entry.isDirectory()) {
        await walk(fullPath);
        continue;
      }

      if (entry.isFile()) {
        files.push({ fullPath, fileName: entry.name });
      }
    }
  }

  await walk(rootDir);
  return files;
}

async function processFolder(folder, config) {
  const files = await collectFilesRecursively(folder);
  const destinationIndex = await buildDestinationIndex(config?.destination);
  const existingCadKeys = await buildExistingCadKeyIndex(config?.destination);
  const sourceMaxVersions = buildHighestNumericVersionByCadKey(files);
  const result = {
    scanned: 0,
    copied: 0,
    skipped: 0,
    unrouted: 0,
    duplicates: 0,
    details: [],
  };

  for (const { fullPath, fileName } of files) {
    const file = fileName;
    result.scanned += 1;

    const cadInfo = getCadInfo(file);
    if (!cadInfo) {
      result.skipped += 1;
      continue;
    }

    if (existingCadKeys.has(toCadKey(cadInfo))) {
      const incomingVersion = parseNumericVersion(cadInfo.version);
      const highestInSource = sourceMaxVersions.get(toCadKey(cadInfo));
      if (incomingVersion !== null && highestInSource !== undefined && incomingVersion < highestInSource) {
        result.details.push({
          file,
          reason: 'Versione piu alta presente nei file da smistare',
        });
        continue;
      }

      const duplicateTarget = await prepareDuplicateTarget(file);
      if (!duplicateTarget.shouldCopy) {
        result.details.push({
          file,
          destination: duplicateTarget.destinationDir,
          reason: duplicateTarget.reason || 'Versione piu alta gia presente',
        });
        continue;
      }

      await fs.copy(fullPath, duplicateTarget.destinationPath, { overwrite: false });

      result.copied += 1;
      result.duplicates += 1;
      result.details.push({
        file,
        destination: duplicateTarget.destinationDir,
        reason: 'Duplicato gia presente prima dello smistamento',
      });
      continue;
    }

    const decision = getDestinationDecision(file, config, destinationIndex);
    const destDir = decision.destination;

    if (!destDir) {
      const unroutedTarget = await prepareUnroutedTarget(file);
      if (!unroutedTarget.shouldCopy) {
        result.details.push({
          file,
          destination: unroutedTarget.destinationDir,
          reason: unroutedTarget.reason || 'Versione piu alta gia presente',
        });
        continue;
      }

      await fs.copy(fullPath, unroutedTarget.destinationPath, { overwrite: false });

      result.copied += 1;
      result.unrouted += 1;
      result.details.push({
        file,
        destination: unroutedTarget.destinationDir,
        reason: decision.reason || 'Nessuna regola trovata',
      });
      continue;
    }

    const dest = path.join(destDir, file);
    await fs.copy(fullPath, dest, { overwrite: true });

    result.copied += 1;
    result.details.push({ file, destination: destDir });
  }

  return result;
}

module.exports = { processFolder };
