import { readdir, readFile, stat } from "node:fs/promises";
import path from "node:path";
const SDK_PACKAGE_NAME = "@modelcontextprotocol/sdk";
const FORBIDDEN_DEPENDENCY_KEYS = [
    "dependencies",
    "peerDependencies",
    "optionalDependencies",
];
const ALLOWED_DEPENDENCY_KEY = "devDependencies";
const defaultFs = {
    readFile,
    readdir,
    stat,
};
export async function auditMcpSdkDependencyUsage(options) {
    const fs = options.fs ?? defaultFs;
    const packageJsonPaths = await listPackageJsonPaths(options.packagesDir, fs);
    const findings = await Promise.all(packageJsonPaths.map(async (manifestPath) => inspectPackageJson(manifestPath, options.packagesDir, fs)));
    const filteredFindings = findings.filter((finding) => finding !== undefined);
    return {
        packagesDir: options.packagesDir,
        packageJsonPaths,
        findings: filteredFindings,
        violations: filteredFindings.filter(hasForbiddenSdkDependency),
    };
}
export async function assertMcpSdkIsDevDependencyOnly(options) {
    const report = await auditMcpSdkDependencyUsage(options);
    if (report.violations.length === 0) {
        return;
    }
    throw new Error(report.violations
        .flatMap((finding) => finding.occurrences
        .filter((occurrence) => occurrence.key !== ALLOWED_DEPENDENCY_KEY)
        .map((occurrence) => `${finding.manifestPath} -> ${occurrence.key}["${SDK_PACKAGE_NAME}"] must move to devDependencies.`))
        .join("\n"));
}
function hasForbiddenSdkDependency(finding) {
    return finding.occurrences.some((occurrence) => occurrence.key !== ALLOWED_DEPENDENCY_KEY);
}
async function inspectPackageJson(manifestPath, packagesDir, fs) {
    const rawManifest = await fs.readFile(manifestPath, "utf8");
    const manifest = JSON.parse(rawManifest);
    const occurrences = readSdkOccurrences(manifest);
    if (occurrences.length === 0) {
        return undefined;
    }
    return {
        manifestPath: path.relative(path.dirname(packagesDir), manifestPath),
        ...(typeof manifest.name === "string" ? { packageName: manifest.name } : {}),
        occurrences,
    };
}
function readSdkOccurrences(manifest) {
    const occurrences = [];
    for (const key of [ALLOWED_DEPENDENCY_KEY, ...FORBIDDEN_DEPENDENCY_KEYS]) {
        const dependencies = manifest[key];
        const version = dependencies?.[SDK_PACKAGE_NAME];
        if (typeof version === "string") {
            occurrences.push({
                key,
                version,
            });
        }
    }
    return occurrences;
}
async function listPackageJsonPaths(rootDir, fs) {
    const entries = (await fs.readdir(rootDir)).sort((left, right) => left.localeCompare(right));
    const paths = await Promise.all(entries.map(async (entry) => {
        const entryPath = path.join(rootDir, entry);
        const entryStat = await fs.stat(entryPath);
        if (!entryStat.isDirectory() || entry === "node_modules") {
            return [];
        }
        const manifestPath = path.join(entryPath, "package.json");
        try {
            const manifestStat = await fs.stat(manifestPath);
            return manifestStat.isFile() ? [manifestPath] : [];
        }
        catch {
            return [];
        }
    }));
    return paths.flat();
}
