import { allPackages, dependencyEdges, isPublishedNpm } from "../model.js";
const id = "no-published-to-private-dep";
/**
 * No published package may depend (dependencies / peerDependencies /
 * optionalDependencies) on a private workspace package — it is never published,
 * so the consumer ships a dependency that cannot be installed.
 * `optionalDependencies` is the exact same-name collision vector.
 */
export const noPublishedToPrivateDep = {
    id,
    run(model) {
        const violations = [];
        for (const consumer of allPackages(model)) {
            if (!isPublishedNpm(consumer))
                continue;
            for (const edge of dependencyEdges(consumer)) {
                const dep = model.byName.get(edge.name);
                if (!dep)
                    continue;
                if (edge.field !== "peerDependencies" && consumer.bundledDependencies.includes(dep.name)) {
                    violations.push(...collectBundledPrivateDependencyViolations({
                        consumer,
                        bundledRoot: dep,
                        modelByName: model.byName,
                        consumerBundled: new Set(consumer.bundledDependencies),
                        via: [dep.name]
                    }));
                    if (dep.private)
                        continue;
                }
                if (!dep.private) {
                    continue;
                }
                violations.push({
                    rule: id,
                    package: consumer.name,
                    severity: "error",
                    via: edge.field,
                    detail: { dependency: dep.name, field: edge.field },
                    message: `published package depends on private workspace package ${dep.name} via ${edge.field}`,
                    fix: `Drop the ${edge.field} on ${dep.name}, publish ${dep.name}, or vendor it with a pinned range (bundledDependencies) — a private package never reaches npm.`
                });
            }
        }
        return violations;
    }
};
function collectBundledPrivateDependencyViolations(options) {
    const violations = [];
    const visited = new Set();
    const pending = [
        { pkg: options.bundledRoot, via: options.via }
    ];
    while (pending.length > 0) {
        const current = pending.shift();
        if (visited.has(current.pkg.name)) {
            continue;
        }
        visited.add(current.pkg.name);
        for (const edge of dependencyEdges(current.pkg)) {
            const dep = options.modelByName.get(edge.name);
            if (!dep || !dep.private) {
                continue;
            }
            if (isBundledRuntimeEdge(current.pkg, edge, dep, options.consumerBundled)) {
                pending.push({ pkg: dep, via: [...current.via, dep.name] });
                continue;
            }
            violations.push({
                rule: id,
                package: options.consumer.name,
                severity: "error",
                via: edge.field,
                detail: {
                    dependency: dep.name,
                    field: edge.field,
                    bundledVia: current.via
                },
                message: `published package bundles ${current.via.join(" -> ")}, whose ${edge.field} requires private workspace package ${dep.name}`,
                fix: `Bundle ${dep.name} into ${current.pkg.name}, publish ${dep.name}, or remove the ${edge.field} edge from the bundled package manifest.`
            });
        }
    }
    return violations;
}
function isBundledRuntimeEdge(pkg, edge, dep, consumerBundled) {
    return (edge.field !== "peerDependencies" &&
        (pkg.bundledDependencies.includes(dep.name) || consumerBundled.has(dep.name)));
}
