"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "patientProgramRegistration", {
    enumerable: true,
    get: function() {
        return patientProgramRegistration;
    }
});
const _express = /*#__PURE__*/ _interop_require_default(require("express"));
const _expressasynchandler = /*#__PURE__*/ _interop_require_default(require("express-async-handler"));
const _ability = require("@casl/ability");
const _errors = require("@tamanu/errors");
const _constants = require("@tamanu/constants");
const _sequelize = require("sequelize");
const _dateTime = require("@tamanu/utils/dateTime");
const _utils = require("./utils");
function _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
const patientProgramRegistration = _express.default.Router();
patientProgramRegistration.get('/:patientId/programRegistration', (0, _expressasynchandler.default)(async (req, res)=>{
    const { params, models } = req;
    req.checkPermission('read', 'Patient');
    req.checkPermission('read', 'PatientProgramRegistration');
    req.checkPermission('list', 'PatientProgramRegistration');
    const registrationData = await models.PatientProgramRegistration.getRegistrationsForPatient(params.patientId);
    const filteredData = registrationData.filter((x)=>req.ability.can('read', x.programRegistry));
    res.send({
        data: filteredData
    });
}));
patientProgramRegistration.post('/:patientId/programRegistration', (0, _expressasynchandler.default)(async (req, res)=>{
    const { db, models, params, body } = req;
    const { patientId } = params;
    const { programRegistryId, registeringFacilityId } = body;
    await (0, _utils.validatePatientProgramRegistrationRequest)(req, patientId, programRegistryId);
    req.checkPermission('create', 'PatientProgramRegistration');
    const { conditions = [], ...registrationData } = body;
    if (conditions.length > 0) {
        req.checkPermission('create', 'PatientProgramRegistrationCondition');
    }
    // Run in a transaction so it either fails or succeeds together
    const [registration, conditionsRecords] = await db.transaction(async (transaction)=>{
        let registrationRecord;
        // Check if this PPR has been previously deleted
        const existingRecordedInErrorRegistration = await models.PatientProgramRegistration.findOne({
            where: {
                programRegistryId,
                patientId,
                registrationStatus: _constants.REGISTRATION_STATUSES.RECORDED_IN_ERROR
            },
            transaction,
            paranoid: false
        });
        // If the registration was previously recorded in error, update that record to preserve the unique id. Otherwise, create a new one.
        if (existingRecordedInErrorRegistration) {
            registrationRecord = await existingRecordedInErrorRegistration.update({
                deletedAt: null,
                clinicalStatusId: null,
                deactivatedDate: null,
                deactivatedClinicianId: null,
                ...registrationData
            }, {
                transaction
            });
        } else {
            registrationRecord = await models.PatientProgramRegistration.create({
                patientId,
                programRegistryId,
                ...registrationData
            }, {
                transaction
            });
        }
        const newConditions = await models.PatientProgramRegistrationCondition.bulkCreate(conditions.filter((condition)=>condition.conditionId).map((condition)=>({
                patientProgramRegistrationId: registrationRecord.id,
                clinicianId: registrationData.clinicianId,
                date: registrationData.date,
                programRegistryConditionId: condition.conditionId,
                programRegistryConditionCategoryId: condition.conditionCategoryId
            })), {
            transaction
        });
        await models.PatientFacility.upsert({
            patientId,
            facilityId: registeringFacilityId
        }, {
            transaction
        });
        return [
            registrationRecord,
            newConditions
        ];
    });
    // Convert Sequelize model to use a custom object as response
    const responseObject = {
        ...registration.get({
            plain: true
        }),
        conditions: conditionsRecords
    };
    res.send(responseObject);
}));
patientProgramRegistration.put('/programRegistration/:id', (0, _expressasynchandler.default)(async (req, res)=>{
    req.checkPermission('write', 'PatientProgramRegistration');
    const { db, models, params, body } = req;
    const { id } = params;
    const { conditions = [], ...registrationData } = body;
    const { PatientProgramRegistration } = models;
    if (conditions.length > 0) {
        req.checkPermission('create', 'PatientProgramRegistrationCondition');
    }
    const existingRegistration = await PatientProgramRegistration.findOne({
        where: {
            id
        }
    });
    if (!existingRegistration) {
        throw new _errors.NotFoundError('PatientProgramRegistration not found');
    }
    const existingConditionIds = conditions.map(({ id: conditionId })=>conditionId).filter(Boolean);
    const existingConditions = existingConditionIds.length > 0 ? await models.PatientProgramRegistrationCondition.findAll({
        where: {
            id: {
                [_sequelize.Op.in]: existingConditionIds
            },
            patientProgramRegistrationId: existingRegistration.id
        }
    }) : [];
    const existingConditionMap = existingConditions.reduce((acc, condition)=>{
        acc[condition.id] = condition;
        return acc;
    }, {});
    const conditionsData = conditions.flatMap((condition)=>{
        const existingCondition = condition.id ? existingConditionMap[condition.id] : null;
        const hasLegacyNewConditionShape = Boolean(condition.id) && !condition.conditionId;
        // Existing row update: only persist if the category actually changed.
        if (existingCondition) {
            if (existingCondition.programRegistryConditionCategoryId === condition.conditionCategoryId) {
                return [];
            }
            return [
                {
                    id: condition.id,
                    patientProgramRegistrationId: existingRegistration.id,
                    clinicianId: registrationData.clinicianId,
                    date: condition.date,
                    programRegistryConditionId: condition.conditionId || existingCondition.programRegistryConditionId,
                    programRegistryConditionCategoryId: condition.conditionCategoryId,
                    reasonForChange: condition.reasonForChange
                }
            ];
        }
        // New rows can come in either modern shape (conditionId) or legacy shape (id).
        const programRegistryConditionId = hasLegacyNewConditionShape ? condition.id : condition.conditionId;
        if (!programRegistryConditionId || !condition.conditionCategoryId) {
            return [];
        }
        return [
            {
                patientProgramRegistrationId: existingRegistration.id,
                clinicianId: registrationData.clinicianId,
                date: condition.date,
                programRegistryConditionId,
                programRegistryConditionCategoryId: condition.conditionCategoryId,
                reasonForChange: condition.reasonForChange
            }
        ];
    });
    const updatedRegistrationData = {
        ...registrationData,
        deactivatedDate: null,
        deactivatedClinicianId: null
    };
    // If the registration status is being changed to INACTIVE, set the deactivated fields
    if (registrationData.registrationStatus === _constants.REGISTRATION_STATUSES.INACTIVE) {
        updatedRegistrationData.deactivatedDate = registrationData.deactivatedDate || (0, _dateTime.getCurrentDateTimeString)();
        updatedRegistrationData.deactivatedClinicianId = registrationData.clinicianId;
    }
    const [registration] = await db.transaction(async ()=>{
        const updates = [
            existingRegistration.update(updatedRegistrationData)
        ];
        if (conditionsData.length > 0) {
            updates.push(models.PatientProgramRegistrationCondition.bulkCreate(conditionsData, {
                updateOnDuplicate: [
                    'date',
                    'programRegistryConditionCategoryId',
                    'reasonForChange'
                ]
            }));
        }
        return Promise.all(updates);
    });
    const responseObject = {
        ...registration.get({
            plain: true
        })
    };
    res.send(responseObject);
}));
patientProgramRegistration.delete('/programRegistration/:id', (0, _expressasynchandler.default)(async (req, res)=>{
    const { db, models, params } = req;
    const { id } = params;
    const { PatientProgramRegistration, PatientProgramRegistrationCondition } = models;
    req.checkPermission('delete', 'PatientProgramRegistration');
    const existingRegistration = await PatientProgramRegistration.findOne({
        where: {
            id
        }
    });
    if (!existingRegistration) {
        throw new _errors.NotFoundError('PatientProgramRegistration not found');
    }
    await db.transaction(async (transaction)=>{
        // Update the status to recordedInError
        await existingRegistration.update({
            registrationStatus: _constants.REGISTRATION_STATUSES.RECORDED_IN_ERROR
        }, {
            transaction
        });
        // Soft delete all related conditions
        await PatientProgramRegistrationCondition.destroy({
            where: {
                patientProgramRegistrationId: id
            },
            transaction
        });
    });
    res.status(200).send({
        message: 'Registration successfully deleted'
    });
}));
patientProgramRegistration.get('/:patientId/programRegistration/:programRegistryId', (0, _expressasynchandler.default)(async (req, res)=>{
    const { models, params, query: { facilityId } } = req;
    const { patientId, programRegistryId } = params;
    const { PatientProgramRegistration } = models;
    req.checkPermission('read', (0, _ability.subject)('ProgramRegistry', {
        id: programRegistryId
    }));
    req.checkPermission('read', 'PatientProgramRegistration');
    const registration = await PatientProgramRegistration.findOne({
        where: {
            patientId,
            programRegistryId
        },
        include: PatientProgramRegistration.getFullReferenceAssociations(),
        raw: true,
        nest: true
    });
    if (!registration) {
        throw new _errors.NotFoundError();
    }
    await req.audit.access({
        recordId: registration.id,
        frontEndContext: params,
        model: PatientProgramRegistration,
        facilityId
    });
    res.send({
        ...registration,
        registrationDate: registration.date,
        registrationClinician: registration.clinician,
        dateRemoved: registration.deactivatedDate,
        removedBy: registration.deactivatedClinician
    });
}));
patientProgramRegistration.get('/:patientId/programRegistration/:programRegistryId/history', (0, _expressasynchandler.default)(async (req, res)=>{
    const { models, params } = req;
    const { patientId, programRegistryId } = params;
    const { ChangeLog, User, PatientProgramRegistration, ProgramRegistryClinicalStatus } = models;
    req.checkPermission('read', (0, _ability.subject)('ProgramRegistry', {
        id: programRegistryId
    }));
    req.checkPermission('list', 'PatientProgramRegistration');
    const registration = await PatientProgramRegistration.findOne({
        where: {
            patientId,
            programRegistryId
        }
    });
    if (!registration) {
        res.send({
            count: 0,
            data: []
        });
    }
    const changes = await ChangeLog.findAll({
        where: {
            tableName: 'patient_program_registrations',
            recordId: registration.id,
            migrationContext: null
        },
        include: [
            {
                model: User,
                as: 'updatedByUser',
                attributes: [
                    'id',
                    'displayName'
                ]
            }
        ],
        order: [
            [
                'loggedAt',
                'DESC'
            ]
        ]
    });
    // Get all unique clinical status IDs from the changes
    const clinicalStatusIds = [
        ...new Set(changes.map((change)=>change.recordData.clinical_status_id).filter(Boolean))
    ];
    // Fetch all clinical statuses in one query
    const clinicalStatuses = await ProgramRegistryClinicalStatus.findAll({
        where: {
            id: {
                [_sequelize.Op.in]: clinicalStatusIds
            }
        },
        attributes: [
            'id',
            'name',
            'color'
        ]
    });
    // Create a map for quick lookup
    const clinicalStatusMap = clinicalStatuses.reduce((acc, status)=>{
        acc[status.id] = status;
        return acc;
    }, {});
    const history = changes.map((change)=>{
        const data = change.recordData;
        return {
            id: change.id,
            date: change.loggedAt,
            registrationStatus: data.registration_status,
            clinicalStatusId: data.clinical_status_id,
            clinicalStatus: data.clinical_status_id ? clinicalStatusMap[data.clinical_status_id] : null,
            clinician: change.updatedByUser,
            registrationDate: data.date
        };
    }).filter((change)=>change.registrationStatus !== _constants.REGISTRATION_STATUSES.INACTIVE)// Add this filter to remove entries with unchanged clinical status
    .filter((change, index, array)=>{
        if (index === array.length - 1) return true; // Always keep the original record
        const nextChange = array[index + 1];
        return change.clinicalStatusId !== nextChange.clinicalStatusId;
    });
    res.send({
        count: history.length,
        data: history
    });
}));

//# sourceMappingURL=patientProgramRegistration.js.map