"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.createArchitectureDependencyContextResolver = createArchitectureDependencyContextResolver;
exports.resolveDependencyContext = resolveDependencyContext;
const path = __importStar(require("node:path"));
const architectureWorkspaceModel_1 = require("./architectureWorkspaceModel");
const moduleDiscovery_1 = require("./moduleDiscovery");
function normalizePath(value) {
    return value
        .replace(/\\/g, '/')
        .replace(/\/+/gu, '/')
        .replace(/^\.\//u, '')
        .replace(/\/+$/u, '')
        .trim();
}
function normalizeImportPath(value) {
    return normalizePath(value.replace(/^\//u, ''));
}
function pathHasFileExtension(value) {
    return /\.[a-z0-9]+$/iu.test(value);
}
function isRelativeImport(importSpecifier) {
    return importSpecifier === '.' ||
        importSpecifier === '..' ||
        importSpecifier.startsWith('./') ||
        importSpecifier.startsWith('../');
}
function isPathInsideOrEqual(childPath, parentPath) {
    const child = normalizePath(childPath).toLowerCase();
    const parent = normalizePath(parentPath).toLowerCase();
    return child === parent || child.startsWith(`${parent}/`);
}
function isNormalizedPathInsideOrEqual(normalizedChildPath, normalizedParentPath) {
    const child = normalizedChildPath.toLowerCase();
    const parent = normalizedParentPath.toLowerCase();
    return child === parent || child.startsWith(`${parent}/`);
}
function toEndpoint(candidate) {
    return {
        componentId: candidate.component.id,
        componentName: candidate.component.name,
        ...(candidate.moduleId ? { moduleId: candidate.moduleId } : {}),
        ...(candidate.path ? { path: normalizePath(candidate.path) } : {}),
        ownership: candidate.ownership,
    };
}
function compareCandidates(left, right) {
    const scoreCompare = right.score - left.score;
    if (scoreCompare !== 0) {
        return scoreCompare;
    }
    return left.component.id.localeCompare(right.component.id);
}
function normalizedModulePathMatchScore(normalizedInputPath, normalizedModulePath) {
    if (pathHasFileExtension(normalizedModulePath)) {
        return isNormalizedPathInsideOrEqual(normalizedInputPath, normalizedModulePath) ||
            isNormalizedPathInsideOrEqual(normalizedModulePath, normalizedInputPath)
            ? normalizedModulePath.length + 1000
            : undefined;
    }
    return isNormalizedPathInsideOrEqual(normalizedInputPath, normalizedModulePath)
        ? normalizedModulePath.length + 1000
        : undefined;
}
function modulePathMatchScore(inputPath, modulePath) {
    return normalizedModulePathMatchScore(normalizePath(inputPath), normalizePath(modulePath));
}
function findComponentForPath(components, inputPath) {
    const normalizedInputPath = normalizePath(inputPath);
    const candidates = [];
    for (const component of components) {
        for (const moduleReference of component.modules) {
            if (!moduleReference.normalizedPath) {
                continue;
            }
            const score = normalizedModulePathMatchScore(normalizedInputPath, moduleReference.normalizedPath);
            if (score !== undefined) {
                candidates.push({
                    component: component.component,
                    moduleId: moduleReference.moduleId,
                    path: moduleReference.path,
                    ownership: 'module-path',
                    score,
                });
            }
        }
        if (component.normalizedPath &&
            isNormalizedPathInsideOrEqual(normalizedInputPath, component.normalizedPath)) {
            candidates.push({
                component: component.component,
                path: component.component.path,
                ownership: 'component-path',
                score: component.normalizedPath.length,
            });
        }
    }
    return candidates.sort(compareCandidates)[0];
}
function findComponentForModuleSpecifier(components, importSpecifier) {
    const normalizedImport = normalizeImportPath(importSpecifier);
    const normalizedSegments = normalizedImport.split('/').filter((segment) => segment.length > 0);
    const candidates = [];
    for (const component of components) {
        for (const moduleReference of component.modules) {
            const moduleId = moduleReference.moduleId;
            if (normalizedImport === moduleId || normalizedImport.startsWith(`${moduleId}/`)) {
                candidates.push({
                    component: component.component,
                    moduleId,
                    ...(moduleReference.path ? { path: moduleReference.path } : {}),
                    ownership: moduleReference.path ? 'module-path' : 'component-path',
                    score: moduleId.length + 500,
                });
            }
            if (component.component.id === architectureWorkspaceModel_1.VIRTUAL_WORKSPACE_COMPONENT_ID &&
                normalizedSegments.includes(moduleId)) {
                candidates.push({
                    component: component.component,
                    moduleId,
                    ...(moduleReference.path ? { path: moduleReference.path } : {}),
                    ownership: 'virtual-workspace',
                    score: moduleId.length + 250,
                });
            }
        }
        if (component.normalizedPath &&
            isNormalizedPathInsideOrEqual(normalizedImport, component.normalizedPath)) {
            candidates.push({
                component: component.component,
                path: component.component.path,
                ownership: 'component-path',
                score: component.normalizedPath.length,
            });
        }
        if (normalizedImport === component.component.id ||
            normalizedImport.startsWith(`${component.component.id}/`)) {
            candidates.push({
                component: component.component,
                ...(component.component.path ? { path: component.component.path } : {}),
                ownership: component.component.path ? 'component-path' : 'virtual-workspace',
                score: component.component.id.length,
            });
        }
    }
    return candidates.sort(compareCandidates)[0];
}
function resolveRelativeImportPath(sourceFile, importSpecifier) {
    return normalizePath(path.posix.normalize(path.posix.join(normalizePath(sourceFile).split('/').slice(0, -1).join('/'), importSpecifier)));
}
function resolveLegacyVirtualSource(contract, sourceFile, component) {
    const modulesRoot = (0, moduleDiscovery_1.resolveModulesRootFromContract)(contract);
    const normalizedSource = normalizePath(sourceFile);
    for (const [moduleId, moduleReference] of Object.entries(contract.modules).sort(([left], [right]) => left.localeCompare(right))) {
        if (moduleReference.path && modulePathMatchScore(normalizedSource, moduleReference.path) !== undefined) {
            return {
                component,
                moduleId,
                path: moduleReference.path,
                ownership: 'virtual-workspace',
                score: moduleReference.path.length,
            };
        }
    }
    const relativeToModulesRoot = normalizedSource.startsWith(`${normalizePath(modulesRoot)}/`)
        ? normalizedSource.slice(normalizePath(modulesRoot).length + 1)
        : '';
    const moduleId = relativeToModulesRoot.split('/')[0];
    return {
        component,
        ...(moduleId ? { moduleId } : {}),
        path: modulesRoot,
        ownership: 'virtual-workspace',
        score: 0,
    };
}
function resolveSourceCandidate(contract, components, sourceFile) {
    const directMatch = findComponentForPath(components, sourceFile);
    if (directMatch) {
        return directMatch;
    }
    const virtualComponent = components.find((component) => component.component.id === architectureWorkspaceModel_1.VIRTUAL_WORKSPACE_COMPONENT_ID)?.component;
    if (virtualComponent) {
        return resolveLegacyVirtualSource(contract, sourceFile, virtualComponent);
    }
    const fallback = [...components]
        .map((component) => component.component)
        .sort((left, right) => left.id.localeCompare(right.id))[0];
    if (fallback) {
        return {
            component: fallback,
            ...(fallback.path ? { path: fallback.path } : {}),
            ownership: fallback.path ? 'component-path' : 'virtual-workspace',
            score: 0,
        };
    }
    throw new Error('Architecture dependency context requires at least one normalized component.');
}
function resolveTargetCandidate(contract, components, sourceFile, importSpecifier) {
    if (isRelativeImport(importSpecifier)) {
        const resolvedTargetPath = resolveRelativeImportPath(sourceFile, importSpecifier);
        return {
            candidate: findComponentForPath(components, resolvedTargetPath),
            resolvedTargetPath,
        };
    }
    const normalizedImport = normalizeImportPath(importSpecifier);
    const normalizedModulesRoot = normalizePath((0, moduleDiscovery_1.resolveModulesRootFromContract)(contract));
    if ((normalizedModulesRoot.length > 0 && normalizedImport.startsWith(`${normalizedModulesRoot}/`)) ||
        normalizedImport.startsWith('src/') ||
        normalizedImport.startsWith('app/') ||
        normalizedImport.startsWith('apps/') ||
        normalizedImport.startsWith('services/') ||
        normalizedImport.startsWith('packages/') ||
        normalizedImport.startsWith('libs/') ||
        normalizedImport.startsWith('libraries/') ||
        normalizedImport.startsWith('workers/')) {
        return {
            candidate: findComponentForPath(components, normalizedImport),
            resolvedTargetPath: normalizedImport,
        };
    }
    const moduleSpecifierMatch = findComponentForModuleSpecifier(components, importSpecifier);
    if (moduleSpecifierMatch) {
        return { candidate: moduleSpecifierMatch };
    }
    return {};
}
function prepareComponentReferences(components) {
    return components.map((component) => ({
        component,
        ...(component.path ? { normalizedPath: normalizePath(component.path) } : {}),
        modules: Object.entries(component.modules)
            .map(([moduleId, moduleReference]) => ({
            moduleId,
            ...(moduleReference.path
                ? {
                    path: moduleReference.path,
                    normalizedPath: normalizePath(moduleReference.path),
                }
                : {}),
        })),
    }));
}
function resolveDependencyContextWithPreparedComponents(contract, components, input) {
    const sourceCandidate = resolveSourceCandidate(contract, components, input.sourceFile);
    const { candidate: resolvedTargetCandidate, resolvedTargetPath } = resolveTargetCandidate(contract, components, input.sourceFile, input.importSpecifier);
    const targetCandidate = resolvedTargetCandidate ??
        (resolvedTargetPath ? resolveSourceCandidate(contract, components, resolvedTargetPath) : undefined);
    if (!targetCandidate) {
        return {
            dependencyType: 'external',
            source: toEndpoint(sourceCandidate),
            importSpecifier: input.importSpecifier,
            ...(resolvedTargetPath ? { resolvedTargetPath } : {}),
            isExternal: true,
        };
    }
    const source = toEndpoint(sourceCandidate);
    const target = toEndpoint(targetCandidate);
    return {
        dependencyType: source.componentId === target.componentId
            ? 'inside-component'
            : 'cross-component',
        source,
        target,
        importSpecifier: input.importSpecifier,
        ...(resolvedTargetPath ? { resolvedTargetPath } : {}),
        isExternal: false,
    };
}
function createArchitectureDependencyContextResolver(contract) {
    const model = (0, architectureWorkspaceModel_1.normalizeArchitectureWorkspaceModel)(contract);
    const components = prepareComponentReferences(model.components);
    return {
        contract,
        model,
        resolve: (input) => resolveDependencyContextWithPreparedComponents(contract, components, input),
    };
}
function resolveDependencyContext(contract, input) {
    return createArchitectureDependencyContextResolver(contract).resolve(input);
}
//# sourceMappingURL=architectureDependencyContext.js.map