const fs = require('fs-extra');
const path = require('path');
const os = require('os');
const { getCadInfo } = require('./router');

const PRIMARY_UNROUTED_DIR = '/cadroute/__NON_SMISTATI';
const HOME_UNROUTED_DIR = path.join(os.homedir(), '.cadroute', '__NON_SMISTATI');
const PRIMARY_DUPLICATES_DIR = '/cadroute/duplicati';
const HOME_DUPLICATES_DIR = path.join(os.homedir(), '.cadroute', 'duplicati');

const SPECIAL_TARGETS = {
  unrouted: {
    primary: PRIMARY_UNROUTED_DIR,
    fallback: HOME_UNROUTED_DIR,
  },
  duplicates: {
    primary: PRIMARY_DUPLICATES_DIR,
    fallback: HOME_DUPLICATES_DIR,
  },
};

const resolvedDirs = new Map();

async function resolveTargetDir(kind) {
  const target = SPECIAL_TARGETS[kind];
  if (!target) {
    throw new Error(`Target speciale non supportato: ${kind}`);
  }

  if (!resolvedDirs.has(kind)) {
    resolvedDirs.set(
      kind,
      (async () => {
        try {
          await fs.ensureDir(target.primary);
          return target.primary;
        } catch {
          await fs.ensureDir(target.fallback);
          return target.fallback;
        }
      })()
    );
  }

  return resolvedDirs.get(kind);
}

async function resolveUnroutedDir() {
  return resolveTargetDir('unrouted');
}

async function resolveDuplicatesDir() {
  return resolveTargetDir('duplicates');
}

async function getUniquePath(destinationDir, fileName) {
  const parsed = path.parse(fileName);
  let candidate = path.join(destinationDir, fileName);
  let counter = 1;

  while (await fs.pathExists(candidate)) {
    candidate = path.join(destinationDir, `${parsed.name}__${counter}${parsed.ext}`);
    counter += 1;
  }

  return candidate;
}

async function getTarget(kind, fileName) {
  const destinationDir = await resolveTargetDir(kind);
  const destinationPath = await getUniquePath(destinationDir, fileName);

  return {
    destinationDir,
    destinationPath,
  };
}

async function getUnroutedTarget(fileName) {
  return getTarget('unrouted', fileName);
}

async function getDuplicateTarget(fileName) {
  return getTarget('duplicates', fileName);
}

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

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

function normalizeCadKey(cadInfo) {
  return String(cadInfo?.key || '').toLowerCase();
}

async function findComparableVersionFiles(destinationDir, incomingCadInfo) {
  const incomingKey = normalizeCadKey(incomingCadInfo);
  const entries = await fs.readdir(destinationDir, { withFileTypes: true }).catch(() => []);
  const comparable = [];

  for (const entry of entries) {
    if (!entry.isFile()) {
      continue;
    }

    const cadInfo = getCadInfo(entry.name);
    if (!cadInfo || normalizeCadKey(cadInfo) !== incomingKey) {
      continue;
    }

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

    comparable.push({
      path: path.join(destinationDir, entry.name),
      version: numericVersion,
      name: entry.name,
    });
  }

  return comparable;
}

async function prepareSpecialTarget(kind, fileName) {
  const destinationDir = await resolveTargetDir(kind);
  const incomingCadInfo = getCadInfo(fileName);
  if (!incomingCadInfo) {
    const destinationPath = await getUniquePath(destinationDir, fileName);
    return { shouldCopy: true, destinationDir, destinationPath };
  }

  const incomingVersion = parseNumericVersion(incomingCadInfo.version);
  if (incomingVersion === null) {
    const destinationPath = await getUniquePath(destinationDir, fileName);
    return { shouldCopy: true, destinationDir, destinationPath };
  }

  const comparable = await findComparableVersionFiles(destinationDir, incomingCadInfo);
  if (!comparable.length) {
    const destinationPath = await getUniquePath(destinationDir, fileName);
    return { shouldCopy: true, destinationDir, destinationPath };
  }

  const highest = comparable.reduce((max, row) => (row.version > max.version ? row : max));
  if (incomingVersion <= highest.version) {
    return {
      shouldCopy: false,
      destinationDir,
      reason: `Versione piu alta gia presente (${highest.name})`,
    };
  }

  await Promise.all(comparable.map((row) => fs.remove(row.path)));
  const destinationPath = await getUniquePath(destinationDir, fileName);
  return {
    shouldCopy: true,
    destinationDir,
    destinationPath,
    cleaned: comparable.length,
  };
}

async function prepareUnroutedTarget(fileName) {
  return prepareSpecialTarget('unrouted', fileName);
}

async function prepareDuplicateTarget(fileName) {
  return prepareSpecialTarget('duplicates', fileName);
}

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

  async function walk(currentDir) {
    let entries;
    try {
      entries = await fs.readdir(currentDir, { withFileTypes: true });
    } catch {
      return;
    }

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

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

      if (!entry.isFile()) {
        continue;
      }

      const stats = await fs.stat(fullPath).catch(() => null);
      if (!stats) {
        continue;
      }

      files.push({
        name: entry.name,
        relativePath: path.relative(rootDir, fullPath),
        size: stats.size,
        updatedAt: stats.mtime.toISOString(),
      });
    }
  }

  await walk(rootDir);
  files.sort((a, b) => a.relativePath.localeCompare(b.relativePath, 'it'));
  return files;
}

async function listTargetFiles(kind) {
  const directory = await resolveTargetDir(kind);
  const files = await listFilesRecursively(directory);
  return { directory, files };
}

async function clearTargetFiles(kind) {
  const directory = await resolveTargetDir(kind);
  await fs.emptyDir(directory);
  return { directory };
}

async function listUnroutedFiles() {
  return listTargetFiles('unrouted');
}

async function listDuplicateFiles() {
  return listTargetFiles('duplicates');
}

async function clearUnroutedFiles() {
  return clearTargetFiles('unrouted');
}

async function clearDuplicateFiles() {
  return clearTargetFiles('duplicates');
}

module.exports = {
  getUnroutedTarget,
  getDuplicateTarget,
  prepareUnroutedTarget,
  prepareDuplicateTarget,
  resolveUnroutedDir,
  resolveDuplicatesDir,
  listUnroutedFiles,
  listDuplicateFiles,
  clearUnroutedFiles,
  clearDuplicateFiles,
  PRIMARY_UNROUTED_DIR,
  HOME_UNROUTED_DIR,
  PRIMARY_DUPLICATES_DIR,
  HOME_DUPLICATES_DIR,
};
