import { isDefined, setIsEqual } from '@takeshape/util';
import { dereferenceSchema, getRef, serializeRef } from "../refs.js";
import { isObjectSchema } from "../types/index.js";
/**
 * Normalize ref, oneOf and array schemas and get a list of @ref expressions
 */
function getRefStrings(context, propertySchema) {
    const schema = propertySchema.items ?? propertySchema;
    const schemas = schema.oneOf ? schema.oneOf : [schema];
    return schemas
        .map((propertySchema) => {
        const ref = getRef(context, propertySchema);
        return ref ? serializeRef(ref) : undefined;
    })
        .filter(isDefined);
}
function isRefEqual(context, a, b) {
    return setIsEqual(getRefStrings(context, a), getRefStrings(context, b));
}
/**
 * Given a list of shapes find the conflicting props
 */
export function getConflictingProperties(context, shapes) {
    const conflicts = new Set();
    const allProps = {};
    for (const shape of Object.values(shapes)) {
        const schema = dereferenceSchema(context, shape.schema, ['shapes', shape.name]);
        if (!isObjectSchema(schema)) {
            continue;
        }
        const required = new Set(schema.required ?? []);
        for (const [name, prop] of Object.entries(schema.properties)) {
            // skip props that we know already have  conflict
            if (conflicts.has(name)) {
                continue;
            }
            const isRequired = required.has(name);
            if (allProps[name]) {
                if (
                // same type
                allProps[name][0].type !== prop.type ||
                    // same refs
                    !isRefEqual(context, allProps[name][0], prop) ||
                    // same nullability
                    isRequired !== allProps[name][1]) {
                    conflicts.add(name);
                }
            }
            else {
                allProps[name] = [prop, isRequired];
            }
        }
    }
    return conflicts;
}
