/**
 * Include resolver cache state and cache utility helpers.
 */

import * as fs from "fs";

const includeSymbolCache = new Map();
const includeSymbolWarmInFlight = new Map();
const MAX_INCLUDE_RECURSION_DEPTH = 64;

// Short-lived cache for fs.stat results. Prevents repeated syscalls for the
// same file within a tight validation window (e.g. multiple documents sharing
// the same include graph validating close together). Entries expire after
// STAT_CACHE_TTL_MS and are evicted lazily on the next access for that path.
const statMetadataCache = new Map();
const STAT_CACHE_TTL_MS = 200;

function getFileMetadata(filePath) {
    const now = Date.now();
    const cached = statMetadataCache.get(filePath);
    if (cached && cached.expiresAt > now) {
        return cached.metadata;
    }
    try {
        const stat = fs.statSync(filePath);
        const metadata = { mtimeMs: stat.mtimeMs, size: stat.size };
        statMetadataCache.set(filePath, { metadata, expiresAt: now + STAT_CACHE_TTL_MS });
        return metadata;
    } catch (error) {
        statMetadataCache.delete(filePath);
        return null;
    }
}

async function getFileMetadataAsync(filePath) {
    const now = Date.now();
    const cached = statMetadataCache.get(filePath);
    if (cached && cached.expiresAt > now) {
        return cached.metadata;
    }
    try {
        const stat = await fs.promises.stat(filePath);
        const metadata = { mtimeMs: stat.mtimeMs, size: stat.size };
        statMetadataCache.set(filePath, { metadata, expiresAt: now + STAT_CACHE_TTL_MS });
        return metadata;
    } catch (error) {
        statMetadataCache.delete(filePath);
        return null;
    }
}

function evictStatCacheForPath(filePath) {
    statMetadataCache.delete(filePath);
}

function isCacheEntryFresh(entry, metadata) {
    if (!entry || !metadata) {
        return false;
    }

    return entry.mtimeMs === metadata.mtimeMs && entry.size === metadata.size;
}

function cloneSymbols(symbols) {
    return {
        functions: new Set(symbols.functions),
        variables: new Map(symbols.variables),
        functionCaseMap: new Map(symbols.functionCaseMap),
        initialisedVariables: new Set(symbols.initialisedVariables || [])
    };
}

function clonePathSet(values) {
    return new Set(values);
}

export {
    includeSymbolCache,
    includeSymbolWarmInFlight,
    MAX_INCLUDE_RECURSION_DEPTH,
    getFileMetadata,
    getFileMetadataAsync,
    evictStatCacheForPath,
    isCacheEntryFresh,
    cloneSymbols,
    clonePathSet
};
