"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "FhirImagingStudy", {
    enumerable: true,
    get: function() {
        return FhirImagingStudy;
    }
});
const _config = /*#__PURE__*/ _interop_require_default(require("config"));
const _sequelize = require("sequelize");
const _yup = /*#__PURE__*/ _interop_require_wildcard(require("yup"));
const _constants = require("@tamanu/constants");
const _fhirTypes = require("@tamanu/shared/services/fhirTypes");
const _fhir = require("@tamanu/shared/utils/fhir");
const _dateTime = require("@tamanu/utils/dateTime");
const _Resource = require("./Resource");
const _fhirEndpoint = require("./fhirEndpoint");
function _define_property(obj, key, value) {
    if (key in obj) {
        Object.defineProperty(obj, key, {
            value: value,
            enumerable: true,
            configurable: true,
            writable: true
        });
    } else {
        obj[key] = value;
    }
    return obj;
}
function _interop_require_default(obj) {
    return obj && obj.__esModule ? obj : {
        default: obj
    };
}
function _getRequireWildcardCache(nodeInterop) {
    if (typeof WeakMap !== "function") return null;
    var cacheBabelInterop = new WeakMap();
    var cacheNodeInterop = new WeakMap();
    return (_getRequireWildcardCache = function(nodeInterop) {
        return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
    })(nodeInterop);
}
function _interop_require_wildcard(obj, nodeInterop) {
    if (!nodeInterop && obj && obj.__esModule) {
        return obj;
    }
    if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
        return {
            default: obj
        };
    }
    var cache = _getRequireWildcardCache(nodeInterop);
    if (cache && cache.has(obj)) {
        return cache.get(obj);
    }
    var newObj = {
        __proto__: null
    };
    var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
    for(var key in obj){
        if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
            var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
            if (desc && (desc.get || desc.set)) {
                Object.defineProperty(newObj, key, desc);
            } else {
                newObj[key] = obj[key];
            }
        }
    }
    newObj.default = obj;
    if (cache) {
        cache.set(obj, newObj);
    }
    return newObj;
}
let FhirImagingStudy = class FhirImagingStudy extends _Resource.FhirResource {
    static initModel(options, models) {
        super.initResource({
            identifier: _sequelize.DataTypes.JSONB,
            basedOn: _sequelize.DataTypes.JSONB,
            started: _sequelize.DataTypes.TEXT,
            status: {
                type: _sequelize.DataTypes.TEXT,
                allowNull: false
            },
            note: _sequelize.DataTypes.JSONB,
            contained: _sequelize.DataTypes.JSONB
        }, options);
        // it's not materialised yet. TBD in EPI-224
        this.UpstreamModels = [
            models.ImagingResult
        ];
    }
    static get INTAKE_SCHEMA() {
        return _yup.object({
            identifier: _yup.array().of(_fhirTypes.FhirIdentifier.asYup()).optional(),
            basedOn: _yup.array().of(_fhirTypes.FhirReference.asYup()),
            started: _yup.string().optional(),
            status: _yup.string().required(),
            note: _yup.array().of(_fhirTypes.FhirAnnotation.asYup()),
            contained: _yup.array().of(_fhirEndpoint.FHIR_ENDPOINT_SCHEMA).min(1).max(1).optional()
        });
    }
    // This is currently very hardcoded for Aspen's use case.
    // We'll need to make it more generic at some point, but not today!
    async pushUpstream({ settings, requesterId }) {
        const { FhirServiceRequest, ImagingRequest } = this.sequelize.models;
        const serviceRequestFhirId = this.basedOn?.map((ref)=>ref.fhirTypeAndId()).filter(Boolean).find(({ type })=>type === 'ServiceRequest')?.id;
        const serviceRequestId = this.basedOn?.find((b)=>b?.type === 'ServiceRequest' && b?.identifier?.system === _config.default.hl7.dataDictionaries.serviceRequestImagingId)?.identifier.value;
        const serviceRequestDisplayId = this.basedOn?.find((b)=>b?.type === 'ServiceRequest' && b?.identifier?.system === _config.default.hl7.dataDictionaries.serviceRequestImagingDisplayId)?.identifier.value;
        let upstreamRequest;
        if (serviceRequestId) {
            upstreamRequest = await ImagingRequest.findByPk(serviceRequestId);
        } else if (serviceRequestDisplayId) {
            upstreamRequest = await ImagingRequest.findOne({
                where: {
                    displayId: serviceRequestDisplayId
                }
            });
        }
        let serviceRequest;
        if (upstreamRequest) {
            // serviceRequest will always be searched if the upstream record is found
            serviceRequest = await FhirServiceRequest.findOne({
                where: {
                    upstreamId: upstreamRequest.id
                }
            });
        } else if (serviceRequestFhirId) {
            serviceRequest = await FhirServiceRequest.findByPk(serviceRequestFhirId);
        }
        if (!serviceRequest) {
            const failedId = serviceRequestFhirId || serviceRequestId || serviceRequestDisplayId;
            if (failedId) {
                throw new _fhir.Invalid(`ServiceRequest ${failedId} does not exist in Tamanu`, {
                    code: _constants.FHIR_ISSUE_TYPE.INVALID.VALUE
                });
            }
            throw new _fhir.Invalid('Need to have basedOn field that includes a Tamanu identifier', {
                code: _constants.FHIR_ISSUE_TYPE.INVALID.STRUCTURE
            });
        }
        if (![
            _constants.FHIR_IMAGING_STUDY_STATUS.AVAILABLE,
            _constants.FHIR_IMAGING_STUDY_STATUS.FINAL_INVALID_LEGACY,
            _constants.FHIR_IMAGING_STUDY_STATUS.CANCELLED
        ].includes(this.status)) {
            throw new _fhir.Invalid(`ImagingStudy status must be either '${_constants.FHIR_IMAGING_STUDY_STATUS.AVAILABLE}' or '${_constants.FHIR_IMAGING_STUDY_STATUS.CANCELLED}'`, {
                code: _constants.FHIR_ISSUE_TYPE.INVALID.VALUE
            });
        }
        const imagingRequest = await ImagingRequest.findByPk(serviceRequest.upstreamId);
        if (!imagingRequest || imagingRequest.status === _constants.IMAGING_REQUEST_STATUS_TYPES.DELETED) {
            // this is only a possibility when using a FHIR basedOn reference
            throw new _fhir.Deleted('ImagingRequest has been deleted');
        }
        if ([
            _constants.IMAGING_REQUEST_STATUS_TYPES.CANCELLED,
            _constants.IMAGING_REQUEST_STATUS_TYPES.ENTERED_IN_ERROR
        ].includes(imagingRequest.status)) {
            throw new _fhir.Invalid('ImagingRequest has been cancelled');
        }
        if ([
            _constants.FHIR_IMAGING_STUDY_STATUS.AVAILABLE,
            _constants.FHIR_IMAGING_STUDY_STATUS.FINAL_INVALID_LEGACY
        ].includes(this.status)) {
            return await this.attachResults(imagingRequest);
        }
        if (this.status === _constants.FHIR_IMAGING_STUDY_STATUS.CANCELLED) {
            await this.cancelRequest(imagingRequest, requesterId, settings);
        }
    }
    async cancelRequest(imagingRequest, requesterId, settings) {
        const reasons = await settings.get('imagingCancellationReasons');
        const cancelledReason = reasons.find((reason)=>reason.value === 'cancelled-externally')?.label;
        imagingRequest.set({
            status: _constants.IMAGING_REQUEST_STATUS_TYPES.CANCELLED,
            reasonForCancellation: cancelledReason
        });
        await this.sequelize.transaction(async ()=>{
            await imagingRequest.createNote({
                noteTypeId: _constants.NOTE_TYPES.OTHER,
                content: `Request cancelled. Reason: ${cancelledReason}.`,
                authorId: requesterId
            });
            await imagingRequest.save();
        });
    }
    async attachResults(imagingRequest) {
        const imagingAccessCode = this.identifier?.find((i)=>i?.system === _config.default.hl7.dataDictionaries.imagingStudyAccessionId)?.value;
        const resultImageUrl = this.contained?.[0]?.address;
        if (!imagingAccessCode && !resultImageUrl) {
            throw new _fhir.Invalid('Need to have Accession Number identifier or contained Endpoint', {
                code: _constants.FHIR_ISSUE_TYPE.INVALID.STRUCTURE
            });
        }
        const { ImagingResult } = this.sequelize.models;
        const whereClause = {
            imagingRequestId: imagingRequest.id
        };
        if (imagingAccessCode) {
            whereClause.externalCode = imagingAccessCode;
        }
        let result = await ImagingResult.findOne({
            where: whereClause
        });
        const resultNotes = this.note?.map((n)=>n.text).join('\n\n');
        if (result) {
            result.set({
                description: resultNotes,
                resultImageUrl
            });
            await result.save();
        } else {
            result = await ImagingResult.create({
                imagingRequestId: imagingRequest.id,
                description: resultNotes,
                externalCode: imagingAccessCode,
                resultImageUrl,
                completedAt: this.started ? (0, _dateTime.toDateTimeString)(this.started) : (0, _dateTime.getCurrentDateTimeString)()
            });
        }
        imagingRequest.set({
            status: _constants.IMAGING_REQUEST_STATUS_TYPES.COMPLETED
        });
        await imagingRequest.save();
        return result;
    }
};
_define_property(FhirImagingStudy, "CAN_DO", new Set([
    _constants.FHIR_INTERACTIONS.TYPE.CREATE
]));

//# sourceMappingURL=FhirImagingStudy.js.map