"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const http_status_1 = __importDefault(require("http-status"));
const sanitizeInternalFields_1 = __importDefault(require("../../utilities/sanitizeInternalFields"));
const executeAccess_1 = __importDefault(require("../../auth/executeAccess"));
const errors_1 = require("../../errors");
const types_1 = require("../../auth/types");
const saveCollectionDraft_1 = require("../../versions/drafts/saveCollectionDraft");
const saveCollectionVersion_1 = require("../../versions/saveCollectionVersion");
const uploadFile_1 = __importDefault(require("../../uploads/uploadFile"));
const cleanUpFailedVersion_1 = __importDefault(require("../../versions/cleanUpFailedVersion"));
const ensurePublishedCollectionVersion_1 = require("../../versions/ensurePublishedCollectionVersion");
const beforeChange_1 = require("../../fields/hooks/beforeChange");
const beforeValidate_1 = require("../../fields/hooks/beforeValidate");
const afterChange_1 = require("../../fields/hooks/afterChange");
const afterRead_1 = require("../../fields/hooks/afterRead");
async function update(incomingArgs) {
    let args = incomingArgs;
    // /////////////////////////////////////
    // beforeOperation - Collection
    // /////////////////////////////////////
    await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
        await priorHook;
        args = (await hook({
            args,
            operation: 'update',
        })) || args;
    }, Promise.resolve());
    const { depth, collection, collection: { Model, config: collectionConfig, }, id, req, req: { locale, payload, payload: { config, }, }, overrideAccess, showHiddenFields, overwriteExistingFiles = false, draft: draftArg = false, autosave = false, } = args;
    let { data } = args;
    if (!id) {
        throw new errors_1.APIError('Missing ID of document to update.', http_status_1.default.BAD_REQUEST);
    }
    const shouldSaveDraft = Boolean(draftArg && collectionConfig.versions.drafts);
    // /////////////////////////////////////
    // Access
    // /////////////////////////////////////
    const accessResults = !overrideAccess ? await (0, executeAccess_1.default)({ req, id, data }, collectionConfig.access.update) : true;
    const hasWherePolicy = (0, types_1.hasWhereAccessResult)(accessResults);
    // /////////////////////////////////////
    // Retrieve document
    // /////////////////////////////////////
    const queryToBuild = {
        where: {
            and: [
                {
                    id: {
                        equals: id,
                    },
                },
            ],
        },
    };
    if ((0, types_1.hasWhereAccessResult)(accessResults)) {
        queryToBuild.where.and.push(accessResults);
    }
    const query = await Model.buildQuery(queryToBuild, locale);
    const doc = await Model.findOne(query);
    if (!doc && !hasWherePolicy)
        throw new errors_1.NotFound();
    if (!doc && hasWherePolicy)
        throw new errors_1.Forbidden();
    let docWithLocales = doc.toJSON({ virtuals: true });
    docWithLocales = JSON.stringify(docWithLocales);
    docWithLocales = JSON.parse(docWithLocales);
    const originalDoc = await (0, afterRead_1.afterRead)({
        depth: 0,
        doc: docWithLocales,
        entityConfig: collectionConfig,
        req,
        overrideAccess: true,
        showHiddenFields,
    });
    // /////////////////////////////////////
    // Upload and resize potential files
    // /////////////////////////////////////
    data = await (0, uploadFile_1.default)({
        config,
        collection,
        req,
        data,
        throwOnMissingFile: false,
        overwriteExistingFiles,
    });
    // /////////////////////////////////////
    // beforeValidate - Fields
    // /////////////////////////////////////
    data = await (0, beforeValidate_1.beforeValidate)({
        data,
        doc: originalDoc,
        entityConfig: collectionConfig,
        id,
        operation: 'update',
        overrideAccess,
        req,
    });
    // // /////////////////////////////////////
    // // beforeValidate - Collection
    // // /////////////////////////////////////
    await collectionConfig.hooks.beforeValidate.reduce(async (priorHook, hook) => {
        await priorHook;
        data = (await hook({
            data,
            req,
            operation: 'update',
            originalDoc,
        })) || data;
    }, Promise.resolve());
    // /////////////////////////////////////
    // beforeChange - Collection
    // /////////////////////////////////////
    await collectionConfig.hooks.beforeChange.reduce(async (priorHook, hook) => {
        await priorHook;
        data = (await hook({
            data,
            req,
            originalDoc,
            operation: 'update',
        })) || data;
    }, Promise.resolve());
    // /////////////////////////////////////
    // beforeChange - Fields
    // /////////////////////////////////////
    let result = await (0, beforeChange_1.beforeChange)({
        data,
        doc: originalDoc,
        docWithLocales,
        entityConfig: collectionConfig,
        id,
        operation: 'update',
        req,
        skipValidation: shouldSaveDraft,
    });
    // /////////////////////////////////////
    // Handle potential password update
    // /////////////////////////////////////
    const { password } = data;
    if (password && collectionConfig.auth && !shouldSaveDraft) {
        await doc.setPassword(password);
        await doc.save();
        delete data.password;
        delete result.password;
    }
    // /////////////////////////////////////
    // Create version from existing doc
    // /////////////////////////////////////
    let createdVersion;
    if (collectionConfig.versions && !shouldSaveDraft) {
        createdVersion = await (0, saveCollectionVersion_1.saveCollectionVersion)({
            payload,
            config: collectionConfig,
            req,
            docWithLocales,
            id,
        });
    }
    // /////////////////////////////////////
    // Update
    // /////////////////////////////////////
    if (shouldSaveDraft) {
        await (0, ensurePublishedCollectionVersion_1.ensurePublishedCollectionVersion)({
            payload,
            config: collectionConfig,
            req,
            docWithLocales,
            id,
        });
        result = await (0, saveCollectionDraft_1.saveCollectionDraft)({
            payload,
            config: collectionConfig,
            req,
            data: result,
            id,
            autosave,
        });
    }
    else {
        try {
            result = await Model.findByIdAndUpdate({ _id: id }, result, { new: true });
        }
        catch (error) {
            (0, cleanUpFailedVersion_1.default)({
                payload,
                entityConfig: collectionConfig,
                version: createdVersion,
            });
            // Handle uniqueness error from MongoDB
            throw error.code === 11000
                ? new errors_1.ValidationError([{ message: 'Value must be unique', field: Object.keys(error.keyValue)[0] }])
                : error;
        }
        const resultString = JSON.stringify(result);
        result = JSON.parse(resultString);
        // custom id type reset
        result.id = result._id;
    }
    result = (0, sanitizeInternalFields_1.default)(result);
    // /////////////////////////////////////
    // afterRead - Fields
    // /////////////////////////////////////
    result = await (0, afterRead_1.afterRead)({
        depth,
        doc: result,
        entityConfig: collectionConfig,
        req,
        overrideAccess,
        showHiddenFields,
    });
    // /////////////////////////////////////
    // afterRead - Collection
    // /////////////////////////////////////
    await collectionConfig.hooks.afterRead.reduce(async (priorHook, hook) => {
        await priorHook;
        result = await hook({
            req,
            doc: result,
        }) || result;
    }, Promise.resolve());
    // /////////////////////////////////////
    // afterChange - Fields
    // /////////////////////////////////////
    result = await (0, afterChange_1.afterChange)({
        data,
        doc: result,
        entityConfig: collectionConfig,
        operation: 'update',
        req,
    });
    // /////////////////////////////////////
    // afterChange - Collection
    // /////////////////////////////////////
    await collectionConfig.hooks.afterChange.reduce(async (priorHook, hook) => {
        await priorHook;
        result = await hook({
            doc: result,
            req,
            operation: 'update',
        }) || result;
    }, Promise.resolve());
    // /////////////////////////////////////
    // Return results
    // /////////////////////////////////////
    return result;
}
exports.default = update;
//# sourceMappingURL=data:application/json;base64, 