"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_SCAN_SURFACE_THRESHOLDS = void 0;
exports.classifyScanSurface = classifyScanSurface;
exports.scanWorkspaceSurfaces = scanWorkspaceSurfaces;
const workspace_traversal_1 = require("../workspace-traversal");
exports.DEFAULT_SCAN_SURFACE_THRESHOLDS = Object.freeze({
    informationalBytes: 25_000_000,
    actionableEligibleBytes: 100_000_000,
    largestSurfaceCount: 20
});
const CLASSIFIED_SEGMENTS = {
    dependency: ["node_modules", "vendor", "bower_components"],
    generated: ["dist", "build", "generated", ".next", ".nuxt"],
    cache: [".cache", ".turbo", ".parcel-cache", ".vite"],
    release: ["release", "releases", "artifacts"],
    sandbox: ["sandbox", "tmp", "temp"],
    coverage: ["coverage", ".nyc_output"],
    "test-output": ["test-results", "playwright-report", "junit", "screenshots"]
};
function classifyScanSurface(input) {
    const segments = input.replace(/\\/g, "/").split("/").filter(Boolean);
    for (const [classification, names] of Object.entries(CLASSIFIED_SEGMENTS)) {
        if (segments.some((segment) => names.includes(segment)))
            return classification;
    }
    return "eligible";
}
function scanWorkspaceSurfaces(options) {
    const traversal = (0, workspace_traversal_1.traverseWorkspace)({
        workspaceRoot: options.workspaceRoot,
        startPath: options.scanPath,
        policy: options.policy,
        budgets: { ...workspace_traversal_1.DEFAULT_TRAVERSAL_BUDGETS, ...options.budgets },
        readContent: false,
        now: options.now
    });
    const thresholds = { ...exports.DEFAULT_SCAN_SURFACE_THRESHOLDS, ...options.thresholds };
    const surfaces = new Map();
    let approximateBytes = 0;
    for (const entry of traversal.entries) {
        if (entry.kind === "file")
            approximateBytes += entry.size;
        const normalized = entry.path === "." ? "." : entry.path.replace(/\\/g, "/");
        const classification = classifyScanSurface(normalized);
        const firstClassifiedIndex = normalized.split("/").findIndex((_, index, all) => classifyScanSurface(all.slice(0, index + 1).join("/")) !== "eligible");
        const surfacePath = firstClassifiedIndex >= 0
            ? normalized.split("/").slice(0, firstClassifiedIndex + 1).join("/")
            : (normalized.split("/")[0] || ".");
        const current = surfaces.get(surfacePath) ?? {
            path: surfacePath,
            classification,
            approximateBytes: 0,
            fileCount: 0,
            directoryCount: 0,
            excluded: classification !== "eligible"
        };
        if (entry.kind === "file") {
            current.fileCount += 1;
            current.approximateBytes += entry.size;
        }
        if (entry.kind === "directory")
            current.directoryCount += 1;
        surfaces.set(surfacePath, current);
    }
    const largestSurfaces = [...surfaces.values()]
        .sort((left, right) => right.approximateBytes - left.approximateBytes || left.path.localeCompare(right.path, "en"))
        .slice(0, thresholds.largestSurfaceCount);
    const residualEligiblePaths = largestSurfaces.filter((surface) => surface.classification === "eligible");
    const findings = [];
    if (approximateBytes >= thresholds.informationalBytes)
        findings.push({
            id: "SCAN_SURFACE_SIZE_INFORMATION",
            severity: "info",
            message: `Observed eligible files total approximately ${approximateBytes} bytes; repository size alone is informational.`
        });
    for (const surface of residualEligiblePaths)
        if (surface.approximateBytes >= thresholds.actionableEligibleBytes)
            findings.push({
                id: "SCAN_SURFACE_INDEXING_RISK",
                severity: "warning",
                path: surface.path,
                message: `Eligible surface '${surface.path}' remains large enough to present indexing risk.`
            });
    if (!traversal.complete)
        findings.push({
            id: "SCAN_SURFACE_INCOMPLETE",
            severity: "warning",
            path: traversal.omission?.nextPath,
            message: `Scan stopped before exceeding the ${traversal.exhaustedBudget} budget; no complete-health claim is available.`
        });
    return {
        generatedAt: options.generatedAt ?? new Date().toISOString(),
        scanPath: options.scanPath ?? ".",
        policy: { id: options.policy.id, version: options.policy.version },
        complete: traversal.complete,
        healthClaim: "not-assessed",
        usage: traversal.usage,
        limits: traversal.limits,
        exhaustedBudget: traversal.exhaustedBudget,
        omission: traversal.omission,
        recovery: traversal.recovery,
        observed: { files: traversal.usage.files, directories: traversal.usage.directories, approximateBytes },
        appliedExclusions: options.policy.sources.flatMap((source) => source.rules
            .filter((rule) => rule.decision === "exclude")
            .map((rule) => ({ sourceId: source.id, pattern: rule.pattern, rationale: rule.rationale }))),
        largestSurfaces,
        residualEligiblePaths,
        findings
    };
}
