/* eslint-disable no-unused-vars */ "use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "Model", {
    enumerable: true,
    get: function() {
        return Model;
    }
});
const _sequelize = require("sequelize");
const _constants = require("@tamanu/constants");
const _beforeDestroyHooks = require("../utils/beforeDestroyHooks");
const firstLetterLowercase = (s)=>(s[0] || '').toLowerCase() + s.slice(1);
let Model = class Model extends _sequelize.Model {
    static init(modelAttributes, { syncDirection, timestamps = true, schema, ...options }) {
        // this is used in our database init code to make it easier to create models,
        // but shouldn't be passed down to sequelize. instead of forcing every model
        // to erase it even if they don't use it, we delete it here
        if ('primaryKey' in options) {
            delete options.primaryKey;
        }
        const attributes = {
            ...modelAttributes
        };
        const usesPublicSchema = schema === undefined || schema === 'public';
        if (syncDirection !== _constants.SYNC_DIRECTIONS.DO_NOT_SYNC) {
            attributes.updatedAtSyncTick = _sequelize.DataTypes.BIGINT;
        }
        super.init(attributes, {
            timestamps,
            schema,
            ...options,
            hooks: {
                ...options.hooks,
                ...usesPublicSchema && {
                    beforeDestroy: _beforeDestroyHooks.genericBeforeDestroy,
                    beforeBulkDestroy: _beforeDestroyHooks.genericBeforeBulkDestroy
                }
            }
        });
        this.defaultIdValue = typeof attributes.id === 'object' && 'defaultValue' in attributes.id ? attributes.id.defaultValue : undefined;
        if (!syncDirection) {
            throw new Error(`Every model must specify a sync direction, even if that is "DO_NOT_SYNC". Check the model definition for ${this.name}`);
        }
        this.syncDirection = syncDirection;
        this.validateSync(timestamps);
        this.usesPublicSchema = usesPublicSchema;
        return this;
    }
    static generateId() {
        return _sequelize.Utils.toDefaultValue(this.defaultIdValue);
    }
    /**
   * Generates a uuid via the database
   */ static async generateDbUuid() {
        const result = await this.sequelize.query(`SELECT gen_random_uuid();`);
        return result[0][0].gen_random_uuid;
    }
    static validateSync(timestamps) {
        // every syncing model should have timestamps turned on
        if (!timestamps && this.syncDirection !== _constants.SYNC_DIRECTIONS.DO_NOT_SYNC) {
            throw new Error('DEV: syncing models should all have createdAt, updatedAt, deletedAt, and updatedAtSyncTick timestamps turned on');
        }
        // every model that syncs from central to facilities (i.e. PULL_FROM_CENTRAL or BIDRIRECTIONAL
        // sync direction) must implement either buildSyncFilter or buildPatientSyncFilter, to make sure
        // it is considered
        // models that sync all records to all facilities (i.e. don't need a sync filter) should
        // implement buildSyncFilter by returning null
        if ([
            _constants.SYNC_DIRECTIONS.BIDIRECTIONAL,
            _constants.SYNC_DIRECTIONS.PULL_FROM_CENTRAL
        ].includes(this.syncDirection) && !this.buildSyncFilter && !this.buildPatientSyncFilter) {
            throw new Error(`DEV: ${this.name} syncs from central to facility, and must implement either buildSyncFilter or buildPatientSyncFilter. If it syncs everywhere, simply implement buildSyncFilter and return null.`);
        }
    }
    forResponse() {
        // Reassign reference associations to use camelCase & dataValues.
        // That is, it turns
        // { id: 12345, field: 'value', ReferenceObject: [model instance] }
        // into
        // { id: 12345, field: 'value', referenceObject: { id: 23456, name: 'object' } }
        const models = this.sequelize.models;
        const values = Object.entries(this.dataValues).filter(([, val])=>val !== null).reduce((obj, [key, val])=>({
                ...obj,
                [key]: val
            }), {});
        const references = this.constructor.getListReferenceAssociations(models);
        if (!references) return values;
        // Note that we don't call forResponse on the nested object, this is under the assumption that
        // if the structure of a nested object differs significantly from its database representation,
        // it's probably more correct to implement that as a separate endpoint rather than putting the
        // logic here.
        return references.reduce((allValues, referenceName)=>{
            const { [referenceName]: referenceVal, ...otherValues } = allValues;
            if (!referenceVal) return allValues;
            return {
                ...otherValues,
                [firstLetterLowercase(referenceName)]: referenceVal.dataValues
            };
        }, values);
    }
    toJSON() {
        return this.forResponse();
    }
    getModelName() {
        return this.constructor.name;
    }
    static getListReferenceAssociations(_models) {
        // List of relations to include when fetching this model
        // as part of a list (eg to display in a table)
        //
        // This will get used in an options object passed to a sequelize
        // query, so returning 'undefined' by default here just leaves that key
        // empty (which is the desired behaviour).
        return undefined;
    }
    static getFullReferenceAssociations() {
        // List of relations when fetching just this model
        // (eg to display in a detailed view)
        const { models } = this.sequelize;
        return this.getListReferenceAssociations(models);
    }
    static async findByIds(ids, paranoid = true) {
        if (ids.length === 0) return [];
        return this.findAll({
            where: {
                id: {
                    [_sequelize.Op.in]: ids
                }
            },
            paranoid
        });
    }
    static sanitizeForCentralServer(values) {
        // implement on the specific model if needed
        return values;
    }
    static sanitizeForFacilityServer(values) {
        // implement on the specific model if needed
        return values;
    }
};

//# sourceMappingURL=Model.js.map