import get from 'lodash/get';
import isEqual from 'lodash/isEqual';
import set from 'lodash/set';
import { ALL_OF_KEY, ANY_OF_KEY, ADDITIONAL_PROPERTIES_KEY, DEPENDENCIES_KEY, ITEMS_KEY, NAME_KEY, ONE_OF_KEY, PROPERTIES_KEY, REF_KEY, RJSF_ADDITIONAL_PROPERTIES_FLAG, } from '../constants';
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema';
import getClosestMatchingOption from './getClosestMatchingOption';
import retrieveSchema from './retrieveSchema';
/** An internal helper that generates an `PathSchema` object for the `schema`, recursively with protection against
 * infinite recursion
 *
 * @param validator - An implementation of the `ValidatorType` interface that will be used when necessary
 * @param schema - The schema for which the `PathSchema` is desired
 * @param [name=''] - The base name for the schema
 * @param [rootSchema] - The root schema, used to primarily to look up `$ref`s
 * @param [formData] - The current formData, if any, to assist retrieving a schema
 * @param [_recurseList=[]] - The list of retrieved schemas currently being recursed, used to prevent infinite recursion
 * @returns - The `PathSchema` object for the `schema`
 */
function toPathSchemaInternal(validator, schema, name, rootSchema, formData, _recurseList = []) {
    if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
        const _schema = retrieveSchema(validator, schema, rootSchema, formData);
        const sameSchemaIndex = _recurseList.findIndex((item) => isEqual(item, _schema));
        if (sameSchemaIndex === -1) {
            return toPathSchemaInternal(validator, _schema, name, rootSchema, formData, _recurseList.concat(_schema));
        }
    }
    let pathSchema = {
        [NAME_KEY]: name.replace(/^\./, ''),
    };
    if (ONE_OF_KEY in schema || ANY_OF_KEY in schema) {
        const xxxOf = ONE_OF_KEY in schema ? schema.oneOf : schema.anyOf;
        const discriminator = getDiscriminatorFieldFromSchema(schema);
        const index = getClosestMatchingOption(validator, rootSchema, formData, xxxOf, 0, discriminator);
        const _schema = xxxOf[index];
        pathSchema = {
            ...pathSchema,
            ...toPathSchemaInternal(validator, _schema, name, rootSchema, formData, _recurseList),
        };
    }
    if (ADDITIONAL_PROPERTIES_KEY in schema && schema[ADDITIONAL_PROPERTIES_KEY] !== false) {
        set(pathSchema, RJSF_ADDITIONAL_PROPERTIES_FLAG, true);
    }
    if (ITEMS_KEY in schema && Array.isArray(formData)) {
        const { items: schemaItems, additionalItems: schemaAdditionalItems } = schema;
        if (Array.isArray(schemaItems)) {
            formData.forEach((element, i) => {
                if (schemaItems[i]) {
                    pathSchema[i] = toPathSchemaInternal(validator, schemaItems[i], `${name}.${i}`, rootSchema, element, _recurseList);
                }
                else if (schemaAdditionalItems) {
                    pathSchema[i] = toPathSchemaInternal(validator, schemaAdditionalItems, `${name}.${i}`, rootSchema, element, _recurseList);
                }
                else {
                    console.warn(`Unable to generate path schema for "${name}.${i}". No schema defined for it`);
                }
            });
        }
        else {
            formData.forEach((element, i) => {
                pathSchema[i] = toPathSchemaInternal(validator, schemaItems, `${name}.${i}`, rootSchema, element, _recurseList);
            });
        }
    }
    else if (PROPERTIES_KEY in schema) {
        for (const property in schema.properties) {
            const field = get(schema, [PROPERTIES_KEY, property]);
            pathSchema[property] = toPathSchemaInternal(validator, field, `${name}.${property}`, rootSchema, 
            // It's possible that formData is not an object -- this can happen if an
            // array item has just been added, but not populated with data yet
            get(formData, [property]), _recurseList);
        }
    }
    return pathSchema;
}
/** Generates an `PathSchema` object for the `schema`, recursively
 *
 * @param validator - An implementation of the `ValidatorType` interface that will be used when necessary
 * @param schema - The schema for which the `PathSchema` is desired
 * @param [name=''] - The base name for the schema
 * @param [rootSchema] - The root schema, used to primarily to look up `$ref`s
 * @param [formData] - The current formData, if any, to assist retrieving a schema
 * @returns - The `PathSchema` object for the `schema`
 */
export default function toPathSchema(validator, schema, name = '', rootSchema, formData) {
    return toPathSchemaInternal(validator, schema, name, rootSchema, formData);
}
//# sourceMappingURL=toPathSchema.js.map