import { DataTypes, Op } from 'sequelize';
import { NOTIFICATION_TYPES, SYNC_DIRECTIONS, ADMINISTRATION_STATUS, INVOICE_ITEMS_CATEGORIES, INVOICEABLE_MEDICATION_ENCOUNTER_TYPES } from '@tamanu/constants';
import { getCurrentDateTimeString } from '@tamanu/utils/dateTime';
import { Model } from './Model';
import { dateTimeType } from '../types/model';
import { EncounterPrescription } from './EncounterPrescription';
import { buildEncounterLinkedLookupSelect } from '../sync/buildEncounterLinkedLookupFilter';
export class Prescription extends Model {
    static initModel({ primaryKey, ...options }, models) {
        super.init({
            id: primaryKey,
            isOngoing: DataTypes.BOOLEAN,
            isPrn: DataTypes.BOOLEAN,
            isVariableDose: DataTypes.BOOLEAN,
            doseAmount: DataTypes.DECIMAL,
            units: {
                type: DataTypes.STRING,
                allowNull: false
            },
            frequency: {
                type: DataTypes.STRING,
                allowNull: false
            },
            idealTimes: DataTypes.ARRAY(DataTypes.STRING),
            route: {
                type: DataTypes.STRING,
                allowNull: false
            },
            date: dateTimeType('date', {
                allowNull: false,
                defaultValue: getCurrentDateTimeString
            }),
            startDate: dateTimeType('startDate', {
                allowNull: false,
                defaultValue: getCurrentDateTimeString
            }),
            endDate: dateTimeType('endDate'),
            durationValue: DataTypes.DECIMAL,
            durationUnit: DataTypes.STRING,
            indication: DataTypes.STRING,
            isPhoneOrder: DataTypes.BOOLEAN,
            notes: DataTypes.STRING,
            pharmacyNotes: DataTypes.STRING,
            displayPharmacyNotesInMar: DataTypes.BOOLEAN,
            quantity: DataTypes.INTEGER,
            discontinued: DataTypes.BOOLEAN,
            discontinuedDate: DataTypes.STRING,
            discontinuingReason: DataTypes.STRING,
            repeats: DataTypes.INTEGER
        }, {
            ...options,
            syncDirection: SYNC_DIRECTIONS.BIDIRECTIONAL,
            hooks: {
                afterCreate: async (prescription)=>{
                    if (prescription.durationValue && prescription.durationUnit) {
                        const { add } = await import('date-fns');
                        prescription.endDate = add(new Date(prescription.startDate), {
                            [prescription.durationUnit]: prescription.durationValue
                        }).toISOString();
                    }
                    await prescription.save();
                },
                afterUpdate: async (prescription, options)=>{
                    if (prescription.changed('pharmacyNotes')) {
                        await models.Notification.pushNotification(NOTIFICATION_TYPES.PHARMACY_NOTE, prescription.dataValues, {
                            transaction: options.transaction
                        });
                    }
                    if (prescription.changed('discontinued') && prescription.discontinued) {
                        await models.MedicationAdministrationRecord.removeInvalidMedicationAdministrationRecords(options.transaction);
                    }
                }
            }
        });
    }
    static initRelations(models) {
        this.belongsTo(models.User, {
            foreignKey: 'prescriberId',
            as: 'prescriber'
        });
        this.belongsTo(models.User, {
            foreignKey: 'discontinuingClinicianId',
            as: 'discontinuingClinician'
        });
        this.hasOne(models.EncounterPrescription, {
            foreignKey: 'prescriptionId',
            as: 'encounterPrescription'
        });
        this.belongsToMany(models.Encounter, {
            through: models.EncounterPrescription,
            foreignKey: 'prescriptionId',
            as: 'encounters'
        });
        this.hasOne(models.PatientOngoingPrescription, {
            foreignKey: 'prescriptionId',
            as: 'patientOngoingPrescription'
        });
        this.belongsToMany(models.Patient, {
            through: models.PatientOngoingPrescription,
            foreignKey: 'prescriptionId',
            as: 'patients'
        });
        this.belongsTo(models.ReferenceData, {
            foreignKey: 'medicationId',
            as: 'medication'
        });
        this.hasMany(models.MedicationAdministrationRecord, {
            foreignKey: 'prescriptionId',
            as: 'medicationAdministrationRecords'
        });
        this.hasOne(models.InvoiceItem, {
            foreignKey: 'sourceRecordId',
            as: 'invoiceItem',
            constraints: false,
            scope: {
                source_record_type: this.name
            }
        });
    }
    static getListReferenceAssociations() {
        return [
            'medication',
            'prescriber',
            'discontinuingClinician'
        ];
    }
    static buildPatientSyncFilter(patientCount, markedForSyncPatientsTable) {
        if (patientCount === 0) {
            return null;
        }
        return `
      LEFT JOIN encounter_prescriptions ON prescriptions.id = encounter_prescriptions.prescription_id
      LEFT JOIN encounters ON encounter_prescriptions.encounter_id = encounters.id
      LEFT JOIN patient_ongoing_prescriptions ON prescriptions.id = patient_ongoing_prescriptions.prescription_id
      WHERE (
        (encounters.patient_id IS NOT NULL AND encounters.patient_id IN (SELECT patient_id FROM ${markedForSyncPatientsTable}))
        OR
        (patient_ongoing_prescriptions.patient_id IS NOT NULL AND patient_ongoing_prescriptions.patient_id IN (SELECT patient_id FROM ${markedForSyncPatientsTable}))
      )
      AND (
        prescriptions.updated_at_sync_tick > :since
        OR patient_ongoing_prescriptions.updated_at_sync_tick > :since
      )
    `;
    }
    static async buildSyncLookupQueryDetails() {
        return {
            select: await buildEncounterLinkedLookupSelect(this, {
                patientId: 'COALESCE(encounters.patient_id, patient_ongoing_prescriptions.patient_id)'
            }),
            joins: `
        LEFT JOIN encounter_prescriptions ON prescriptions.id = encounter_prescriptions.prescription_id
        LEFT JOIN encounters ON encounter_prescriptions.encounter_id = encounters.id
        LEFT JOIN patient_ongoing_prescriptions ON prescriptions.id = patient_ongoing_prescriptions.prescription_id
        LEFT JOIN locations ON encounters.location_id = locations.id
        LEFT JOIN facilities ON locations.facility_id = facilities.id
      `,
            where: `
        prescriptions.updated_at_sync_tick > :since
        OR patient_ongoing_prescriptions.updated_at_sync_tick > :since
      `
        };
    }
    async recalculateAndApplyInvoiceQuantity(userId) {
        const { Encounter, InvoiceProduct, Invoice, MedicationAdministrationRecord, MedicationAdministrationRecordDose, PharmacyOrderPrescription, PharmacyOrder } = this.sequelize.models;
        const prescription = this;
        const encounter = prescription.encounterPrescription?.encounter;
        if (!encounter) return;
        const invoiceProduct = await InvoiceProduct.findOne({
            where: {
                category: INVOICE_ITEMS_CATEGORIES.DRUG,
                sourceRecordId: prescription.medicationId
            }
        });
        if (!invoiceProduct) return;
        const pharmacyOrderPrescriptions = await PharmacyOrderPrescription.findAll({
            where: {
                prescriptionId: prescription.id
            },
            include: [
                {
                    model: PharmacyOrder,
                    as: 'pharmacyOrder',
                    attributes: [
                        'date',
                        'orderingClinicianId',
                        'isDischargePrescription'
                    ],
                    where: {
                        isDischargePrescription: true
                    },
                    required: true
                }
            ]
        });
        const hasPharmacy = pharmacyOrderPrescriptions.length > 0;
        const earliestPharmacyDate = hasPharmacy ? pharmacyOrderPrescriptions.map((p)=>p.pharmacyOrder.date).sort((a, b)=>a.localeCompare(b))[0] : undefined;
        const totalSentQty = pharmacyOrderPrescriptions.reduce((sum, p)=>sum + (Number(p.quantity) || 0), 0);
        let marQty = 0;
        const givenMars = await MedicationAdministrationRecord.findAll({
            where: {
                prescriptionId: prescription.id,
                status: ADMINISTRATION_STATUS.GIVEN
            },
            attributes: [
                'id'
            ],
            include: [
                {
                    model: Prescription,
                    as: 'prescription',
                    required: true,
                    include: [
                        {
                            model: EncounterPrescription,
                            as: 'encounterPrescription',
                            required: true,
                            include: [
                                {
                                    model: Encounter,
                                    as: 'encounter',
                                    required: true,
                                    where: {
                                        encounterType: {
                                            [Op.in]: INVOICEABLE_MEDICATION_ENCOUNTER_TYPES
                                        }
                                    }
                                }
                            ]
                        }
                    ]
                }
            ]
        });
        if (givenMars.length > 0) {
            const marIds = givenMars.map((m)=>m.id);
            const doses = await MedicationAdministrationRecordDose.findAll({
                where: {
                    marId: {
                        [Op.in]: marIds
                    },
                    isRemoved: {
                        [Op.or]: [
                            false,
                            null
                        ]
                    },
                    ...earliestPharmacyDate ? {
                        givenTime: {
                            [Op.lte]: earliestPharmacyDate
                        }
                    } : {}
                },
                attributes: [
                    'doseAmount'
                ]
            });
            marQty = doses.reduce((sum, d)=>sum + Number(d.doseAmount || 0), 0);
        }
        // Consolidate all administered + dispensed quantities into a single invoice item (update quantity instead of creating duplicates)
        const finalQty = marQty + totalSentQty;
        if (finalQty > 0) {
            await Invoice.addItemToInvoice(prescription, encounter.id, invoiceProduct, userId || pharmacyOrderPrescriptions[0]?.pharmacyOrder?.orderingClinicianId, {
                quantity: finalQty
            });
        } else {
            await Invoice.removeItemFromInvoice(prescription, encounter.id);
        }
    }
}

//# sourceMappingURL=Prescription.js.map