"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: Object.getOwnPropertyDescriptor(all, name).get
    });
}
_export(exports, {
    get createDataImporterEndpoint () {
        return createDataImporterEndpoint;
    },
    get importerTransaction () {
        return importerTransaction;
    },
    get normaliseSheetName () {
        return normaliseSheetName;
    }
});
const _config = /*#__PURE__*/ _interop_require_default(require("config"));
const _expressasynchandler = /*#__PURE__*/ _interop_require_default(require("express-async-handler"));
const _fs = require("fs");
const _inflection = require("inflection");
const _lodash = require("lodash");
const _sequelize = require("sequelize");
const _constants = require("@tamanu/constants");
const _facts = require("@tamanu/constants/facts");
const _getUploadedData = require("@tamanu/shared/utils/getUploadedData");
const _log = require("@tamanu/shared/services/logging/log");
const _errors = require("../errors");
const _stats = require("../stats");
function _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
const normMapping = {
    // singularize transforms 'reference data' to 'reference datum', which is not what we want
    referenceDatumRelation: 'referenceDataRelation',
    vaccineSchedule: _constants.OTHER_REFERENCE_TYPES.SCHEDULED_VACCINE,
    procedure: 'procedureType',
    // This is needed to handle the way we are exporting that data
    patientFieldDefCategory: _constants.OTHER_REFERENCE_TYPES.PATIENT_FIELD_DEFINITION_CATEGORY,
    invoiceInsuranceItem: _constants.OTHER_REFERENCE_TYPES.INVOICE_INSURANCE_PLAN_ITEM,
    // We need mapping for program registry imports because program registry data is imported in the
    // worksheet sheets called registry and registryCondition but the full model names
    // are ProgramRegistry and ProgramRegistryCondition which are used everywhere else.
    // ProgramRegistryClinicalStatus is imported in the registry sheet so it doesn't need a mapping here
    registry: _constants.PROGRAM_REFERENCE_TYPES.PROGRAM_REGISTRY,
    registryCondition: _constants.PROGRAM_REFERENCE_TYPES.PROGRAM_REGISTRY_CONDITION,
    registryConditionCategory: _constants.PROGRAM_REFERENCE_TYPES.PROGRAM_REGISTRY_CONDITION_CATEGORY
};
function normaliseSheetName(name, modelName) {
    const norm = (0, _lodash.camelCase)((0, _lodash.lowerCase)(name).split(/\s+/).map((word)=>(0, _inflection.singularize)(word)).join(' '));
    // Exceptions where the sheet name for the program/survey/etc is not consistent with the model name
    if (modelName === 'ProgramRegistryClinicalStatus') {
        return _constants.PROGRAM_REFERENCE_TYPES.PROGRAM_REGISTRY_CLINICAL_STATUS;
    }
    if (modelName === 'ProgramDataElement') {
        return _constants.PROGRAM_REFERENCE_TYPES.PROGRAM_DATA_ELEMENT;
    }
    if (modelName === 'SurveyScreenComponent') {
        return _constants.PROGRAM_REFERENCE_TYPES.SURVEY_SCREEN_COMPONENT;
    }
    if (modelName === 'Program') {
        return _constants.PROGRAM_REFERENCE_TYPES.PROGRAM;
    }
    if (modelName === 'Survey') {
        return _constants.PROGRAM_REFERENCE_TYPES.SURVEY;
    }
    return normMapping[norm] || norm;
}
async function importerTransaction({ importer, models, file, dryRun = false, includedDataTypes = [], checkPermission, ...extraOptions }) {
    const errors = [];
    const stats = [];
    try {
        _log.log.debug('Starting transaction');
        await models.ReferenceData.sequelize.transaction({
            // strongest level to be sure to read/write good data
            isolationLevel: _sequelize.Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE
        }, async (transaction)=>{
            // acquire a lock on the sync time row in the local system facts table, so that all imported
            // changes have the same updated_at_sync_tick, and no sync pull snapshot can start while this
            // import is still in progress
            // the pull snapshot starts by updating the current time, so this locks that out while the
            // import transaction happens, to avoid the snapshot missing records that get saved during
            // this import, but aren't visible in the db to be snapshot until the transaction commits,
            // so would otherwise be completely skipped over by that sync client
            await models.LocalSystemFact.findAll({
                where: {
                    key: _facts.FACT_CURRENT_SYNC_TICK
                },
                lock: transaction.LOCK.UPDATE
            });
            try {
                await importer({
                    errors,
                    models,
                    stats,
                    file,
                    includedDataTypes,
                    checkPermission,
                    ...extraOptions
                });
            } catch (err) {
                errors.push(err);
            }
            if (errors.length > 0) throw new Error('rollback on errors');
            if (dryRun) throw new _errors.DryRun(); // roll back the transaction
        });
        _log.log.debug('Ended transaction');
        return {
            errors: [],
            stats: (0, _stats.coalesceStats)(stats)
        };
    } catch (err) {
        _log.log.error(`while importing refdata: ${err.stack}`);
        if (dryRun && err instanceof _errors.DryRun) {
            return {
                didntSendReason: 'dryRun',
                errors: [],
                stats: (0, _stats.coalesceStats)(stats)
            };
        }
        if (errors.length) {
            return {
                didntSendReason: 'validationFailed',
                errors,
                stats: (0, _stats.coalesceStats)(stats)
            };
        }
        return {
            didntSendReason: 'validationFailed',
            errors: [
                err
            ],
            stats: (0, _stats.coalesceStats)(stats)
        };
    }
}
function createDataImporterEndpoint(importer) {
    return (0, _expressasynchandler.default)(async (req, res)=>{
        const start = Date.now();
        const { store, checkPermission } = req;
        // read uploaded data
        const { file, deleteFileAfterImport = true, dryRun = false, includedDataTypes, ...extraOptions } = await (0, _getUploadedData.getUploadedData)(req);
        const result = await importerTransaction({
            importer,
            file,
            models: store.models,
            dryRun,
            includedDataTypes,
            checkPermission,
            ...extraOptions
        });
        // we don't need the file any more
        if (deleteFileAfterImport) {
            // eslint-disable-next-line no-unused-vars
            await _fs.promises.unlink(file).catch((ignore)=>{});
        }
        result.errors = result.errors?.map((err)=>(err instanceof Error || typeof err === 'string') && !(err instanceof _errors.DataImportError) ? new _errors.DataImportError('(general)', -3, err) : err) ?? [];
        res.send({
            ...result,
            duration: (Date.now() - start) / 1000.0,
            serverInfo: {
                host: _config.default.canonicalHostName
            }
        });
    });
}

//# sourceMappingURL=importerEndpoint.js.map