import { BadDataError } from '@takeshape/errors';
import { camelCase, isRecord } from '@takeshape/util';
import omit from 'lodash/omit.js';
import { getRef, refItemToNamespacedShapeName } from "./refs.js";
import { getOutputShapeName } from "./template-shapes/names.js";
import { isRefSchema } from "./types/utils.js";
import { getShapeById } from "./util/shapes.js";
export function enumerateOneOfKeys(projectSchema, oneOf) {
    const result = [];
    for (const child of oneOf) {
        const refItem = getRef(projectSchema, child);
        if (refItem) {
            const shapeName = refItemToNamespacedShapeName(projectSchema, refItem);
            if (projectSchema.shapes[shapeName]) {
                const shapeId = projectSchema.shapes[shapeName].id;
                result.push({
                    propName: getOutputShapeName(camelCase(refItem.shapeName)),
                    propSchema: child,
                    shapeName,
                    shapeId
                });
            }
        }
    }
    return result;
}
function getOneOf(schema) {
    if (!schema.oneOf) {
        throw new Error(`Expected ${JSON.stringify(schema)} to be a oneOf schema`);
    }
    return schema.oneOf;
}
export function isUnionSchema(schema) {
    return Boolean(schema.oneOf?.every(isRefSchema));
}
export function oneOfToObject(projectSchema, schema) {
    const properties = {};
    for (const { propName, propSchema: child } of enumerateOneOfKeys(projectSchema, getOneOf(schema))) {
        properties[propName] = child;
    }
    return { ...omit(schema, 'oneOf'), type: 'object', properties };
}
function formatKeys(keys) {
    return keys.map((k) => `"${k}"`).join(', ');
}
export const createUnwrapTransform = (projectSchema) => (obj, schema, name, shape, next) => {
    if (isRecord(obj)) {
        const oneOfKeys = enumerateOneOfKeys(projectSchema, getOneOf(schema));
        const objectKeys = Object.keys(obj);
        if (objectKeys.length !== 1) {
            const propNames = oneOfKeys.map((k) => k.propName);
            throw new BadDataError(`Object should only specify one of the following keys: ${formatKeys(propNames)}, but found keys: ${formatKeys(objectKeys)}`);
        }
        for (const { propName, shapeId, propSchema } of oneOfKeys) {
            const propValue = obj[propName];
            if (isRecord(propValue)) {
                const transformedPropValue = next(propValue, propSchema, name);
                if (isRecord(transformedPropValue)) {
                    return {
                        ...transformedPropValue,
                        _shapeId: shapeId
                    };
                }
            }
        }
    }
};
export const createWrapTransform = (projectSchema) => (obj, schema, name, shape, next) => {
    if (isRecord(obj) && typeof obj._shapeId === 'string') {
        const shape = getShapeById(projectSchema, obj._shapeId);
        if (shape) {
            return {
                [camelCase(shape.name)]: next(omit(obj, '_shapeId'), shape.schema, name)
            };
        }
    }
};
