import { DeclarationReflection, ReflectionKind, ReflectionType, } from "../models/index.js";
import { i18n, removeFlag } from "#utils";
export function validateDocumentation(project, logger, requiredToBeDocumented, intentionallyNotDocumented, packagesRequiringDocumentation) {
    let kinds = requiredToBeDocumented.reduce((prev, cur) => prev | ReflectionKind[cur], 0);
    // Functions, Constructors, and Accessors never have comments directly on them.
    // If they are required to be documented, what's really required is that their
    // contained signatures have a comment.
    if (kinds & ReflectionKind.FunctionOrMethod) {
        kinds |= ReflectionKind.CallSignature;
        kinds = removeFlag(kinds, ReflectionKind.FunctionOrMethod);
    }
    if (kinds & ReflectionKind.Constructor) {
        kinds |= ReflectionKind.ConstructorSignature;
        kinds = removeFlag(kinds, ReflectionKind.Constructor);
    }
    if (kinds & ReflectionKind.Accessor) {
        kinds |= ReflectionKind.GetSignature | ReflectionKind.SetSignature;
        kinds = removeFlag(kinds, ReflectionKind.Accessor);
    }
    const toProcess = project.getReflectionsByKind(kinds);
    const seen = new Set();
    const intentionalUsage = new Set();
    outer: while (toProcess.length) {
        const ref = toProcess.shift();
        if (seen.has(ref))
            continue;
        seen.add(ref);
        // If inside a parameter, we shouldn't care. Callback parameter's values don't get deeply documented.
        let r = ref.parent;
        while (r) {
            if (r.kindOf(ReflectionKind.Parameter)) {
                continue outer;
            }
            r = r.parent;
        }
        // Type aliases own their comments, even if they're function-likes.
        // So if we're a type literal owned by a type alias, don't do anything.
        if (ref.kindOf(ReflectionKind.TypeLiteral) &&
            ref.parent?.kindOf(ReflectionKind.TypeAlias)) {
            toProcess.push(ref.parent);
            continue;
        }
        // Call signatures are considered documented if they have a comment directly, or their
        // container has a comment and they are directly within a type literal belonging to that container.
        if (ref.kindOf(ReflectionKind.CallSignature) &&
            ref.parent?.kindOf(ReflectionKind.TypeLiteral)) {
            toProcess.push(ref.parent.parent);
            continue;
        }
        // Construct signatures are considered documented if they are directly within a documented type alias.
        if (ref.kindOf(ReflectionKind.ConstructorSignature) &&
            ref.parent?.parent?.kindOf(ReflectionKind.TypeAlias)) {
            toProcess.push(ref.parent.parent);
            continue;
        }
        if (ref instanceof DeclarationReflection) {
            const signatures = ref.type instanceof ReflectionType
                ? ref.type.declaration.getNonIndexSignatures()
                : ref.getNonIndexSignatures();
            if (signatures.length) {
                // We've been asked to validate this reflection, so we should validate that
                // signatures all have comments
                toProcess.push(...signatures);
                if (ref.kindOf(ReflectionKind.SignatureContainer)) {
                    // Comments belong to each signature, and will not be included on this object.
                    continue;
                }
            }
        }
        const symbolId = project.getSymbolIdFromReflection(ref);
        // #2644, signatures may be documented by their parent reflection.
        const hasComment = ref.hasComment() ||
            (ref.kindOf(ReflectionKind.SomeSignature) &&
                ref.parent?.hasComment());
        if (!hasComment && symbolId) {
            if (!packagesRequiringDocumentation.includes(symbolId.packageName)) {
                continue;
            }
            const intentionalIndex = intentionallyNotDocumented.indexOf(ref.getFriendlyFullName());
            if (intentionalIndex !== -1) {
                intentionalUsage.add(intentionalIndex);
                continue;
            }
            logger.warn(i18n.reflection_0_kind_1_defined_in_2_does_not_have_any_documentation(ref.getFriendlyFullName(), ReflectionKind[ref.kind], `${symbolId.packageName}/${symbolId.packagePath}`));
        }
    }
    const unusedIntentional = intentionallyNotDocumented.filter((_, i) => !intentionalUsage.has(i));
    if (unusedIntentional.length) {
        logger.warn(i18n.invalid_intentionally_not_documented_names_0(unusedIntentional.join("\n\t")));
    }
}
