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 PRIMARY_SKIPPED_DIR = '/cadroute/__SALTATI'; const HOME_SKIPPED_DIR = path.join(os.homedir(), '.cadroute', '__SALTATI'); const SPECIAL_TARGETS = { unrouted: { primary: PRIMARY_UNROUTED_DIR, fallback: HOME_UNROUTED_DIR, }, duplicates: { primary: PRIMARY_DUPLICATES_DIR, fallback: HOME_DUPLICATES_DIR, }, skipped: { primary: PRIMARY_SKIPPED_DIR, fallback: HOME_SKIPPED_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 resolveSkippedDir() { return resolveTargetDir('skipped'); } async function getSkippedTarget(fileName) { return getTarget('skipped', fileName); } async function listSkippedFiles() { return listTargetFiles('skipped'); } async function clearSkippedFiles() { return clearTargetFiles('skipped'); } 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, getSkippedTarget, prepareUnroutedTarget, prepareDuplicateTarget, resolveUnroutedDir, resolveDuplicatesDir, resolveSkippedDir, listUnroutedFiles, listDuplicateFiles, listSkippedFiles, clearUnroutedFiles, clearDuplicateFiles, clearSkippedFiles, PRIMARY_UNROUTED_DIR, HOME_UNROUTED_DIR, PRIMARY_DUPLICATES_DIR, HOME_DUPLICATES_DIR, PRIMARY_SKIPPED_DIR, HOME_SKIPPED_DIR, };