import { deepClone } from '@takeshape/util';
import set from 'lodash/fp/set.js';
import unset from 'lodash/fp/unset.js';
import upperFirst from 'lodash/upperFirst.js';
import { SERVICE_OBJECT_PATTERN_NAME } from "../../constants.js";
import { getRefOrItemsRef, refItemToNamespacedShapeName } from "../../refs.js";
import { createSchemaPropertyAccessor } from "../../schema-util.js";
import { CURRENT_SCHEMA_VERSION } from "../../schemas/index.js";
import { getServiceObjectFields } from "../../services/services.js";
// Map of form studio widgets and their replacements in the shape editor
const widgetMigrations = {
    object: 'shapeObject',
    repeater: 'shapeArray'
};
export function normalizeForms(projectSchema) {
    const normalizedForms = {};
    const { forms = {} } = projectSchema;
    const storeSourceForm = (shapeName, formConfig, propName) => {
        normalizedForms[shapeName] ||= { default: deepClone(formConfig) };
        const propConfig = normalizedForms[shapeName].default.properties[propName];
        propConfig.properties = undefined;
        propConfig.order = undefined;
        propConfig.widget = widgetMigrations[propConfig.widget] ?? propConfig.widget;
    };
    const normalizeNested = (shapeName, formConfig) => {
        const shape = projectSchema.shapes[shapeName];
        if (!shape || !formConfig.properties) {
            return;
        }
        const propertyAccessor = createSchemaPropertyAccessor(projectSchema, shape);
        for (const propName of Object.keys(formConfig.properties)) {
            const propConfig = formConfig.properties[propName];
            const propSchema = propertyAccessor.getValue(propName);
            if (propSchema) {
                const ref = getRefOrItemsRef(projectSchema, propSchema);
                if (ref && propConfig.properties && propConfig.widget !== 'shopify') {
                    const nestedShapeName = refItemToNamespacedShapeName(projectSchema, ref);
                    normalizedForms[nestedShapeName] = { default: deepClone(propConfig) };
                    storeSourceForm(shapeName, formConfig, propName);
                    normalizeNested(nestedShapeName, propConfig);
                }
            }
        }
    };
    if (forms) {
        for (const shapeName of Object.keys(forms)) {
            normalizeNested(shapeName, forms[shapeName].default);
            // copy forms with no changes
            normalizedForms[shapeName] ||= forms[shapeName];
        }
    }
    return normalizedForms;
}
export function migrateInterfaceShapes(projectSchema) {
    let updatedProjectSchema = projectSchema;
    for (const shape of Object.values(projectSchema.shapes)) {
        if (!shape.model) {
            continue;
        }
        // Set the schema version to the current to appease type check this line will eventually break as the schema diverges
        const serviceObjectFields = getServiceObjectFields({ ...projectSchema, schemaVersion: CURRENT_SCHEMA_VERSION }, shape, 'shopify');
        for (const field of serviceObjectFields) {
            const { fieldName, service } = field;
            const { serviceObjectType } = field.generators[SERVICE_OBJECT_PATTERN_NAME];
            const serviceShapeName = upperFirst(serviceObjectType);
            const interfaceShapeName = `${serviceShapeName}Interface`;
            updatedProjectSchema = unset(['shapes', interfaceShapeName], updatedProjectSchema);
            if ('type' in shape.schema && shape.schema.type === 'object') {
                const prop = shape.schema.properties[fieldName];
                if (prop) {
                    updatedProjectSchema = set(['shapes', shape.name, 'schema', 'properties', fieldName, '@input'], { '@ref': `${service}:${serviceShapeName}Input` }, updatedProjectSchema);
                }
            }
            for (const [mutationName, mutation] of Object.entries(projectSchema.mutations)) {
                if (!mutation || typeof mutation.args !== 'string') {
                    continue;
                }
                for (const template of ['CreateArgs', 'UpdateArgs']) {
                    if (mutation.args === `${template}<${interfaceShapeName}>`) {
                        updatedProjectSchema = set(['mutations', mutationName], { ...mutation, args: `${template}<${serviceShapeName}>` }, updatedProjectSchema);
                    }
                }
            }
        }
    }
    return updatedProjectSchema;
}
const migrate = async (_, projectSchema) => {
    let normalized = projectSchema;
    try {
        normalized = {
            ...projectSchema,
            forms: normalizeForms(projectSchema)
        };
    }
    catch (e) {
        // TODO remove console.log when a formal warning mechanism is added https://app.shortcut.com/takeshape/story/11533/return-warnings-from-schema-migration
        console.info('v3.34.0 migration warning: Failed to normalize forms', e.message);
    }
    try {
        normalized = migrateInterfaceShapes(normalized);
    }
    catch (e) {
        console.info('v3.34.0 migration warning: Failed to migrate interface shapes', e.message);
    }
    return {
        ...normalized,
        schemaVersion: '3.34.0'
    };
};
export default migrate;
