import ObjectIdImport from 'bson-objectid';
import { canAccessAdmin, traverseFields } from 'payload';
import { fieldAffectsData, fieldShouldBeLocalized, tabHasName } from 'payload/shared';
const ObjectId = 'default' in ObjectIdImport ? ObjectIdImport.default : ObjectIdImport;
function iterateFields(fields, fromLocaleData, toLocaleData, req, parentIsLocalized) {
    fields.map((field)=>{
        if (fieldAffectsData(field)) {
            switch(field.type){
                case 'array':
                    // if the field has no value, take the source value
                    if (field.name in toLocaleData && // only replace if the target value is null or undefined
                    [
                        null,
                        undefined
                    ].includes(toLocaleData[field.name]) && field.name in fromLocaleData) {
                        toLocaleData[field.name] = fromLocaleData[field.name];
                        break;
                    }
                    // if the field has a value - loop over the data from target
                    if (field.name in toLocaleData) {
                        toLocaleData[field.name].map((item, index)=>{
                            if (fromLocaleData[field.name]?.[index]) {
                                // Generate new IDs if the field is localized to prevent errors with relational DBs.
                                if (fieldShouldBeLocalized({
                                    field,
                                    parentIsLocalized
                                })) {
                                    toLocaleData[field.name][index].id = new ObjectId().toHexString();
                                }
                                iterateFields(field.fields, fromLocaleData[field.name][index], item, req, parentIsLocalized || field.localized);
                            }
                        });
                    }
                    break;
                case 'blocks':
                    // if the field has no value, take the source value
                    if (field.name in toLocaleData && // only replace if the target value is null, undefined, or empty array
                    ([
                        null,
                        undefined
                    ].includes(toLocaleData[field.name]) || Array.isArray(toLocaleData[field.name]) && toLocaleData[field.name].length === 0) && field.name in fromLocaleData) {
                        toLocaleData[field.name] = fromLocaleData[field.name];
                        break;
                    }
                    // if the field has a value - loop over the data from target
                    if (field.name in toLocaleData) {
                        toLocaleData[field.name].map((blockData, index)=>{
                            const block = req.payload.blocks[blockData.blockType] ?? (field.blockReferences ?? field.blocks).find((block)=>typeof block !== 'string' && block.slug === blockData.blockType);
                            // Generate new IDs if the field is localized to prevent errors with relational DBs.
                            if (fieldShouldBeLocalized({
                                field,
                                parentIsLocalized
                            })) {
                                toLocaleData[field.name][index].id = new ObjectId().toHexString();
                            }
                            if (block?.fields?.length) {
                                iterateFields(block?.fields, fromLocaleData[field.name][index], blockData, req, parentIsLocalized || field.localized);
                            }
                        });
                    }
                    break;
                case 'checkbox':
                case 'code':
                case 'date':
                case 'email':
                case 'json':
                case 'number':
                case 'point':
                case 'radio':
                case 'relationship':
                case 'richText':
                case 'select':
                case 'text':
                case 'textarea':
                case 'upload':
                    if (field.name in toLocaleData && // only replace if the target value is null or undefined
                    [
                        null,
                        undefined
                    ].includes(toLocaleData[field.name]) && field.name in fromLocaleData) {
                        toLocaleData[field.name] = fromLocaleData[field.name];
                    }
                    break;
                case 'group':
                    {
                        if (fieldAffectsData(field) && field.name in toLocaleData && fromLocaleData?.[field.name] !== undefined) {
                            iterateFields(field.fields, fromLocaleData[field.name], toLocaleData[field.name], req, parentIsLocalized || field.localized);
                        } else {
                            iterateFields(field.fields, fromLocaleData, toLocaleData, req, parentIsLocalized);
                        }
                        break;
                    }
            }
        } else {
            switch(field.type){
                case 'collapsible':
                case 'row':
                    iterateFields(field.fields, fromLocaleData, toLocaleData, req, parentIsLocalized);
                    break;
                case 'tabs':
                    field.tabs.map((tab)=>{
                        if (tabHasName(tab)) {
                            if (tab.name in toLocaleData && fromLocaleData?.[tab.name] !== undefined) {
                                iterateFields(tab.fields, fromLocaleData[tab.name], toLocaleData[tab.name], req, parentIsLocalized);
                            }
                        } else {
                            iterateFields(tab.fields, fromLocaleData, toLocaleData, req, parentIsLocalized);
                        }
                    });
                    break;
            }
        }
    });
}
function mergeData(fromLocaleData, toLocaleData, fields, req, parentIsLocalized) {
    iterateFields(fields, fromLocaleData, toLocaleData, req, parentIsLocalized);
    return toLocaleData;
}
/**
 * We don't have to recursively remove all ids,
 * just the ones from the fields inside a localized array or block.
 */ function removeIdIfParentIsLocalized(data, fields) {
    traverseFields({
        callback: ({ parentIsLocalized, ref })=>{
            if (parentIsLocalized) {
                delete ref.id;
            }
        },
        fields,
        fillEmpty: false,
        ref: data
    });
    return data;
}
export const copyDataFromLocaleHandler = async (args)=>{
    const { req } = args;
    try {
        return await copyDataFromLocale(args);
    } catch (err) {
        req.payload.logger.error({
            err,
            msg: `There was an error copying data from "${args.fromLocale}" to "${args.toLocale}"`
        });
        // Re-throw the error so it can be caught by the client
        throw err;
    }
};
export const copyDataFromLocale = async (args)=>{
    const { collectionSlug, docID, fromLocale, globalSlug, overrideData = false, req: { payload, payload: { collections, globals }, user }, req, toLocale } = args;
    await canAccessAdmin({
        req
    });
    const [fromLocaleData, toLocaleData] = await Promise.allSettled([
        globalSlug ? payload.findGlobal({
            slug: globalSlug,
            depth: 0,
            locale: fromLocale,
            overrideAccess: false,
            user
        }) : // `select` would allow us to select only the fields we need in the future
        payload.findByID({
            id: docID,
            collection: collectionSlug,
            depth: 0,
            joins: false,
            locale: fromLocale,
            overrideAccess: false,
            user
        }),
        // `select` would allow us to select only the fields we need in the future
        globalSlug ? payload.findGlobal({
            slug: globalSlug,
            depth: 0,
            locale: toLocale,
            overrideAccess: false,
            user
        }) : // `select` would allow us to select only the fields we need in the future
        payload.findByID({
            id: docID,
            collection: collectionSlug,
            depth: 0,
            joins: false,
            locale: toLocale,
            overrideAccess: false,
            user
        })
    ]);
    // `select` would allow us to select only the fields we need in the future
    if (fromLocaleData.status === 'rejected') {
        throw new Error(`Error fetching data from locale "${fromLocale}"`);
    }
    if (toLocaleData.status === 'rejected') {
        throw new Error(`Error fetching data from locale "${toLocale}"`);
    }
    const fields = globalSlug ? globals[globalSlug].config.fields : collections[collectionSlug].config.fields;
    const fromLocaleDataWithoutID = fromLocaleData.value;
    const toLocaleDataWithoutID = toLocaleData.value;
    const dataWithID = overrideData ? fromLocaleDataWithoutID : mergeData(fromLocaleDataWithoutID, toLocaleDataWithoutID, fields, req, false);
    const data = removeIdIfParentIsLocalized(dataWithID, fields);
    return globalSlug ? await payload.updateGlobal({
        slug: globalSlug,
        data,
        locale: toLocale,
        overrideAccess: false,
        req,
        user
    }) : await payload.update({
        id: docID,
        collection: collectionSlug,
        data,
        locale: toLocale,
        overrideAccess: false,
        req,
        user
    });
};

//# sourceMappingURL=copyDataFromLocale.js.map