"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.detectRepoTopology = detectRepoTopology;
exports.debugTestTopologyDetection = debugTestTopologyDetection;
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const manifestFileNames = [
    'package.json',
    'pom.xml',
    'requirements.txt',
    'go.mod',
    'composer.json',
];
const workspaceConfigFileNames = [
    'pnpm-workspace.yaml',
    'lerna.json',
    'nx.json',
    'turbo.json',
    'rush.json',
];
const monorepoRootDirectorySignals = ['apps', 'packages', 'libs', 'services', 'tools'];
const ignoredDirectoryNames = new Set([
    '.git',
    '.archpilot',
    '.vscode-test',
    'node_modules',
    'dist',
    'build',
    'out',
    'coverage',
    '.next',
    'target',
    'vendor',
    '.venv',
    'venv',
    '__pycache__',
]);
const ignoredRootPathPrefixes = [
    'tests',
    'test',
    'fixtures',
    'examples',
    'example',
    'spec',
    '__tests__',
    '.vscode-test',
    'public',
    'static',
    'assets',
    'vendor',
    'plugins',
    'plugin',
    'themes',
    'theme',
    'generated',
    'storage',
];
function normalizeRelativePath(value) {
    return value.replaceAll('\\', '/');
}
function isUnderJvmResourceRoot(relativePath) {
    return /(^|\/)src\/main\/resources(?:\/|$)/u.test(normalizeRelativePath(relativePath).toLowerCase());
}
function sortUnique(values) {
    return [...new Set(values)].sort((left, right) => left.localeCompare(right));
}
function pushEvidence(evidence, message) {
    if (!evidence.includes(message)) {
        evidence.push(message);
    }
}
function safeReadDirectoryEntries(directoryPath) {
    try {
        return fs.readdirSync(directoryPath, { withFileTypes: true });
    }
    catch {
        return [];
    }
}
function safeReadJsonFile(jsonPath) {
    try {
        return JSON.parse(fs.readFileSync(jsonPath, { encoding: 'utf8' }));
    }
    catch {
        return undefined;
    }
}
function hasNonEmptyWorkspaceField(value) {
    if (!value || typeof value !== 'object' || Array.isArray(value)) {
        return false;
    }
    const typed = value;
    const workspaces = typed.workspaces;
    if (Array.isArray(workspaces)) {
        return workspaces.length > 0;
    }
    if (!workspaces || typeof workspaces !== 'object' || Array.isArray(workspaces)) {
        return false;
    }
    const packages = workspaces.packages;
    return Array.isArray(packages) && packages.length > 0;
}
function isIgnoredRelativePath(relativePath) {
    const normalized = normalizeRelativePath(relativePath).replace(/^\.\/+/u, '');
    if (normalized.length === 0 || normalized === '.') {
        return false;
    }
    const segments = normalized.split('/').filter((segment) => segment.length > 0);
    const rootSegment = segments[0];
    if (!rootSegment || ignoredRootPathPrefixes.includes(rootSegment)) {
        return Boolean(rootSegment);
    }
    return segments.some((segment) => [
        'node_modules',
        'vendor',
        'public',
        'static',
        'assets',
        'plugins',
        'plugin',
        'themes',
        'theme',
        'generated',
        'dist',
        'build',
        'coverage',
        'storage',
    ].includes(segment.toLowerCase()));
}
function scanWorkspace(workspaceRoot, options) {
    const maxDepth = options?.maxDepth ?? 6;
    const rootEntries = safeReadDirectoryEntries(workspaceRoot)
        .sort((left, right) => left.name.localeCompare(right.name));
    const rootDirectoryNames = new Set(rootEntries.filter((entry) => entry.isDirectory()).map((entry) => entry.name));
    const rootFileNames = new Set(rootEntries.filter((entry) => entry.isFile()).map((entry) => entry.name));
    const manifestPathsByName = {
        'package.json': [],
        'pom.xml': [],
        'requirements.txt': [],
        'go.mod': [],
        'composer.json': [],
    };
    const srcDirectoryPaths = [];
    const workspaceConfigFiles = [...workspaceConfigFileNames]
        .filter((fileName) => rootFileNames.has(fileName))
        .sort((left, right) => left.localeCompare(right));
    const queue = [
        { absolutePath: workspaceRoot, relativePath: '', depth: 0 },
    ];
    while (queue.length > 0) {
        const current = queue.shift();
        if (!current) {
            continue;
        }
        const entries = safeReadDirectoryEntries(current.absolutePath)
            .sort((left, right) => left.name.localeCompare(right.name));
        for (const entry of entries) {
            const absoluteEntryPath = path.join(current.absolutePath, entry.name);
            const relativeEntryPath = current.relativePath.length > 0
                ? `${current.relativePath}/${entry.name}`
                : entry.name;
            const normalizedRelativeEntryPath = normalizeRelativePath(relativeEntryPath);
            if (options?.ignoreMatcher?.isIgnored(normalizedRelativeEntryPath)) {
                continue;
            }
            if (entry.isDirectory()) {
                if (current.depth >= maxDepth || ignoredDirectoryNames.has(entry.name)) {
                    continue;
                }
                if (entry.name === 'src') {
                    srcDirectoryPaths.push(normalizedRelativeEntryPath);
                }
                queue.push({
                    absolutePath: absoluteEntryPath,
                    relativePath: normalizedRelativeEntryPath,
                    depth: current.depth + 1,
                });
                continue;
            }
            if (!entry.isFile()) {
                continue;
            }
            const fileName = entry.name;
            if (manifestFileNames.includes(fileName)) {
                manifestPathsByName[fileName].push(normalizedRelativeEntryPath);
            }
        }
    }
    const rootPackageHasWorkspaces = rootFileNames.has('package.json')
        ? hasNonEmptyWorkspaceField(safeReadJsonFile(path.join(workspaceRoot, 'package.json')))
        : false;
    return {
        rootDirectoryNames,
        rootFileNames,
        manifestPathsByName,
        workspaceConfigFiles,
        srcDirectoryPaths: sortUnique(srcDirectoryPaths),
        rootPackageHasWorkspaces,
    };
}
function collectDetectedRoots(scan) {
    const roots = new Set();
    for (const manifestName of manifestFileNames) {
        for (const manifestPath of scan.manifestPathsByName[manifestName]) {
            if (isIgnoredRelativePath(manifestPath) || isUnderJvmResourceRoot(manifestPath)) {
                continue;
            }
            const containingDirectory = normalizeRelativePath(path.dirname(manifestPath));
            if (containingDirectory !== '.' && containingDirectory.length > 0) {
                roots.add(containingDirectory);
            }
        }
    }
    return sortUnique(roots);
}
function buildResultFromScan(scan) {
    const evidence = [];
    const detectedRoots = collectDetectedRoots(scan);
    const filteredManifestPathsByName = {
        'package.json': scan.manifestPathsByName['package.json'].filter((entry) => !isIgnoredRelativePath(entry) && !isUnderJvmResourceRoot(entry)),
        'pom.xml': scan.manifestPathsByName['pom.xml'].filter((entry) => !isIgnoredRelativePath(entry) && !isUnderJvmResourceRoot(entry)),
        'requirements.txt': scan.manifestPathsByName['requirements.txt'].filter((entry) => !isIgnoredRelativePath(entry) && !isUnderJvmResourceRoot(entry)),
        'go.mod': scan.manifestPathsByName['go.mod'].filter((entry) => !isIgnoredRelativePath(entry) && !isUnderJvmResourceRoot(entry)),
        'composer.json': scan.manifestPathsByName['composer.json'].filter((entry) => !isIgnoredRelativePath(entry) && !isUnderJvmResourceRoot(entry)),
    };
    let monorepoStrongSignals = 0;
    let monorepoWeakSignals = 0;
    let singleStrongSignals = 0;
    let singleWeakSignals = 0;
    for (const fileName of scan.workspaceConfigFiles) {
        pushEvidence(evidence, `Found ${fileName}`);
        monorepoStrongSignals += 1;
    }
    if (scan.rootPackageHasWorkspaces) {
        pushEvidence(evidence, 'Found workspaces definition in root package.json');
        monorepoStrongSignals += 1;
    }
    const presentMonorepoDirectories = [...monorepoRootDirectorySignals]
        .filter((directoryName) => scan.rootDirectoryNames.has(directoryName));
    for (const directoryName of presentMonorepoDirectories) {
        pushEvidence(evidence, `Found ${directoryName}/ directory`);
        monorepoWeakSignals += 1;
    }
    if (presentMonorepoDirectories.includes('apps') && presentMonorepoDirectories.includes('packages')) {
        pushEvidence(evidence, 'Detected project roots in apps/ and packages/');
        monorepoStrongSignals += 1;
    }
    if (scan.rootDirectoryNames.has('backend') && scan.rootDirectoryNames.has('frontend')) {
        pushEvidence(evidence, 'Found backend/ and frontend/ directories at repository root');
        monorepoStrongSignals += 1;
    }
    const multiManifestTypes = manifestFileNames.filter((manifestName) => filteredManifestPathsByName[manifestName].length > 0);
    if (multiManifestTypes.length > 1) {
        pushEvidence(evidence, 'Multiple language manifests detected');
        monorepoWeakSignals += 1;
    }
    let nonRootManifestCount = 0;
    for (const manifestName of manifestFileNames) {
        const paths = filteredManifestPathsByName[manifestName];
        if (paths.length > 1) {
            pushEvidence(evidence, `Multiple ${manifestName} files detected (${paths.length})`);
            monorepoStrongSignals += 1;
        }
        const rootCount = paths.filter((entry) => normalizeRelativePath(entry) === manifestName).length;
        const nestedCount = Math.max(0, paths.length - rootCount);
        nonRootManifestCount += nestedCount;
        if (rootCount === 1 && nestedCount === 0) {
            pushEvidence(evidence, `Single ${manifestName} at root`);
            singleStrongSignals += 1;
        }
    }
    if (nonRootManifestCount >= 2) {
        pushEvidence(evidence, `Multiple project manifests detected (${nonRootManifestCount} nested manifests)`);
        monorepoStrongSignals += 1;
    }
    if (detectedRoots.length > 1) {
        pushEvidence(evidence, `Detected multiple project roots (${detectedRoots.length})`);
        monorepoStrongSignals += 1;
    }
    const filteredSrcDirectoryPaths = scan.srcDirectoryPaths.filter((entry) => !isIgnoredRelativePath(entry));
    if (scan.rootDirectoryNames.has('src')) {
        pushEvidence(evidence, 'Found src/ directory at root');
        singleWeakSignals += 1;
    }
    if (filteredSrcDirectoryPaths.length > 1) {
        pushEvidence(evidence, `Detected multiple src/ directories (${filteredSrcDirectoryPaths.length})`);
        monorepoWeakSignals += 1;
    }
    const noMonorepoFoldersPresent = presentMonorepoDirectories.length === 0 &&
        !(scan.rootDirectoryNames.has('backend') && scan.rootDirectoryNames.has('frontend'));
    if (scan.workspaceConfigFiles.length === 0 && noMonorepoFoldersPresent) {
        singleWeakSignals += 1;
        pushEvidence(evidence, 'No workspace config files or monorepo root folders detected');
    }
    const monorepoScore = monorepoStrongSignals * 3 + monorepoWeakSignals;
    const singleProjectScore = singleStrongSignals * 3 + singleWeakSignals;
    if (monorepoScore === 0 && singleProjectScore === 0) {
        return {
            topology: 'unknown',
            confidence: 'low',
            evidence: ['No strong monorepo or single-project signals detected'],
        };
    }
    let topology = 'unknown';
    if (monorepoScore > singleProjectScore) {
        topology = 'monorepo';
    }
    else if (singleProjectScore > monorepoScore) {
        topology = 'single_project';
    }
    else if (detectedRoots.length > 1 || monorepoStrongSignals > singleStrongSignals) {
        topology = 'monorepo';
    }
    else if (singleStrongSignals > 0 || singleWeakSignals > 0) {
        topology = 'single_project';
    }
    if (topology === 'unknown') {
        return {
            topology: 'unknown',
            confidence: 'low',
            evidence,
        };
    }
    let confidence = 'low';
    if (topology === 'monorepo') {
        if (monorepoStrongSignals >= 2) {
            confidence = 'high';
        }
        else if (monorepoStrongSignals >= 1 || monorepoWeakSignals >= 2) {
            confidence = 'medium';
        }
    }
    else {
        if (singleStrongSignals >= 2) {
            confidence = 'high';
        }
        else if (singleStrongSignals >= 1 || singleWeakSignals >= 2) {
            confidence = 'medium';
        }
    }
    if ((topology === 'monorepo' && singleProjectScore > 0) ||
        (topology === 'single_project' && monorepoScore > 0)) {
        confidence = confidence === 'high' ? 'medium' : confidence;
    }
    return {
        topology,
        confidence,
        evidence,
        ...(topology === 'monorepo' && detectedRoots.length > 0 ? { detectedRoots } : {}),
        ...(topology === 'single_project' && detectedRoots.length === 0 ? { detectedRoots: ['.'] } : {}),
    };
}
function detectRepoTopology(workspaceRoot, options) {
    return buildResultFromScan(scanWorkspace(workspaceRoot, options));
}
function debugTestTopologyDetection() {
    const scenarios = [
        {
            name: 'Single Node Project',
            expected: 'single_project',
            scan: {
                rootDirectoryNames: new Set(['src']),
                rootFileNames: new Set(['package.json']),
                manifestPathsByName: {
                    'package.json': ['package.json'],
                    'pom.xml': [],
                    'requirements.txt': [],
                    'go.mod': [],
                    'composer.json': [],
                },
                workspaceConfigFiles: [],
                srcDirectoryPaths: ['src'],
                rootPackageHasWorkspaces: false,
            },
        },
        {
            name: 'Apps + Packages Monorepo',
            expected: 'monorepo',
            scan: {
                rootDirectoryNames: new Set(['apps', 'packages']),
                rootFileNames: new Set(['pnpm-workspace.yaml', 'package.json']),
                manifestPathsByName: {
                    'package.json': ['package.json', 'apps/api/package.json', 'apps/web/package.json', 'packages/shared/package.json'],
                    'pom.xml': [],
                    'requirements.txt': [],
                    'go.mod': [],
                    'composer.json': [],
                },
                workspaceConfigFiles: ['pnpm-workspace.yaml'],
                srcDirectoryPaths: ['apps/api/src', 'apps/web/src', 'packages/shared/src'],
                rootPackageHasWorkspaces: true,
            },
        },
        {
            name: 'Backend + Frontend Repo',
            expected: 'monorepo',
            scan: {
                rootDirectoryNames: new Set(['backend', 'frontend']),
                rootFileNames: new Set(['package.json']),
                manifestPathsByName: {
                    'package.json': ['package.json', 'backend/package.json', 'frontend/package.json'],
                    'pom.xml': [],
                    'requirements.txt': [],
                    'go.mod': [],
                    'composer.json': [],
                },
                workspaceConfigFiles: [],
                srcDirectoryPaths: ['backend/src', 'frontend/src'],
                rootPackageHasWorkspaces: false,
            },
        },
        {
            name: 'Empty Repo',
            expected: 'unknown',
            scan: {
                rootDirectoryNames: new Set(),
                rootFileNames: new Set(),
                manifestPathsByName: {
                    'package.json': [],
                    'pom.xml': [],
                    'requirements.txt': [],
                    'go.mod': [],
                    'composer.json': [],
                },
                workspaceConfigFiles: [],
                srcDirectoryPaths: [],
                rootPackageHasWorkspaces: false,
            },
        },
    ];
    for (const scenario of scenarios) {
        const result = buildResultFromScan(scenario.scan);
        console.log(`Test Scenario: ${scenario.name} -> ${scenario.expected}`);
        console.log(JSON.stringify(result, null, 2));
    }
}
//# sourceMappingURL=repoTopologyDetection.js.map