import config from 'config';
import { FHIR_REQUEST_INTENT, FHIR_REQUEST_PRIORITY, FHIR_REQUEST_STATUS, IMAGING_REQUEST_STATUS_TYPES, IMAGING_TABLE_STATUS_GROUPINGS, LAB_REQUEST_STATUSES, LAB_REQUEST_TABLE_STATUS_GROUPINGS, NOTE_TYPES } from '@tamanu/constants';
import { getNotesWithType } from '@tamanu/shared/utils/notes';
import { FhirAnnotation, FhirCodeableConcept, FhirCoding, FhirIdentifier, FhirReference } from '@tamanu/shared/services/fhirTypes';
import { Exception, formatFhirDate } from '@tamanu/shared/utils/fhir';
export async function getValues(upstream, models) {
    const { ImagingRequest, LabRequest } = models;
    if (upstream instanceof ImagingRequest) return getValuesFromImagingRequest(upstream, models);
    if (upstream instanceof LabRequest) return getValuesFromLabRequest(upstream, models);
    throw new Error(`Invalid upstream type for service request ${upstream.constructor.name}`);
}
async function getValuesFromImagingRequest(upstream, models) {
    const { ImagingAreaExternalCode } = models;
    const areaExtCodes = new Map((await ImagingAreaExternalCode.findAll({
        where: {
            areaId: upstream.areas.map((area)=>area.id)
        }
    })).map((ext)=>[
            ext.areaId,
            {
                code: ext.code,
                description: ext.description,
                updatedAt: ext.updatedAt
            }
        ]));
    const subject = await FhirReference.to(models.FhirPatient, upstream.encounter?.patient?.id, {
        display: `${upstream.encounter?.patient?.firstName} ${upstream.encounter?.patient?.lastName}`
    });
    const encounter = await FhirReference.to(models.FhirEncounter, upstream.encounter?.id);
    const requester = await FhirReference.to(models.FhirPractitioner, upstream.requestedBy?.id, {
        display: upstream.requestedBy?.displayName
    });
    return {
        lastUpdated: new Date(),
        identifier: [
            new FhirIdentifier({
                system: config.hl7.dataDictionaries.serviceRequestImagingId,
                value: upstream.id
            }),
            new FhirIdentifier({
                system: config.hl7.dataDictionaries.serviceRequestImagingDisplayId,
                value: upstream.displayId
            })
        ],
        status: statusFromImagingRequest(upstream),
        intent: FHIR_REQUEST_INTENT.ORDER._,
        category: [
            new FhirCodeableConcept({
                coding: [
                    new FhirCoding({
                        system: 'http://snomed.info/sct',
                        code: '363679005'
                    })
                ]
            })
        ],
        priority: validatePriority(upstream.priority),
        code: await imagingCode(upstream, models),
        orderDetail: upstream.areas.flatMap(({ id })=>areaExtCodes.has(id) ? [
                new FhirCodeableConcept({
                    text: areaExtCodes.get(id)?.description,
                    coding: [
                        new FhirCoding({
                            code: areaExtCodes.get(id)?.code,
                            system: config.hl7.dataDictionaries.areaExternalCode
                        })
                    ]
                })
            ] : []),
        subject,
        encounter,
        occurrenceDateTime: formatFhirDate(upstream.requestedDate),
        requester,
        locationCode: locationCode(upstream),
        note: imagingAnnotations(upstream),
        resolved: subject.isResolved() && encounter.isResolved() && requester.isResolved()
    };
}
async function getValuesFromLabRequest(upstream, models) {
    const subject = await FhirReference.to(models.FhirPatient, upstream.encounter?.patient?.id, {
        display: `${upstream.encounter?.patient?.firstName} ${upstream.encounter?.patient?.lastName}`
    });
    const encounter = await FhirReference.to(models.FhirEncounter, upstream.encounter?.id);
    const requester = await FhirReference.to(models.FhirPractitioner, upstream.requestedBy?.id, {
        display: upstream.requestedBy?.displayName
    });
    const specimen = await resolveSpecimen(upstream, models);
    return {
        lastUpdated: new Date(),
        identifier: [
            new FhirIdentifier({
                system: config.hl7.dataDictionaries.serviceRequestLabId,
                value: upstream.id
            }),
            new FhirIdentifier({
                system: config.hl7.dataDictionaries.serviceRequestLabDisplayId,
                value: upstream.displayId
            })
        ],
        status: statusFromLabRequest(upstream),
        intent: FHIR_REQUEST_INTENT.ORDER._,
        category: [
            new FhirCodeableConcept({
                coding: [
                    new FhirCoding({
                        system: 'http://snomed.info/sct',
                        code: '108252007'
                    })
                ]
            })
        ],
        priority: validatePriority(upstream.priority?.name),
        code: labCode(upstream),
        orderDetail: labOrderDetails(upstream),
        subject,
        encounter,
        occurrenceDateTime: formatFhirDate(upstream.requestedDate),
        requester,
        note: labAnnotations(upstream),
        specimen: specimen ? [
            specimen
        ] : null,
        resolved: subject.isResolved() && encounter.isResolved() && requester.isResolved() && (specimen ? specimen.isResolved() : true)
    };
}
function resolveSpecimen(upstream, models) {
    if (!upstream.specimenAttached) {
        return null;
    }
    return FhirReference.to(models.FhirSpecimen, upstream.id);
}
async function imagingCode(upstream, models) {
    const { ImagingTypeExternalCode } = models;
    const { imagingTypes } = config.localisation.data;
    if (!imagingTypes) throw new Exception('No imaging types specified in localisation.');
    const { imagingType: baseImagingCode } = upstream;
    const typeExtCode = await ImagingTypeExternalCode.findOne({
        where: {
            imagingTypeCode: baseImagingCode
        }
    });
    const code = typeExtCode?.code ?? baseImagingCode;
    const { label: baseLabel } = imagingTypes[baseImagingCode] || {};
    const label = typeExtCode?.description ?? baseLabel;
    if (!label) throw new Exception(`No label matching imaging type ${baseImagingCode} in localisation.`);
    return generateCodings(code, undefined, label, config.hl7.dataDictionaries.serviceRequestImagingTypeCodeSystem);
}
// Match the priority to a FHIR ServiceRequest priority where possible
// otherwise return null
// See: https://hl7.org/fhir/R4B/valueset-request-priority.html#expansion
function validatePriority(priority = '') {
    if (!Object.values(FHIR_REQUEST_PRIORITY).includes(priority)) {
        return null;
    }
    return priority;
}
function locationCode(upstream) {
    const facility = upstream.locationGroup?.facility ?? // most accurate
    upstream.location?.facility ?? // legacy data
    upstream.encounter?.location?.facility; // fallback to encounter
    if (!facility) return [];
    return [
        new FhirCodeableConcept({
            text: facility.name
        })
    ];
}
function statusFromImagingRequest(upstream) {
    return ({
        [IMAGING_REQUEST_STATUS_TYPES.CANCELLED]: FHIR_REQUEST_STATUS.REVOKED,
        [IMAGING_REQUEST_STATUS_TYPES.COMPLETED]: FHIR_REQUEST_STATUS.COMPLETED,
        [IMAGING_REQUEST_STATUS_TYPES.DELETED]: FHIR_REQUEST_STATUS.REVOKED,
        [IMAGING_REQUEST_STATUS_TYPES.ENTERED_IN_ERROR]: FHIR_REQUEST_STATUS.ENTERED_IN_ERROR,
        [IMAGING_REQUEST_STATUS_TYPES.IN_PROGRESS]: FHIR_REQUEST_STATUS.ACTIVE,
        [IMAGING_REQUEST_STATUS_TYPES.PENDING]: FHIR_REQUEST_STATUS.DRAFT
    })[upstream.status] ?? FHIR_REQUEST_STATUS.UNKNOWN;
}
function statusFromLabRequest(upstream) {
    switch(upstream.status){
        case LAB_REQUEST_STATUSES.SAMPLE_NOT_COLLECTED:
        case LAB_REQUEST_STATUSES.RECEPTION_PENDING:
            return FHIR_REQUEST_STATUS.DRAFT;
        case LAB_REQUEST_STATUSES.RESULTS_PENDING:
        case LAB_REQUEST_STATUSES.INTERIM_RESULTS:
        case LAB_REQUEST_STATUSES.TO_BE_VERIFIED:
        case LAB_REQUEST_STATUSES.VERIFIED:
            return FHIR_REQUEST_STATUS.ACTIVE;
        case LAB_REQUEST_STATUSES.PUBLISHED:
            return FHIR_REQUEST_STATUS.COMPLETED;
        case LAB_REQUEST_STATUSES.CANCELLED:
        case LAB_REQUEST_STATUSES.INVALIDATED:
        case LAB_REQUEST_STATUSES.DELETED:
            return FHIR_REQUEST_STATUS.REVOKED;
        case LAB_REQUEST_STATUSES.ENTERED_IN_ERROR:
            return FHIR_REQUEST_STATUS.ENTERED_IN_ERROR;
        default:
            return FHIR_REQUEST_STATUS.UNKNOWN;
    }
}
export function getIsLive(upstream, models) {
    const { ImagingRequest, LabRequest } = models;
    if (upstream instanceof ImagingRequest) return IMAGING_TABLE_STATUS_GROUPINGS.ACTIVE.includes(upstream.status);
    if (upstream instanceof LabRequest) return LAB_REQUEST_TABLE_STATUS_GROUPINGS.ACTIVE.includes(upstream.status);
    throw new Error(`Invalid upstream type for service request ${upstream.constructor.name}`);
}
export function shouldForceRematerialise(upstream, downstream, models) {
    const { ImagingRequest, LabRequest } = models;
    // Force remateralisation on status change
    if (upstream instanceof ImagingRequest) {
        return statusFromImagingRequest(upstream) !== downstream.status;
    }
    if (upstream instanceof LabRequest) {
        return statusFromLabRequest(upstream) !== downstream.status;
    }
    throw new Error(`Invalid upstream type for service request ${upstream.constructor.name}`);
}
function labCode(upstream) {
    const { labTestPanelRequest } = upstream;
    // ServiceRequests may not have a panel
    if (!labTestPanelRequest) {
        return null;
    }
    const { externalCode, name, code } = labTestPanelRequest.labTestPanel || {};
    return generateCodings(code, externalCode, name, config.hl7.dataDictionaries.serviceRequestLabPanelCodeSystem, config.hl7.dataDictionaries.serviceRequestLabPanelExternalCodeSystem);
}
function labOrderDetails({ tests }) {
    if (tests.length) {
        return tests.map(({ labTestType })=>{
            if (!labTestType) throw new Exception('Received a null test');
            const { externalCode, code, name } = labTestType;
            return generateCodings(code, externalCode, name, config.hl7.dataDictionaries.serviceRequestLabTestCodeSystem, config.hl7.dataDictionaries.serviceRequestLabTestExternalCodeSystem);
        });
    }
    return [];
}
function labAnnotations(upstream) {
    return upstream.notes.map((note)=>{
        return new FhirAnnotation({
            time: formatFhirDate(note.date),
            text: note.content
        });
    });
}
function imagingAnnotations(upstream) {
    // See EPI-451: imaging requests can embed notes about the area to image
    return getNotesWithType(upstream.notes, NOTE_TYPES.OTHER).map((note)=>new FhirAnnotation({
            time: formatFhirDate(note.date),
            text: note.content
        }));
}
function generateCodings(code, externalCode, name, codeSystem, externalCodeSystem) {
    const coding = [];
    if (code) {
        coding.push(new FhirCoding({
            system: codeSystem,
            code,
            display: name
        }));
    }
    // Sometimes externalCode will not exists but if it does include it
    if (externalCode) {
        coding.push(new FhirCoding({
            system: externalCodeSystem,
            code: externalCode,
            display: name
        }));
    }
    if (coding.length > 0) {
        return new FhirCodeableConcept({
            coding,
            text: name
        });
    }
    return null;
}

//# sourceMappingURL=getValues.js.map