"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.detectModules = detectModules;
exports.detectModuleCandidatesForRoot = detectModuleCandidatesForRoot;
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
const moduleScope_1 = require("../moduleScope");
const ignoredDirectoryNames = new Set([
    '.git',
    '.archpilot',
    '.vscode-test',
    'node_modules',
    'dist',
    'build',
    'out',
    'coverage',
    '.next',
    'target',
    'vendor',
    '.venv',
    'venv',
    '__pycache__',
]);
const lowValueModuleNames = new Set([
    'utils',
    'shared',
    'common',
    'lib',
    'libs',
    'core',
    'infra',
    'infrastructure',
    'config',
    'scripts',
    'test',
    'tests',
    'cypress-tests',
    '__tests__',
    'fixtures',
    'docs',
    'health',
    'assets',
    'public',
    'dist',
    'build',
    'node_modules',
    'types',
    'generated',
]);
const technicalLayerNames = new Set([
    'action',
    'actions',
    'api',
    'auth',
    'authentication',
    'authorization',
    'config',
    'configuration',
    'console',
    'controller',
    'controllers',
    'dependency',
    'dependencies',
    'dependencyinjection',
    'dto',
    'dtos',
    'entity',
    'entities',
    'exception',
    'exceptions',
    'export',
    'exports',
    'handler',
    'handlers',
    'helper',
    'helpers',
    'hook',
    'hooks',
    'http',
    'import',
    'imports',
    'interceptor',
    'interceptors',
    'job',
    'jobs',
    'mail',
    'mailer',
    'mailers',
    'middleware',
    'middlewares',
    'migration',
    'migrations',
    'model',
    'models',
    'module',
    'modules',
    'payload',
    'payloads',
    'persistence',
    'provider',
    'providers',
    'repository',
    'repositories',
    'relation',
    'relations',
    'route',
    'routes',
    'rule',
    'rules',
    'security',
    'service',
    'services',
    'setting',
    'settings',
    'schema',
    'schemas',
    'trait',
    'traits',
    'ui',
    'validation',
    'validators',
    'view',
    'views',
]);
const sharedInfrastructureNames = new Set([
    'common',
    'commons',
    'core',
    'infra',
    'infrastructure',
    'lib',
    'libs',
    'platform',
    'shared',
    'shared-ui',
    'tooling',
    'tools',
    'util',
    'utilities',
    'utils',
]);
const highConfidenceDomainNames = new Set([
    'account',
    'accounts',
    'billing',
    'cart',
    'catalog',
    'chat',
    'checkout',
    'customer',
    'customers',
    'inventory',
    'invoice',
    'invoices',
    'master',
    'notification',
    'notifications',
    'order',
    'orders',
    'payment',
    'payments',
    'product',
    'products',
    'reporting',
    'scorecard',
    'subscription',
    'subscriptions',
    'tenant',
    'tenants',
    'user',
    'users',
    'vendor',
    'vendors',
    'workflow',
    'workflows',
]);
const componentRoleNames = new Set([
    'app',
    'application',
    'backend',
    'bff',
    'client',
    'engine',
    'frontend',
    'gateway',
    'ledger',
    'mobile',
    'service',
    'web',
    'worker',
]);
const frontendUtilityDirectoryNames = new Set([
    'components',
    'hooks',
    'utils',
    'lib',
    'libs',
    'common',
    'shared-ui',
    'styles',
    'assets',
    'public',
]);
const appRouteReservedDirectoryNames = new Set([
    'api',
    'components',
    'hooks',
    'utils',
    'lib',
    'common',
    'shared-ui',
]);
const sourceFileExtensions = new Set([
    '.ts',
    '.tsx',
    '.js',
    '.jsx',
    '.vue',
    '.java',
    '.py',
    '.php',
    '.go',
    '.cs',
    '.rb',
    '.kt',
    '.swift',
    '.rs',
]);
const roleFileHints = [
    'index.ts',
    'index.js',
    'service.ts',
    'service.js',
    'controller.ts',
    'controller.js',
    'repository.ts',
    'repository.js',
    'routes.ts',
    'routes.js',
    'handler.ts',
    'handler.js',
    'model.ts',
    'model.js',
    'schema.ts',
    'schema.js',
    'dto.ts',
    'dto.js',
    'page.tsx',
    'page.jsx',
    'layout.tsx',
    'layout.jsx',
    'route.ts',
    'route.js',
    'view.vue',
    'page.vue',
];
function normalizePath(value) {
    return value.replaceAll('\\', '/');
}
function isUnderJvmResourceRoot(relativePath) {
    return /(^|\/)src\/main\/resources(?:\/|$)/u.test(normalizePath(relativePath).toLowerCase());
}
function safeReadDirEntries(directoryPath) {
    try {
        return fs.readdirSync(directoryPath, { withFileTypes: true });
    }
    catch {
        return [];
    }
}
function pathExists(pathValue) {
    try {
        fs.accessSync(pathValue);
        return true;
    }
    catch {
        return false;
    }
}
function isIgnoredPath(context, relativePath) {
    return context?.ignoreMatcher?.isIgnored(normalizePath(relativePath)) ?? false;
}
function uniqueSorted(values) {
    return [...new Set(values)].sort((left, right) => left.localeCompare(right));
}
function isLowValueModuleName(moduleName) {
    return lowValueModuleNames.has(moduleName.trim().toLowerCase());
}
function moduleNameSegments(moduleName) {
    return moduleName
        .toLowerCase()
        .split(/[\/._-]+/u)
        .map((segment) => segment.trim())
        .filter((segment) => segment.length > 0);
}
function normalizeIdentity(value) {
    return value
        .toLowerCase()
        .replace(/[^a-z0-9]+/gu, '');
}
function pathLeafIdentity(value) {
    const normalized = normalizePath(value).replace(/\/+$/gu, '');
    const leaf = normalized.split('/').filter((segment) => segment.length > 0).at(-1) ?? normalized;
    return normalizeIdentity(leaf);
}
function getComponentAlignedEvidence(input) {
    const modulePath = normalizePath(input.modulePath).replace(/\/+$/gu, '');
    const moduleNameIdentity = normalizeIdentity(input.moduleName);
    const modulePathLeaf = pathLeafIdentity(modulePath);
    const matchingPath = input.context?.componentPaths
        ?.map((entry) => normalizePath(entry).replace(/\/+$/gu, ''))
        .find((componentPath) => componentPath.length > 0 &&
        componentPath !== '.' &&
        (componentPath === modulePath ||
            componentPath.startsWith(`${modulePath}/`)));
    if (matchingPath) {
        return `Matches detected component/service/app path ${matchingPath}`;
    }
    const matchingName = input.context?.componentNames
        ?.map(normalizeIdentity)
        .find((identity) => identity.length > 0 && (identity === moduleNameIdentity || identity === modulePathLeaf));
    if (matchingName) {
        return `Matches detected component/service/app identity ${input.moduleName}`;
    }
    const matchingResourcePath = input.context?.resourcePaths
        ?.map((entry) => normalizePath(entry).replace(/\/+$/gu, ''))
        .find((resourcePath) => resourcePath.length > 0 &&
        resourcePath !== '.' &&
        (resourcePath === modulePath ||
            resourcePath.startsWith(`${modulePath}/`)));
    if (matchingResourcePath) {
        return `Matches detected resource path ${matchingResourcePath}`;
    }
    const matchingResourceName = input.context?.resourceNames
        ?.map(normalizeIdentity)
        .find((identity) => identity.length > 0 && (identity === moduleNameIdentity || identity === modulePathLeaf));
    if (matchingResourceName) {
        return `Matches detected resource identity ${input.moduleName}`;
    }
    return undefined;
}
function classifyModuleCandidate(input) {
    const evidence = [];
    const segments = moduleNameSegments(input.moduleName);
    const leafName = segments[segments.length - 1] ?? input.moduleName.toLowerCase();
    const rootLeaf = moduleNameSegments(input.rootPath).at(-1) ?? '';
    const hasTechnicalLayerName = segments.some((segment) => technicalLayerNames.has(segment));
    const hasSharedInfrastructureName = segments.some((segment) => sharedInfrastructureNames.has(segment));
    const hasDomainName = segments.some((segment) => highConfidenceDomainNames.has(segment));
    const hasComponentRoleName = segments.some((segment) => componentRoleNames.has(segment));
    const underExplicitArchitectureRoot = ['modules', 'features', 'domain', 'domains'].includes(rootLeaf);
    const underLayerRoot = ['application', 'infrastructure'].includes(rootLeaf);
    const hasLowValueName = segments.some((segment) => lowValueModuleNames.has(segment));
    if (underExplicitArchitectureRoot && input.hasSourceFiles && input.sourceFileCount >= 1) {
        evidence.push(`Located under explicit architecture boundary root ${input.rootPath}`);
        if (hasTechnicalLayerName || hasSharedInfrastructureName || hasLowValueName) {
            evidence.push(`Name '${leafName}' may be infrastructure-oriented; leaving it optional`);
        }
        return {
            classification: 'architecture-module',
            evidence,
            selectedByDefault: !hasTechnicalLayerName &&
                !hasSharedInfrastructureName &&
                !hasLowValueName &&
                (input.hasRoleFiles || input.sourceFileCount >= 1),
        };
    }
    if (hasTechnicalLayerName) {
        evidence.push(`Name '${leafName}' is commonly a technical layer or framework convention`);
        return {
            classification: 'technical-layer',
            evidence,
            selectedByDefault: false,
        };
    }
    if (hasSharedInfrastructureName) {
        evidence.push(`Name '${leafName}' is commonly shared infrastructure or implementation support`);
        return {
            classification: 'shared-infrastructure',
            evidence,
            selectedByDefault: false,
        };
    }
    if (underLayerRoot) {
        evidence.push(`Located under ${input.rootPath}, which is commonly a layered architecture partition`);
        return {
            classification: 'technical-layer',
            evidence,
            selectedByDefault: false,
        };
    }
    if (underExplicitArchitectureRoot && segments.length > 1 && hasComponentRoleName) {
        evidence.push(`Name '${input.moduleName}' combines a module root with an app/service/component role`);
        return {
            classification: 'uncertain',
            evidence,
            selectedByDefault: false,
        };
    }
    if (hasDomainName && input.hasSourceFiles) {
        evidence.push(`Name '${leafName}' matches a common business/domain capability`);
        return {
            classification: 'architecture-module',
            evidence,
            selectedByDefault: input.hasRoleFiles || input.sourceFileCount >= 1,
        };
    }
    if (input.hasRoleFiles && input.sourceFileCount >= 2) {
        evidence.push('Contains multiple role-bearing source files');
        return {
            classification: 'architecture-module',
            evidence,
            selectedByDefault: true,
        };
    }
    evidence.push('Candidate needs review before becoming a governed architecture boundary');
    return {
        classification: 'uncertain',
        evidence,
        selectedByDefault: false,
    };
}
function toStackSlots(stacks) {
    if (!stacks || stacks.length === 0) {
        return {};
    }
    if (stacks.includes('nextjs')) {
        return { frontendStack: 'nextjs' };
    }
    if (stacks.includes('react')) {
        return { frontendStack: 'react' };
    }
    if (stacks.includes('angular')) {
        return { frontendStack: 'angular' };
    }
    if (stacks.includes('vue')) {
        return { frontendStack: 'vue' };
    }
    return { backendStack: 'node_typescript' };
}
function resolveModuleScope(workspaceRoot, modulePath, context) {
    return (0, moduleScope_1.classifyModuleScope)({
        modulePath,
        workspaceRoot,
        projectKinds: context?.projectKinds,
        stacks: toStackSlots(context?.stacks),
    });
}
function inspectModuleFolder(absolutePath) {
    let hasSourceFiles = false;
    let hasRoleFiles = false;
    let sourceFileCount = 0;
    const queue = [{ absolutePath, depth: 0 }];
    while (queue.length > 0) {
        const current = queue.shift();
        if (!current) {
            continue;
        }
        const entries = safeReadDirEntries(current.absolutePath);
        for (const entry of entries) {
            const entryPath = path.join(current.absolutePath, entry.name);
            if (entry.isDirectory()) {
                if (current.depth >= 2 || ignoredDirectoryNames.has(entry.name)) {
                    continue;
                }
                queue.push({ absolutePath: entryPath, depth: current.depth + 1 });
                continue;
            }
            if (!entry.isFile()) {
                continue;
            }
            const extension = path.extname(entry.name).toLowerCase();
            if (sourceFileExtensions.has(extension)) {
                hasSourceFiles = true;
                sourceFileCount += 1;
            }
            const fileNameLower = entry.name.toLowerCase();
            if (roleFileHints.some((hint) => fileNameLower.includes(hint))) {
                hasRoleFiles = true;
            }
        }
    }
    return { hasSourceFiles, hasRoleFiles, sourceFileCount };
}
function containsAppRouteFiles(absolutePath) {
    const queue = [{ absolutePath, depth: 0 }];
    while (queue.length > 0) {
        const current = queue.shift();
        if (!current) {
            continue;
        }
        for (const entry of safeReadDirEntries(current.absolutePath)) {
            const entryPath = path.join(current.absolutePath, entry.name);
            if (entry.isDirectory()) {
                if (current.depth >= 2 || ignoredDirectoryNames.has(entry.name)) {
                    continue;
                }
                queue.push({ absolutePath: entryPath, depth: current.depth + 1 });
                continue;
            }
            if (!entry.isFile()) {
                continue;
            }
            const fileNameLower = entry.name.toLowerCase();
            if (fileNameLower.startsWith('page.') ||
                fileNameLower.startsWith('layout.') ||
                fileNameLower.startsWith('route.') ||
                fileNameLower.startsWith('loading.') ||
                fileNameLower.startsWith('error.')) {
                return true;
            }
        }
    }
    return false;
}
function makeDetectedModule(workspaceRoot, moduleName, modulePath, rootPath, hasSourceFiles, hasRoleFiles, sourceFileCount, context) {
    if (isUnderJvmResourceRoot(modulePath)) {
        return undefined;
    }
    const evidence = [];
    if (hasSourceFiles) {
        evidence.push('Contains source files');
    }
    if (hasRoleFiles) {
        evidence.push('Found service/controller/repository style files');
    }
    evidence.push(`Located under ${rootPath}`);
    let confidence = 'low';
    if (hasRoleFiles || (hasSourceFiles && !isLowValueModuleName(moduleName))) {
        confidence = 'high';
    }
    else if (hasSourceFiles) {
        confidence = 'medium';
    }
    if (isLowValueModuleName(moduleName)) {
        confidence = confidence === 'high' ? 'medium' : 'low';
        evidence.push('Module name is commonly infrastructure-oriented');
    }
    if (!hasSourceFiles && !hasRoleFiles) {
        return undefined;
    }
    const candidateClassification = classifyModuleCandidate({
        moduleName,
        modulePath,
        rootPath,
        hasSourceFiles,
        hasRoleFiles,
        sourceFileCount,
    });
    evidence.push(...candidateClassification.evidence);
    const componentAlignedEvidence = getComponentAlignedEvidence({
        moduleName,
        modulePath,
        context,
    });
    if (componentAlignedEvidence) {
        evidence.push(componentAlignedEvidence);
    }
    const scope = resolveModuleScope(workspaceRoot, modulePath, context);
    const classification = componentAlignedEvidence && candidateClassification.classification === 'architecture-module'
        ? 'uncertain'
        : candidateClassification.classification;
    return {
        name: moduleName,
        path: modulePath,
        confidence,
        evidence,
        ...(scope ? { scope } : {}),
        classification,
        selectedByDefault: classification === 'architecture-module' &&
            candidateClassification.selectedByDefault &&
            confidence !== 'low',
    };
}
function collectModuleCandidatesFromRoot(workspaceRoot, modulesRootRelative, defaultConfidence, context) {
    const absoluteRoot = path.join(workspaceRoot, ...modulesRootRelative.split('/'));
    if (isIgnoredPath(context, modulesRootRelative)) {
        return [];
    }
    const rootLower = modulesRootRelative.toLowerCase();
    const skipFrontendUtilities = rootLower === 'src/features' ||
        rootLower.endsWith('/src/features') ||
        rootLower === 'app' ||
        rootLower === 'src/app' ||
        rootLower.endsWith('/app') ||
        rootLower.endsWith('/src/app');
    const entries = safeReadDirEntries(absoluteRoot)
        .filter((entry) => entry.isDirectory() &&
        !ignoredDirectoryNames.has(entry.name) &&
        !isIgnoredPath(context, normalizePath(`${modulesRootRelative}/${entry.name}`)))
        .sort((left, right) => left.name.localeCompare(right.name));
    const modules = [];
    for (const entry of entries) {
        const candidateProjectRoot = normalizePath(`${modulesRootRelative}/${entry.name}`);
        if (isUnderJvmResourceRoot(candidateProjectRoot)) {
            continue;
        }
        if (['apps', 'packages', 'services', 'libs'].includes(rootLower) &&
            (context?.projectRoots?.length ?? 0) > 0 &&
            !context?.projectRoots?.includes(candidateProjectRoot)) {
            continue;
        }
        if (skipFrontendUtilities && frontendUtilityDirectoryNames.has(entry.name.toLowerCase())) {
            continue;
        }
        const rootSegments = modulesRootRelative.split('/').filter((segment) => segment.length > 0);
        const sourceIndex = rootSegments.lastIndexOf('src');
        const rootLeaf = rootSegments[rootSegments.length - 1]?.toLowerCase();
        const parentName = sourceIndex > 0 && rootLeaf === 'src'
            ? rootSegments[sourceIndex - 1]
            : undefined;
        const moduleName = parentName && parentName !== 'src'
            ? `${parentName}/${entry.name}`
            : ['domain', 'domains', 'application', 'infrastructure'].includes(rootLeaf ?? '')
                ? `${rootSegments[rootSegments.length - 1]}/${entry.name}`
                : entry.name;
        const moduleAbsolutePath = path.join(absoluteRoot, entry.name);
        const inspected = inspectModuleFolder(moduleAbsolutePath);
        const detected = makeDetectedModule(workspaceRoot, moduleName, normalizePath(`${modulesRootRelative}/${entry.name}`), modulesRootRelative, inspected.hasSourceFiles, inspected.hasRoleFiles, inspected.sourceFileCount, context);
        if (!detected) {
            continue;
        }
        if (defaultConfidence === 'medium' && detected.confidence === 'high' && !inspected.hasRoleFiles) {
            detected.confidence = 'medium';
        }
        modules.push(detected);
    }
    return modules;
}
function collectAppRouteModules(workspaceRoot, appRootRelative, context) {
    const absoluteRoot = path.join(workspaceRoot, ...appRootRelative.split('/'));
    if (isIgnoredPath(context, appRootRelative)) {
        return [];
    }
    const entries = safeReadDirEntries(absoluteRoot)
        .filter((entry) => entry.isDirectory() &&
        !ignoredDirectoryNames.has(entry.name) &&
        !isIgnoredPath(context, normalizePath(`${appRootRelative}/${entry.name}`)))
        .sort((left, right) => left.name.localeCompare(right.name));
    const modules = [];
    for (const entry of entries) {
        const rawName = entry.name;
        const lowerName = rawName.toLowerCase();
        if (appRouteReservedDirectoryNames.has(lowerName) ||
            rawName.startsWith('_') ||
            rawName.startsWith('[')) {
            continue;
        }
        const isRouteGroup = rawName.startsWith('(') && rawName.endsWith(')');
        const moduleName = isRouteGroup ? rawName.slice(1, -1).trim() : rawName;
        if (!moduleName || frontendUtilityDirectoryNames.has(moduleName.toLowerCase())) {
            continue;
        }
        const moduleRelativePath = normalizePath(`${appRootRelative}/${rawName}`);
        const moduleAbsolutePath = path.join(absoluteRoot, rawName);
        if (!containsAppRouteFiles(moduleAbsolutePath)) {
            continue;
        }
        const inspected = inspectModuleFolder(moduleAbsolutePath);
        const detected = makeDetectedModule(workspaceRoot, moduleName, moduleRelativePath, appRootRelative, inspected.hasSourceFiles, true, inspected.sourceFileCount, context);
        if (!detected) {
            continue;
        }
        detected.evidence.push(isRouteGroup
            ? 'Detected Next.js route-group boundary'
            : 'Detected app-router feature boundary');
        modules.push(detected);
    }
    return modules;
}
function collectExplicitRoots(workspaceRoot, context) {
    const roots = [];
    for (const root of [
        'src/modules',
        'src/features',
        'src/domain',
        'src/domains',
        'src/application',
        'src/infrastructure',
        'src/app',
        'app',
        'backend/src/modules',
        'backend/src/features',
        'frontend/src/modules',
        'frontend/src/features',
        'frontend/src/views',
        'src/views',
        'routes',
        'src/routes',
        'app/Modules',
        'app/services',
        'services',
        'packages',
        'libs',
    ]) {
        if (['apps', 'packages', 'services', 'libs'].includes(root) &&
            (context?.projectRoots?.length ?? 0) > 0 &&
            !context?.projectRoots?.some((projectRoot) => projectRoot.startsWith(`${root}/`))) {
            continue;
        }
        if (!isIgnoredPath(context, root) && pathExists(path.join(workspaceRoot, ...root.split('/')))) {
            roots.push(root);
        }
    }
    const appsRoot = path.join(workspaceRoot, 'apps');
    for (const appEntry of safeReadDirEntries(appsRoot)) {
        if (!appEntry.isDirectory() ||
            ignoredDirectoryNames.has(appEntry.name) ||
            isIgnoredPath(context, `apps/${appEntry.name}`)) {
            continue;
        }
        for (const candidate of [
            `apps/${appEntry.name}/src`,
            `apps/${appEntry.name}/src/modules`,
            `apps/${appEntry.name}/src/features`,
            `apps/${appEntry.name}/src/domain`,
            `apps/${appEntry.name}/src/domains`,
            `apps/${appEntry.name}/src/application`,
            `apps/${appEntry.name}/src/infrastructure`,
            `apps/${appEntry.name}/app`,
            `apps/${appEntry.name}/src/app`,
        ]) {
            if ((context?.projectRoots?.length ?? 0) > 0 && !context?.projectRoots?.includes(`apps/${appEntry.name}`)) {
                continue;
            }
            if (!isIgnoredPath(context, candidate) && pathExists(path.join(workspaceRoot, ...candidate.split('/')))) {
                roots.push(candidate);
            }
        }
    }
    for (const container of ['packages', 'services']) {
        const containerRoot = path.join(workspaceRoot, container);
        for (const containerEntry of safeReadDirEntries(containerRoot)) {
            if (!containerEntry.isDirectory() ||
                ignoredDirectoryNames.has(containerEntry.name) ||
                isIgnoredPath(context, `${container}/${containerEntry.name}`)) {
                continue;
            }
            for (const candidate of [
                `${container}/${containerEntry.name}/src`,
                `${container}/${containerEntry.name}/src/modules`,
                `${container}/${containerEntry.name}/src/features`,
                `${container}/${containerEntry.name}/src/domain`,
                `${container}/${containerEntry.name}/src/domains`,
                `${container}/${containerEntry.name}/src/application`,
                `${container}/${containerEntry.name}/src/infrastructure`,
            ]) {
                if ((context?.projectRoots?.length ?? 0) > 0 &&
                    !context?.projectRoots?.includes(`${container}/${containerEntry.name}`)) {
                    continue;
                }
                if (!isIgnoredPath(context, candidate) && pathExists(path.join(workspaceRoot, ...candidate.split('/')))) {
                    roots.push(candidate);
                }
            }
        }
    }
    return uniqueSorted(roots);
}
function collectJavaPackageRoots(workspaceRoot, context) {
    const roots = [];
    for (const sourceRoot of ['src/main/java', 'src/main/kotlin', 'src']) {
        const absoluteSourceRoot = path.join(workspaceRoot, ...sourceRoot.split('/'));
        if (isIgnoredPath(context, sourceRoot) || !pathExists(absoluteSourceRoot)) {
            continue;
        }
        const queue = [
            { absolutePath: absoluteSourceRoot, relativePath: sourceRoot, depth: 0 },
        ];
        while (queue.length > 0) {
            const current = queue.shift();
            if (!current) {
                continue;
            }
            if (current.depth > 6) {
                continue;
            }
            for (const entry of safeReadDirEntries(current.absolutePath)) {
                const childRelativePath = normalizePath(`${current.relativePath}/${entry.name}`);
                if (!entry.isDirectory() ||
                    ignoredDirectoryNames.has(entry.name) ||
                    isIgnoredPath(context, childRelativePath)) {
                    continue;
                }
                const lowerName = entry.name.toLowerCase();
                if (lowerName === 'modules' || lowerName === 'features') {
                    const childCount = safeReadDirEntries(path.join(current.absolutePath, entry.name))
                        .filter((child) => child.isDirectory() &&
                        !ignoredDirectoryNames.has(child.name) &&
                        !isIgnoredPath(context, normalizePath(`${childRelativePath}/${child.name}`)))
                        .length;
                    if (childCount >= 2) {
                        roots.push(childRelativePath);
                    }
                    continue;
                }
                queue.push({
                    absolutePath: path.join(current.absolutePath, entry.name),
                    relativePath: childRelativePath,
                    depth: current.depth + 1,
                });
            }
        }
    }
    return roots;
}
function collectFallbackSrcModules(workspaceRoot, context) {
    const srcRoot = path.join(workspaceRoot, 'src');
    if (isIgnoredPath(context, 'src')) {
        return [];
    }
    const srcEntries = safeReadDirEntries(srcRoot)
        .filter((entry) => entry.isDirectory() &&
        !ignoredDirectoryNames.has(entry.name) &&
        !isIgnoredPath(context, `src/${entry.name}`))
        .sort((left, right) => left.name.localeCompare(right.name));
    const modules = [];
    for (const entry of srcEntries) {
        if (entry.name === 'modules' ||
            entry.name === 'features' ||
            entry.name === 'app' ||
            isLowValueModuleName(entry.name) ||
            frontendUtilityDirectoryNames.has(entry.name.toLowerCase())) {
            continue;
        }
        const relativePath = `src/${entry.name}`;
        const inspected = inspectModuleFolder(path.join(srcRoot, entry.name));
        const detected = makeDetectedModule(workspaceRoot, entry.name, relativePath, 'src', inspected.hasSourceFiles, inspected.hasRoleFiles, inspected.sourceFileCount, context);
        if (!detected) {
            continue;
        }
        if (detected.confidence === 'high' && !inspected.hasRoleFiles) {
            detected.confidence = 'medium';
        }
        modules.push(detected);
    }
    return modules;
}
function detectModules(workspaceRoot, options) {
    const evidence = [];
    const modulesByPath = new Map();
    const context = {
        projectKinds: options?.projectKinds,
        stacks: options?.stacks,
        ignoreMatcher: options?.ignoreMatcher,
        projectRoots: options?.projectRoots?.map(normalizePath),
        componentPaths: options?.componentPaths?.map(normalizePath),
        componentNames: options?.componentNames,
        resourcePaths: options?.resourcePaths?.map(normalizePath),
        resourceNames: options?.resourceNames,
    };
    const explicitRoots = collectExplicitRoots(workspaceRoot, context);
    const usesFrontendAppRouter = options?.stacks?.some((stack) => stack === 'nextjs' || stack === 'react' || stack === 'vue' || stack === 'angular');
    if (options?.stacks?.some((stack) => stack === 'java' || stack === 'spring' || stack === 'kotlin' || stack === 'kotlin_spring')) {
        for (const root of collectJavaPackageRoots(workspaceRoot, context)) {
            if (!explicitRoots.includes(root)) {
                explicitRoots.push(root);
            }
        }
        explicitRoots.sort((left, right) => left.localeCompare(right));
    }
    for (const root of explicitRoots) {
        if (root.endsWith('/src/modules') ||
            root === 'src/modules' ||
            root === 'backend/src/modules') {
            evidence.push(`Found ${root} directory`);
        }
        else {
            evidence.push(`Found likely module root ${root}`);
        }
        const detectedModules = usesFrontendAppRouter && (root === 'app' ||
            root === 'src/app' ||
            root.endsWith('/app') ||
            root.endsWith('/src/app'))
            ? collectAppRouteModules(workspaceRoot, root, context)
            : collectModuleCandidatesFromRoot(workspaceRoot, root, root.includes('modules') ? 'high' : 'medium', context);
        for (const moduleEntry of detectedModules) {
            modulesByPath.set(moduleEntry.path, moduleEntry);
        }
        if (detectedModules.length >= 2) {
            evidence.push(`Detected repeated module folders under ${root}`);
        }
    }
    if (modulesByPath.size === 0) {
        const fallbackModules = collectFallbackSrcModules(workspaceRoot, context);
        for (const moduleEntry of fallbackModules) {
            modulesByPath.set(moduleEntry.path, moduleEntry);
        }
        if (fallbackModules.length > 0) {
            evidence.push('Detected likely business modules under src/');
        }
    }
    const modules = [...modulesByPath.values()].sort((left, right) => left.path.localeCompare(right.path));
    if (modules.length === 0) {
        return {
            modules: [],
            ...(explicitRoots.length > 0 ? { moduleRoots: explicitRoots } : {}),
            confidence: 'low',
            evidence: evidence.length > 0 ? uniqueSorted(evidence) : ['No clear module structure detected'],
        };
    }
    const rootCounts = new Map();
    for (const moduleEntry of modules) {
        const root = moduleEntry.path.includes('/')
            ? moduleEntry.path.slice(0, moduleEntry.path.lastIndexOf('/'))
            : moduleEntry.path;
        rootCounts.set(root, (rootCounts.get(root) ?? 0) + 1);
    }
    const bestRootEntry = [...rootCounts.entries()]
        .sort((left, right) => {
        if (right[1] !== left[1]) {
            return right[1] - left[1];
        }
        return left[0].localeCompare(right[0]);
    })[0];
    const modulesRoot = bestRootEntry && bestRootEntry[1] >= 2 ? bestRootEntry[0] : undefined;
    let confidence = 'low';
    const highModules = modules.filter((entry) => entry.confidence === 'high').length;
    if (modulesRoot && modules.length >= 2 && highModules >= 2) {
        confidence = 'high';
    }
    else if (modules.length >= 1 && (highModules >= 1 || modulesRoot)) {
        confidence = 'medium';
    }
    return {
        modules,
        ...(modulesRoot ? { modulesRoot } : {}),
        ...(explicitRoots.length > 0 ? { moduleRoots: explicitRoots } : {}),
        confidence,
        evidence: uniqueSorted(evidence),
    };
}
function detectModuleCandidatesForRoot(workspaceRoot, modulesRootRelative, options) {
    const normalizedRoot = normalizePath(modulesRootRelative.trim()).replace(/^\/+|\/+$/gu, '');
    if (normalizedRoot.length === 0) {
        return [];
    }
    return collectModuleCandidatesFromRoot(workspaceRoot, normalizedRoot, normalizedRoot.includes('modules') ? 'high' : 'medium', {
        projectKinds: options?.projectKinds,
        stacks: options?.stacks,
        ignoreMatcher: options?.ignoreMatcher,
        projectRoots: options?.projectRoots?.map(normalizePath),
        componentPaths: options?.componentPaths?.map(normalizePath),
        componentNames: options?.componentNames,
        resourcePaths: options?.resourcePaths?.map(normalizePath),
        resourceNames: options?.resourceNames,
    });
}
//# sourceMappingURL=moduleDetection.js.map