/**
 * .NET semantic registry loading
 */
import * as fs from "node:fs";
import * as path from "node:path";
import { DotnetMetadataRegistry, } from "../dotnet-metadata.js";
import { getDotnetBindingPayload } from "./dotnet-binding-payload.js";
import { resolveDependencyPackageRoot } from "./package-roots.js";
/**
 * Recursively scan a directory for tsbindgen `<Namespace>/bindings.json` files.
 */
const scanForBindingsFiles = (dir) => {
    if (!fs.existsSync(dir)) {
        return [];
    }
    const results = [];
    const entries = fs.readdirSync(dir, { withFileTypes: true });
    for (const entry of entries) {
        const fullPath = path.join(dir, entry.name);
        if (entry.isDirectory()) {
            if (entry.name === "node_modules" || entry.name === ".git") {
                continue;
            }
            results.push(...scanForBindingsFiles(fullPath));
        }
        else if (entry.name === "bindings.json") {
            results.push(fullPath);
        }
    }
    return results;
};
const isSourcePackageRoot = (packageRoot) => {
    const manifestPath = path.join(packageRoot, "tsonic.package.json");
    if (!fs.existsSync(manifestPath)) {
        return false;
    }
    try {
        const parsed = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
        return parsed.kind === "tsonic-source-package";
    }
    catch {
        return false;
    }
};
const loadMetadataFromPackage = (registry, packageRoot, visited, forceDependencyTraversal = false) => {
    const absoluteRoot = path.resolve(packageRoot);
    if (visited.has(absoluteRoot)) {
        return;
    }
    visited.add(absoluteRoot);
    if (!fs.existsSync(absoluteRoot)) {
        return;
    }
    const sourcePackageRoot = isSourcePackageRoot(absoluteRoot);
    const bindingsFiles = sourcePackageRoot
        ? []
        : scanForBindingsFiles(absoluteRoot);
    let discoveredClrBindingsInPackage = false;
    for (const bindingsPath of bindingsFiles) {
        try {
            const content = fs.readFileSync(bindingsPath, "utf-8");
            const parsed = JSON.parse(content);
            const dotnetPayload = getDotnetBindingPayload(parsed);
            if (dotnetPayload) {
                discoveredClrBindingsInPackage = true;
                registry.loadBindingsFile(bindingsPath, dotnetPayload);
            }
        }
        catch (err) {
            console.warn(`Failed to load bindings from ${bindingsPath}:`, err);
        }
    }
    const hasBindingsManifest = !sourcePackageRoot &&
        fs.existsSync(path.join(absoluteRoot, "tsonic.bindings.json"));
    const hasSurfaceManifest = fs.existsSync(path.join(absoluteRoot, "tsonic.surface.json"));
    const shouldTraverseDependencies = forceDependencyTraversal ||
        sourcePackageRoot ||
        discoveredClrBindingsInPackage ||
        hasBindingsManifest ||
        hasSurfaceManifest;
    const packageJsonPath = path.join(absoluteRoot, "package.json");
    if (!shouldTraverseDependencies || !fs.existsSync(packageJsonPath)) {
        return;
    }
    try {
        const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
        const dependencyNames = new Set();
        const dependencyBuckets = [
            packageJson.dependencies,
            packageJson.optionalDependencies,
            packageJson.peerDependencies,
        ];
        for (const bucket of dependencyBuckets) {
            if (bucket !== null &&
                typeof bucket === "object" &&
                !Array.isArray(bucket)) {
                for (const depName of Object.keys(bucket)) {
                    dependencyNames.add(depName);
                }
            }
        }
        for (const depName of dependencyNames) {
            const dependencyRoot = resolveDependencyPackageRoot(absoluteRoot, depName);
            if (dependencyRoot) {
                loadMetadataFromPackage(registry, dependencyRoot, visited, false);
            }
        }
    }
    catch {
        // Ignore unreadable or invalid package manifests.
    }
};
/**
 * Load .NET semantic info from configured type roots.
 *
 * tsbindgen emits a single manifest per namespace: `<Namespace>/bindings.json`.
 * This loader scans the configured type roots and loads all discovered manifests.
 */
export const loadDotnetMetadata = (typeRoots) => {
    const registry = new DotnetMetadataRegistry();
    const visited = new Set();
    for (const typeRoot of typeRoots) {
        loadMetadataFromPackage(registry, typeRoot, visited, true);
    }
    return registry;
};
//# sourceMappingURL=metadata.js.map