export class LibraryDependencyResolver {
    constructor(options) {
        this.debug = false;
        this.debug = options?.debug || false;
    }
    parseDependencies(json) {
        const dependencies = new Map();
        if (!json || json.trim() === '') {
            return dependencies;
        }
        try {
            const parsed = JSON.parse(json);
            if (typeof parsed === 'object' && parsed !== null) {
                for (const [name, version] of Object.entries(parsed)) {
                    if (typeof version === 'string') {
                        dependencies.set(name, version);
                    }
                }
            }
        }
        catch (error) {
            console.error('Failed to parse dependencies JSON:', error);
        }
        return dependencies;
    }
    buildDependencyGraph(libraries) {
        const nodes = new Map();
        const roots = new Set();
        for (const library of libraries) {
            const dependencies = this.parseDependencies(library.Dependencies);
            nodes.set(library.Name, {
                library,
                dependencies,
                dependents: new Set()
            });
            roots.add(library.Name);
        }
        for (const [name, node] of nodes) {
            for (const [depName, depVersion] of node.dependencies) {
                const depNode = nodes.get(depName);
                if (depNode) {
                    roots.delete(depName);
                    depNode.dependents.add(name);
                }
                else if (this.debug) {
                    console.warn(`Dependency '${depName}' not found for library '${name}'`);
                }
            }
        }
        return { nodes, roots };
    }
    detectCycles(graph) {
        const cycles = [];
        const visited = new Set();
        const recursionStack = new Set();
        const path = [];
        const detectCyclesUtil = (nodeName) => {
            visited.add(nodeName);
            recursionStack.add(nodeName);
            path.push(nodeName);
            const node = graph.nodes.get(nodeName);
            if (node) {
                for (const [depName] of node.dependencies) {
                    if (!visited.has(depName)) {
                        detectCyclesUtil(depName);
                    }
                    else if (recursionStack.has(depName)) {
                        const cycleStartIndex = path.indexOf(depName);
                        const cycle = [...path.slice(cycleStartIndex), depName];
                        cycles.push(cycle);
                    }
                }
            }
            path.pop();
            recursionStack.delete(nodeName);
        };
        for (const nodeName of graph.nodes.keys()) {
            if (!visited.has(nodeName)) {
                detectCyclesUtil(nodeName);
            }
        }
        return cycles;
    }
    topologicalSort(graph) {
        const result = [];
        const visited = new Set();
        const tempMarked = new Set();
        const visit = (nodeName) => {
            if (tempMarked.has(nodeName)) {
                return false;
            }
            if (visited.has(nodeName)) {
                return true;
            }
            tempMarked.add(nodeName);
            const node = graph.nodes.get(nodeName);
            if (node) {
                for (const [depName] of node.dependencies) {
                    if (graph.nodes.has(depName)) {
                        if (!visit(depName)) {
                            return false;
                        }
                    }
                }
                result.push(node.library);
            }
            tempMarked.delete(nodeName);
            visited.add(nodeName);
            return true;
        };
        for (const nodeName of graph.nodes.keys()) {
            if (!visited.has(nodeName)) {
                if (!visit(nodeName)) {
                    console.error(`Cycle detected involving library: ${nodeName}`);
                }
            }
        }
        return result;
    }
    parseVersion(version) {
        const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-([^+]+))?(?:\+(.+))?$/);
        if (!match) {
            return null;
        }
        return {
            major: parseInt(match[1], 10),
            minor: parseInt(match[2], 10),
            patch: parseInt(match[3], 10),
            prerelease: match[4],
            build: match[5]
        };
    }
    parseVersionRange(spec) {
        if (spec === '*' || spec === '' || spec === 'latest') {
            return { type: 'any', raw: spec };
        }
        if (spec.startsWith('~')) {
            const version = this.parseVersion(spec.substring(1));
            return {
                type: 'tilde',
                version: version || undefined,
                raw: spec
            };
        }
        if (spec.startsWith('^')) {
            const version = this.parseVersion(spec.substring(1));
            return {
                type: 'caret',
                version: version || undefined,
                raw: spec
            };
        }
        const version = this.parseVersion(spec);
        if (version) {
            return {
                type: 'exact',
                version,
                raw: spec
            };
        }
        return { type: 'any', raw: spec };
    }
    versionSatisfiesRange(version, range) {
        if (range.type === 'any') {
            return true;
        }
        if (!range.version) {
            return false;
        }
        const rangeVer = range.version;
        switch (range.type) {
            case 'exact':
                return version.major === rangeVer.major &&
                    version.minor === rangeVer.minor &&
                    version.patch === rangeVer.patch;
            case 'tilde':
                if (version.major !== rangeVer.major)
                    return false;
                if (version.minor !== rangeVer.minor)
                    return false;
                return version.patch >= rangeVer.patch;
            case 'caret':
                if (rangeVer.major === 0) {
                    if (version.major !== 0)
                        return false;
                    if (rangeVer.minor === 0) {
                        return version.minor === 0 && version.patch === rangeVer.patch;
                    }
                    if (version.minor !== rangeVer.minor)
                        return false;
                    return version.patch >= rangeVer.patch;
                }
                if (version.major !== rangeVer.major)
                    return false;
                if (version.minor < rangeVer.minor)
                    return false;
                if (version.minor === rangeVer.minor) {
                    return version.patch >= rangeVer.patch;
                }
                return true;
            default:
                return false;
        }
    }
    compareVersions(a, b) {
        if (a.major !== b.major)
            return a.major - b.major;
        if (a.minor !== b.minor)
            return a.minor - b.minor;
        if (a.patch !== b.patch)
            return a.patch - b.patch;
        if (!a.prerelease && b.prerelease)
            return 1;
        if (a.prerelease && !b.prerelease)
            return -1;
        if (a.prerelease && b.prerelease) {
            return a.prerelease.localeCompare(b.prerelease);
        }
        return 0;
    }
    resolveVersionConflicts(requirements, availableLibraries) {
        if (requirements.length === 0) {
            throw new Error('No version requirements provided');
        }
        const libraryName = requirements[0].library;
        const warnings = [];
        const availableVersions = availableLibraries
            .filter(lib => lib.Name === libraryName && lib.Version)
            .map(lib => ({
            library: lib,
            version: this.parseVersion(lib.Version)
        }))
            .filter(item => item.version !== null)
            .sort((a, b) => this.compareVersions(b.version, a.version));
        if (availableVersions.length === 0) {
            throw new Error(`No versions available for library '${libraryName}'`);
        }
        const ranges = requirements.map(req => ({
            ...req,
            range: this.parseVersionRange(req.versionSpec)
        }));
        for (const { library, version } of availableVersions) {
            let satisfiesAll = true;
            const satisfiedRequirements = [];
            for (const req of ranges) {
                if (this.versionSatisfiesRange(version, req.range)) {
                    satisfiedRequirements.push(req);
                }
                else {
                    satisfiesAll = false;
                    warnings.push(`Version ${library.Version} does not satisfy '${req.versionSpec}' required by ${req.requestedBy}`);
                    break;
                }
            }
            if (satisfiesAll) {
                return {
                    library: libraryName,
                    version: library.Version,
                    satisfies: requirements,
                    warnings: warnings.length > 0 ? warnings : undefined
                };
            }
        }
        const latest = availableVersions[0];
        warnings.push(`Could not find a version that satisfies all requirements. Using ${latest.library.Version}`);
        return {
            library: libraryName,
            version: latest.library.Version,
            satisfies: [],
            warnings
        };
    }
    getLoadOrder(requestedLibs, allLibs, options) {
        const errors = [];
        const warnings = [];
        const validRequestedLibs = requestedLibs.filter(lib => {
            if (!lib || typeof lib !== 'string') {
                const warning = `Invalid library name: ${lib} (type: ${typeof lib})`;
                warnings.push(warning);
                if (this.debug || options?.debug) {
                    console.warn(`⚠️ ${warning}`);
                }
                return false;
            }
            return true;
        });
        if (this.debug || options?.debug) {
            console.log('🔍 Getting load order for requested libraries:');
            console.log('  📝 Requested (raw):', requestedLibs);
            console.log('  📝 Requested (valid):', validRequestedLibs);
            console.log('  📚 Total available libraries:', allLibs.length);
        }
        const libMap = new Map();
        for (const lib of allLibs) {
            if (!lib?.Name) {
                warnings.push(`Library with missing name found in available libraries`);
                continue;
            }
            const key = lib.Name.toLowerCase();
            if (!libMap.has(key)) {
                libMap.set(key, []);
            }
            libMap.get(key).push(lib);
        }
        const needed = new Set();
        const toProcess = [...validRequestedLibs];
        const processed = new Set();
        const versionRequirements = new Map();
        let depth = 0;
        const maxDepth = options?.maxDepth || 10;
        while (toProcess.length > 0 && depth < maxDepth) {
            const current = toProcess.shift();
            if (!current || typeof current !== 'string') {
                const warning = `Unexpected invalid library name during processing: ${current}`;
                warnings.push(warning);
                if (this.debug || options?.debug) {
                    console.warn(`⚠️ ${warning}`);
                }
                continue;
            }
            if (processed.has(current))
                continue;
            processed.add(current);
            needed.add(current);
            const libVersions = libMap.get(current.toLowerCase());
            if (!libVersions || libVersions.length === 0) {
                if (this.debug || options?.debug) {
                    console.log(`    ❌ Library '${current}' not found in available libraries`);
                }
                errors.push(`Library '${current}' not found`);
                continue;
            }
            const lib = libVersions[0];
            const deps = this.parseDependencies(lib.Dependencies);
            if ((this.debug || options?.debug) && deps.size > 0) {
                console.log(`    📌 ${current} requires:`, Array.from(deps.entries()));
            }
            for (const [depName, depVersion] of deps) {
                needed.add(depName);
                if (!versionRequirements.has(depName)) {
                    versionRequirements.set(depName, []);
                }
                versionRequirements.get(depName).push({
                    library: depName,
                    versionSpec: depVersion,
                    requestedBy: current
                });
                if (!processed.has(depName)) {
                    toProcess.push(depName);
                }
            }
            depth++;
        }
        if (depth >= maxDepth) {
            warnings.push(`Maximum dependency depth (${maxDepth}) reached`);
        }
        const resolvedLibraries = [];
        for (const libName of needed) {
            const requirements = versionRequirements.get(libName) || [];
            const versions = libMap.get(libName.toLowerCase()) || [];
            if (versions.length === 0) {
                errors.push(`Library '${libName}' not found`);
                continue;
            }
            if (requirements.length > 0) {
                try {
                    const resolved = this.resolveVersionConflicts(requirements, versions);
                    const selectedLib = versions.find(lib => lib.Version === resolved.version);
                    if (selectedLib) {
                        resolvedLibraries.push(selectedLib);
                        if (resolved.warnings) {
                            warnings.push(...resolved.warnings);
                        }
                    }
                }
                catch (error) {
                    errors.push(error.message);
                    resolvedLibraries.push(versions[0]);
                }
            }
            else {
                resolvedLibraries.push(versions[0]);
            }
        }
        const graph = this.buildDependencyGraph(resolvedLibraries);
        const cycles = this.detectCycles(graph);
        if (cycles.length > 0) {
            errors.push(`Circular dependencies detected: ${cycles.map(c => c.join(' -> ')).join(', ')}`);
            return {
                success: false,
                cycles,
                errors,
                warnings: warnings.length > 0 ? warnings : undefined
            };
        }
        const sorted = this.topologicalSort(graph);
        if (this.debug || options?.debug) {
            console.log('✅ Load order determined:', sorted.map(lib => `${lib.Name}@${lib.Version}`));
        }
        return {
            success: errors.length === 0,
            order: sorted,
            errors: errors.length > 0 ? errors : undefined,
            warnings: warnings.length > 0 ? warnings : undefined
        };
    }
    getDirectDependencies(library) {
        return this.parseDependencies(library.Dependencies);
    }
    getTransitiveDependencies(libraryName, allLibs, maxDepth = 10) {
        const dependencies = new Set();
        const toProcess = [libraryName];
        const processed = new Set();
        let depth = 0;
        const libMap = new Map();
        for (const lib of allLibs) {
            libMap.set(lib.Name.toLowerCase(), lib);
        }
        while (toProcess.length > 0 && depth < maxDepth) {
            const current = toProcess.shift();
            if (processed.has(current))
                continue;
            processed.add(current);
            const lib = libMap.get(current.toLowerCase());
            if (!lib)
                continue;
            const deps = this.parseDependencies(lib.Dependencies);
            for (const [depName] of deps) {
                dependencies.add(depName);
                if (!processed.has(depName)) {
                    toProcess.push(depName);
                }
            }
            depth++;
        }
        return dependencies;
    }
}
//# sourceMappingURL=library-dependency-resolver.js.map