#!/usr/bin/env node

'use strict';

/**
 * Allergies RPC Prototype - focus on create/update/(delete)
 *
 * Note: RPC testing to prepare for VDM
 * ... using RPCs used by CPRS
 * ... and seeing how brittle they all are.
 *
 * See: http://vistadataproject.info/artifacts/cprsRPCs
 *
 * Immediate TODO/rework - move to using rpcSessionUtils and rpc definition form and off
 * custom form/from vdmTestAllergies used here. ie/ shadow and build on rpcSessions.
 *
 * Note: like others, RPC level for allergies is more like MVDM
 * without redundancy (ingredients/classes with gmr_allergies)
 *
 * Create/Update/Delete (as in EIE):
 * - ORWDAL32 SAVE ALLERGY (EDITSAVE^ORWDAL32) is multi-purpose (new, EIE, NKA)
 * - examine ORWDAL32 SEND BULLETIN (feeds into Eventing too)
 *
 * Read:
 * - ORQQAL DETAIL (DETAIL^ORQQAL) - JLV still uses it)
 * - ORQQAL LIST (REPORT) (LIST^ORQQAL)
 * - ORWDAL32 LOAD FOR EDIT is a READ for EDIT which returns a more structured form.
 * - and DEF, SITE PARAMS, SYMPTOMS, CLINUSER
 * ... not doing the similar GMVALLERGY etc (ie/ lot's of redundant RPCs)
 *
 * And ORWDAL32 SITE PARAMS for parameters
 *
 * ie/ to finish, must execute all RPCs invoked in CPRS in:
 * - CPRS-Chart/Orders/rODAllergy.pas [ORWDAL32]
 * - CPRS/CPRS-Chart/rCover.pas [ORQQAL]
 * See: http://vistadataproject.info/artifacts/cprsRPCs
 *
 * Out of scope: a lot of "meta" get RPCs to construct GUIs looking
 * at contents of 120.82 or getting top ten allergies from 120.84. Will
 * need to ensure VDM Get allows all such gets.
 *
 * Note: VPR "RPC" is in own vprAllergies-spec.js. VPR is a service, not an RPC
 * and deserves its own -spec.
 *
 * TODO: biggest is portable lookups for allergins etc by name so these will work in any VISTA
 *
 * (c) 2016-2017 VISTA Data Project
 */

/*
 * Basic setup
 */
const util = require('util');
const fs = require('fs');
const nodem = require('nodem');
const os = require('os');
const _ = require('lodash');
const moment = require('moment');

const fileman = require('../fileman');
const RPCFacade = require('../rpcFacade');
const ClinicalRPCEmulator = require('../cRPCEmulator');

const vdmUtils = require('../vdmUtils');
const VDM = require('../vdm'); // using VDM read to test results
const allergyModel = require('./vdmAllergiesModel').vdmModel;
const documentModel = require('../documents/vdmDocumentsModel').vdmModel;
const visitModel = require('../visits/vdmVisitsModel').vdmModel;
const testUtils = require('../testUtils');
const allergyUtils = require('./allergyUtils');

const vdmModel = allergyModel.concat(documentModel, visitModel);

const mvdmModel = [].concat(
    // allergies
    require('./mvdmAllergiesModel').mvdmModel,
    require('../documents/mvdmDocumentsModel').mvdmModel,
    require('../visits/mvdmVisitsModel').mvdmModel);

const rpcEmulatorModel = [].concat(
    require('./rpcEmulatorAllergiesModel').rpcEmulatorModel);

/* ************************** End Convenience Functions ******************************/

// want to split OBS and HIST as well as EIE and NKA with describe inside describe
describe('testAllergyRPCs', () => {
    let db; // for afterAll

    let rpcFacade;

    let DUZ;
    let facilityIEN;

    let lastAllergyIEN;
    let lastHistoricalIEN;
    let lastOREDITED; // for mimic load for edit
    let patient1IEN; // id of patient 1
    let lastRPCDefn;
    let record;

    let emulatingOn;

    beforeAll(() => {
        db = new nodem.Gtm();
        db.open();

       /*
        * User id (DUZ)
        * - issue that test allergy provider/patient ids may not match a particular nodeVISTA (osehraVISTA)
        * ... will need to establish baseline patient/providers ie/ 200-1 == X,Y etc. so that can be sure
        * of change arounds with ids.
        *
        * Also DUZ is often default provider in RPCs (ie/ not the passed in value) => want to try DUZ different
        * from provider/user in some cases but not others. Need to be definitive.
        *
        * TODO: after definitive 200/2 ids/names in nodeVISTA "base", be explicit about DUZ in every IT below and
        * don't set globally.
        */
        const userId = testUtils.lookupUserIdByName(db, 'ALEXANDER,ROBERT');
        DUZ = parseInt(userId.split('-')[1], 10);

        const rpcEmulator = new ClinicalRPCEmulator(db, {
            vdmModel,
            mvdmModel,
            rpcEmulatorModel,
        });

        rpcFacade = new RPCFacade(db, rpcEmulator);
        rpcFacade.initializeUser(DUZ);

        if (process.env.RUNNER === 'rpcEmulator') {
            emulatingOn = true;
        } else {
            emulatingOn = false;
        }
       // emulatingOn = false; // TMP during testings
        rpcFacade.setEmulating(emulatingOn);

        VDM.setDBAndModel(db, vdmModel); // for some describes (don't care about userId

        allergyUtils.purgeAllAllergies(db); // want to start with no allergy info in there (includes TIU).

        const uNf = rpcFacade.getUserAndFacility(); // facility gets set from user
    });


   /*
    * Mandatory beyond FM mandatory (FM enforces reactant but not gmr_allergy)
    * ... shows first allergy leads to an assessment exists entry!
    */
    it('Create Historical Class (50.416) Allergy with all required arguments and gmr_allergy', () => {
        patient1IEN = parseInt(testUtils.lookupPatientIdByName(db, 'CARTER,DAVID').split('-')[1], 10);
        const now = moment().format('YYYY-MM-DDTHH:mm:ss');

        let rpcDefn = {

            name: 'ORWDAL32 SAVE ALLERGY',

            args: [

                '',
                patient1IEN,
                {
                    GMRAGNT: 'PENICILLINS AND BETA-LACTAM ANTIMICROBIALS^11;PS(50.605,',
                    GMRATYPE: 'D^Drug',
                    GMRANATR: 'U^Unknown',
                    GMRAORIG: DUZ,
                    GMRAORDT: vdmUtils.toFMDateTime(now),
                    GMRACHT: [
                        vdmUtils.toFMDateTime(now),
                    ],
                    GMRAOBHX: 'h',
                },
            ],
        };
        lastRPCDefn = rpcDefn; // for use below

        let res = rpcFacade.run(rpcDefn.name, rpcDefn.args);
        expect(res.result).toBeDefined();
        expect(res.result).toEqual(0);

       // for class allergy - expect "classes" property but no "ingredients" property
        const expectedIEN = fileman.nextIEN(db, '120.8') - 1; // not ideal - better if passed back but not (passes TIU IEN back in other cases!)
        const descr = VDM.describe(`120_8-${expectedIEN}`);
        expect(descr.drug_classes).toBeDefined();
        expect(descr.ingredients).toBeUndefined();
        expect(parseInt(descr.patient.id.split('-')[1], 10)).toEqual(patient1IEN);

        const expectedAllergyAssessmentIENAfter = fileman.nextIEN(db, '120.86'); // expect patientId + 1 as dinumed'
        expect(expectedAllergyAssessmentIENAfter).toEqual(patient1IEN + 1);
       // could go on and check 'reaction_assessment' True

       // ORQQAL DETAIL - CPRS (rCover.pas) ... simple get on 120.8 + 120.85
       // Pass in PatientIEN, AllergyIEN
       // ... JLV uses for details print out. NB: shows severity from Allergy Report
       /*
         Causative agent: PENICILLINS AND BETA-LACTAM ANTIMICROBIALS
         Nature of Reaction: Unknown

         Drug Classes: PENICILLINS AND BETA-LACTAM ANTIMICROBIALS

         Originator: ALEXANDER,ROBERT <----- may be different name in your nodeVISTA as no definitive patient/provider setup yet
         Originated: Feb 18, 2016@17:32
         Verified: <auto-verified>
         Observed/Historical: Historical
       */
        rpcDefn = {
            name: 'ORQQAL DETAIL',
            args: [
                patient1IEN,
                expectedIEN,
                '2',
            ], // second argument 2 doesn't matter
        };
        res = rpcFacade.run(rpcDefn.name, rpcDefn.args);
        expect(res.result).toBeDefined();
        expect(res.result.length).toEqual(9);
       // Note: from one VISTA version to another this varied (AM114 wasn't in one). So may need to template!
        expect(res.result).toContain('    Causative agent: AM114 [PENICILLINS AND BETA-LACTAM ANTIMICROBIALS]');
    });

   /*
    * Duplicate - EMULATING and Native both stop
    *
    * ... TODO: more $$DUPCHK^GMRAOR0 for reactions (msg: Patient already has a ...)
    */
    it('Try to recreate Historical Allergy - will be stopped - duplicate (results blend with errors)', () => {
        const res = rpcFacade.run(lastRPCDefn.name, lastRPCDefn.args);

        expect(res.result).toBeDefined();

       // One rare time RPC returns a 'proper' error - TODO: must upgrade emulator to match it explicitly
        if (!emulatingOn) {
           // result: '-1^Patient already has a ... ie/ this is a RPC special return msg with its own error codes
            expect(res.result).toEqual('-1^Patient already has a PENICILLINS AND BETA-LACTAM ANTIMICROBIALS reaction entered.  No duplicates allowed.');
        } else {
            expect(res.result).toMatch(/Error \d+/); // will be refining errors
        }
    });


   /*
    * NATIVE: works as long as 'patient' and 'reaction' - otherwise won't work; EMULATING adds semantic checks
    *
    * Note: if no patient - DUPCHK+15^GMRAOR0,%GTM-E-NULSUBSC, Null subscripts are not allowed for region: DEFAULT,%GTM-I-GVIS, \t\tGlobal variable: ^GMR(120.8,"B","",0)
    * Note: /UPDATE\+22\^GMRAGUI1,\%GTM-E-UNDEF, Undefined local variable: IEN\(1\)/ is the error in native if leave out LABEL from reactant ie/ leave out first
    * part of GMRAGNT. Doesn't cause a problem for emulate as it ignores label and only uses ID part.
    *
    * Two properties default: verified = True if H, False if O or if O/H property is missing + "originator_sign_off" is ALWAYS true!
    * even without an originator. ie/ verified and originator_sign_off have default values.
    *
    * vs Prod Clone: mandatory list is much greater including Allergy Type. ex/ AINA - all observable allergies have one!
    * ex/ of prod clone mandatory being more (also allergy-type must be explicit as NOT defaulted)
    *
    */
    it('(Try to) Create Historical reactant and patient-only (Nonsense) Allergy - Native allows, Emulating stops', () => {
       // Quirk of RPC that empty value ("") is NOT the same as no assertion. Get exception if these aren't set.
        const rpcDefn = {

            name: 'ORWDAL32 SAVE ALLERGY',

            args: [

                '',
                patient1IEN,
                {
                    GMRAGNT: 'NONSENSE^',
                    GMRANATR: '',
                    GMRAOBHX: '',
                    GMRAORDT: '',
                    GMRAORIG: '',
                    GMRARDT: '',
                    GMRASEVR: '',
                    GMRATYPE: '',
                },
            ],
        };

        const res = rpcFacade.run(rpcDefn.name, rpcDefn.args);

        if (emulatingOn) {
            expect(res.result).toBeDefined();
           // expect(res.result).toMatch(/the following required properties are missing - allergyType, mechanism, observedOrHistorical/);
            expect(res.result).toMatch(/Error \d+ - allergyType, mechanism, observedOrHistorical/);
            return;
        }

        expect(res.result).toBeDefined();
        expect(res.result).toEqual(0);

        const expectedIEN = fileman.nextIEN(db, '120.8') - 1; // not ideal - better if passed back but not (passes TIU IEN back in other cases!)
        const descr = VDM.describe(`120_8-${expectedIEN}`);
        expect(_.keys(descr).length).toEqual(6); // id, type and patient, reactant, originator_sign_off, verified
    });

   /*
    * Here keep all properties but make but make all but 'patient' pointers invalid. Again EMULATING stops but NATIVE permits
    *
    * Bonus:
    * - set the pseudo enum/string 'allergy_type' to nonsense: still shows in CPRS (but not in VPR which has expectations!)
    * - if set enums (ex/ observed-historical) to a bad value then get a crash!
    */
    it('(Try to) create Historical (Bad Reference) Allergy with bad pointers and codes - native allows/emulator stops', () => {
        const now = moment().format('YYYY-MM-DDTHH:mm:ss');
        const rpcDefn = {

            name: 'ORWDAL32 SAVE ALLERGY',

            args: [

                '',
                patient1IEN,
                {

                    GMRAGNT: `BADREFS^${fileman.nextIEN(db, '50.6')};PSNDF(50.6,`, // invalid gmr allergy and reactant
                    GMRATYPE: 'BAAAAAAAAAAD',
                    GMRANATR: 'U',
                    GMRAORIG: fileman.nextIEN(db, '200'), // invalid DUZ (pointer)
                    GMRAORDT: vdmUtils.toFMDateTime(now),
                    GMRACHT: [
                        vdmUtils.toFMDateTime(now),
                    ],
                    GMRAOBHX: 'h', // if set to "MOREBAAAAAD" then get function+14^v4wNode,%GTM-E-UNDEF, Undefined local variable: undefined

                },
            ],
        };

        let res;

        if (emulatingOn) {
            res = rpcFacade.run(rpcDefn.name, rpcDefn.args);
            expect(res.result).toBeDefined();
           // expect(res.result).toMatch(/the following required properties are missing - allergyType/); // strange - doesn't seem to catch both!
            expect(res.result).toMatch(/Error 11 - allergyType/);
            return;
        }

        const nextIEN = fileman.nextIEN(db, '120.8');
        res = rpcFacade.run(rpcDefn.name, rpcDefn.args);
        expect(res.result).toBeDefined();
        expect(res.result).toEqual(0);

        const descr = VDM.describe(`120_8-${nextIEN}`);
        expect(descr.reactant).toEqual('BADREFS');
        expect(descr.allergy_type).toEqual('BAAAAAAAAAAD');
    });

   /*
    * Create Allergy valid 50_416 (PENICILLIN) allergy with gmr_allergy - changing name too to bypass dup REACTANT check
    * Shows multiple ways to express the same GMR Allergy - Ingredients (50.416) and Ingredient (50.6) have the same
    * VUID but "classes" is only auto-assigned to 50.6 ... the triggered code, "CLIST^PSNAPIS" is only triggered for
    * PSNDF refs (50.68, 50.6)
    * ... 50.416 seems to be for Drug-Drug interaction declarations.
    * ... NOTE: can't check 50 yet as not filled in (yet) in nodeVISTA
    */
    it('Create Historical Ingredients (50.416) Allergy that connotates the same type as Ingredient (50.6) Allergy above', () => {
        const now = moment().format('YYYY-MM-DDTHH:mm:ss');
        const rpcDefn = {

            name: 'ORWDAL32 SAVE ALLERGY',

            args: [

                '',
                patient1IEN,
                {

                    GMRAGNT: 'PENICILLIN^2505;PS(50.416,', // vuid:4019880 same as VUID of 50.6-16 but unlike it, won't get class assigned
                    GMRATYPE: 'D',
                    GMRANATR: 'U',
                    GMRAORIG: DUZ,
                    GMRAORDT: vdmUtils.toFMDateTime(now),
                    GMRACHT: [
                        vdmUtils.toFMDateTime(now),
                    ],
                    GMRAOBHX: 'h',

                },
            ],
        };

        const res = rpcFacade.run(rpcDefn.name, rpcDefn.args);
        expect(res.result).toBeDefined();
        expect(res.result).toEqual(0);
    });

   /*
    * NATIVE allows - EMULATING STOPS
    *
    * Test if copy of an ingredient allergin to drug ingredients is dumb: ie/ if ingredient allergin is invalid, is
    * the copy still made - yep!
    */
    it('(Try to) Create Historical Allergy with invalid ingredient gmr_allergy copied to ingredients - native allows, emulating stops', () => {
        const now = moment().format('YYYY-MM-DDTHH:mm:ss');
        const rpcDefn = {

            name: 'ORWDAL32 SAVE ALLERGY',

            args: [

                '',
                patient1IEN,
                {

                    GMRAGNT: `${'ACETAMINOPHENone^'}${fileman.nextIEN(db, '50.416')};PS(50.416,`, // bad reactant, bad IEN for 50.416
                    GMRATYPE: 'D',
                    GMRANATR: 'P',
                    GMRAORIG: DUZ,
                    GMRAORDT: vdmUtils.toFMDateTime(now),
                    GMRACHT: [
                        vdmUtils.toFMDateTime(now),
                    ],
                    GMRAOBHX: 'h',

                },
            ],
        };

        const res = rpcFacade.run(rpcDefn.name, rpcDefn.args);

        if (emulatingOn) {
            expect(res.result).toBeDefined();
           // expect(res.result).toMatch(/the following properties have invalid values - reactant/);
            expect(res.result).toMatch(/Error \d+ - reactant/);
            return;
        }

        expect(res.result).toBeDefined();
        expect(res.result).toEqual(0);
    });

   // 120.82 Allergy - D Ascorbic Acid - 120_82-53, a D term with many ingredients (50.416) and classes
    it("Create Historical 120.8 D Allergy whose definition has many ingredients and classes - and a comment (not doc'ed)", () => {
        const now = moment().format('YYYY-MM-DDTHH:mm:ss');
        const comment = 'an historical gets a comment';
        const rpcDefn = {

            name: 'ORWDAL32 SAVE ALLERGY',

            args: [

                '',
                patient1IEN,
                {

                    GMRAGNT: 'ASCORBIC ACID^53;GMRD(120.82,',
                    GMRATYPE: 'D',
                    GMRANATR: 'P',
                    GMRAORIG: DUZ,
                    GMRAORDT: vdmUtils.toFMDateTime(now),
                    GMRACHT: [
                        vdmUtils.toFMDateTime(now),
                    ],
                    GMRAOBHX: 'h',
                    GMRACMTS: [
                        comment,
                    ],
                },
            ],
        };

        const res = rpcFacade.run(rpcDefn.name, rpcDefn.args);

       // No side effect doc created even though there IS a comment. Contrast with Observed.
        expect(res.result).toEqual(0);

        const expectedIEN = fileman.nextIEN(db, '120.8') - 1; // not ideal - better if passed back but not (passes TIU IEN back in other cases!)
        const descr = VDM.describe(`120_8-${expectedIEN}`);

        expect(descr.drug_ingredients).toBeDefined();
        expect(descr.drug_ingredients.length).toEqual(4);
        expect(descr.drug_classes).toBeDefined();
        expect(descr.drug_classes.length).toEqual(5);

       // TODO: not toEqual as extra newline put in by RPC
        expect(descr.comments[0].comments).toMatch(comment);
    });

   /*
    * the RPC also takes 50_67 == NDC/UPN and turns it to 50.6. So 50.67 is accepted there but no in FileMan!
    * CHANGE HISTORICAL 5 back to 50_67 + Change model to add 50.67 to allowed ... then revert. ie/ must fool own
    * way.
    *
    * Line in UPDATE^GMRAGUI1 ...
    *    S $P(@GMRARRAY@("GMRAGNT"),U,2)=$$TGTOG^PSNAPIS($P(@GMRARRAY@("GMRAGNT"),U))_";PSNDF(50.6,"
    *
    * CONTEXT: will NOT support this NDC to generic translation inside the model. Expect it outside
    * ... added to "Ongoing Allergy Issue"
   */
   // TODO failing test - needs fix
    xit('Create Historical NDC/UPN 50.67 Allergy - will expect equivalent 50.6 to be created in its stead', () => {
       // fifth example actually started as 50.67 but in VDM/FileMan, it's reduced to 50.6. Gotta put 50.67 back.
        const now = moment().format('YYYY-MM-DDTHH:mm:ss');
        const fiftyDotSixtySevenId = '11';
        const rpcDefn = {

            name: 'ORWDAL32 SAVE ALLERGY',

            args: [

                '',
                patient1IEN,
                {

                    GMRAGNT: `ATROPINE SULFATE^${fiftyDotSixtySevenId};PSNDF(50.67,`, // ATROPINE SULFATE - should go through 50.68 into 50.6
                    GMRATYPE: 'D',
                    GMRANATR: 'A',
                    GMRAORIG: DUZ,
                    GMRAORDT: vdmUtils.toFMDateTime(now),
                    GMRACHT: [
                        vdmUtils.toFMDateTime(now),
                    ],
                    GMRAOBHX: 'h',
                },
            ],
        };

        const res = rpcFacade.run(rpcDefn.name, rpcDefn.args);

        expect(res.result).toEqual(0);

        const expectedIEN = fileman.nextIEN(db, '120.8') - 1; // not ideal - better if passed back but not (passes TIU IEN back in other cases!)
        lastHistoricalIEN = expectedIEN; // for EIE of historical test ... will see doc unlike Comment in historical where don't

       /*
        * Emulated or not differs - emulated doesn't chase from 50_67 through 50_68 to 50_6 as not used in CPRS
        */
        const descr = VDM.describe(`120_8-${expectedIEN}`);
        expect(descr.gmr_allergy.id).toMatch(/50_6-/);
    });

   /*
    * Observed has a different fan out. Doing simple observeds.three (Chocolate).
    *
    * Fan out includes document (done in EN1^GMRAPET0 called from different GMRA routines)
    *
    * Key: show what's needed for 120.85 (beyond 120.8 data with severity/date)
    */
    it('Create Observed Chocolate Allergy - expect fan out of notes and more', () => {
        const now = moment().format('YYYY-MM-DDTHH:MM:SS');
        const rpcDefn = {
            name: 'ORWDAL32 SAVE ALLERGY',
            args: [
                '',
                patient1IEN,
                {
                    GMRAGNT: 'CHOCOLATE^3;GMRD(120.82,',
                    GMRATYPE: 'DF^Drug,Food',
                    GMRANATR: 'A^Allergy',
                    GMRAORIG: DUZ,
                    GMRAORDT: vdmUtils.toFMDateTime(now),
                    GMRACHT: [
                        vdmUtils.toFMDateTime(now),
                    ],
                    GMRAOBHX: 'o^Observed',
                    GMRASYMP: ['1^HIVES'],
                    GMRACMTS: ['unfortunate fellow', 'but mannerly!'],
                    GMRARDT: vdmUtils.toFMDateTime(now).split('.')[0],
                    GMRASEVR: '2^MODERATE',
                },
            ],
        };

       // serv and ardt - passing in severity (though optional 1/2/3) and time reactions were entered or occurred (ambiguous)
       // ... time is can be distinct from date entry for allergy as a whole (CPRS allows that)
       // ... there is a third date (date recorded) for 120.85 which is its date of entry ... can use CPRS dialog to effect that too

        lastOREDITED = rpcDefn.args; // for mimic load for edit
        lastAllergyIEN = fileman.nextIEN(db, '120.8');
        let res = rpcFacade.run(rpcDefn.name, rpcDefn.args);

       // 0^[IEN OF TIU NOTE] ex/ 1
        expect(res.result).toMatch(/\d+\^\d+/);
        const resCode = parseInt(res.result.split('^')[0], 10);
        expect(resCode).toEqual(0); // can we get IEN inserted?
        const tiuNoteIEN = res.result.split('^')[1];

       // Simple CPRS "one allergy read" - ORQQAL DETAIL - also used in JLV [used above for one historical allergy]
       // - will return: Obs dates/severity: MAR 01, 2016 MODERATE
        res = rpcFacade.run('ORQQAL DETAIL', ['', lastAllergyIEN]);
        expect(res.result.length).toEqual(16);
        expect(res.result).toContain('    Causative agent: CHOCOLATE');

        const descr = VDM.describe(`120_8-${lastAllergyIEN}`);
    });

   /*
    * TODO: see note above on ORWDAL32 LOAD FOR EDIT and the flow in fARTAllgy.pas (CPRS) ... as flow from edit for read and EIE edit
    */
    it('Load for Edit BUT not edit (yet) ... why?', () => {
       /*
        * ORWDAL32 LOAD FOR EDIT
        *
        * "Structured" Read for Edit
        *   - leverage in fARTAllgy.pas
        *   - from wrap in Orders/rODAllergy.pas
        *     - has "MUMPS in Delphi" parsers like Piece(ExtractDefault(Dest, 'TYPE'), U, 3);
        *   - implemented around a simple GET method that exposes structured information
        *   - not eHMP as doesn't use this get
        *   ... will "use" in prelude to EIE (see below) ala how the fARTAllgy.pas flow
        *
        * See: https://github.com/OSEHRA/VistA/blob/master/Packages/Order%20Entry%20Results%20Reporting/CPRS/CPRS-Chart/Orders/rODAllergy.pas#L116
        */
        const res = rpcFacade.run('ORWDAL32 LOAD FOR EDIT', [lastAllergyIEN]);
        expect(res.result).toContain('dCHOCOLATE'); // funky structured assert format will blank values

       /*
        See both CPRS and MUMPS code (UPDATE^GMRAGUI1) where ADCOM is only called from isolate EIE code (see below)
        AND from update == save new allergy or update but must reset alot of existing fields. Will do reset required.
           SUB="GMRAGNT;.02","GMRATYPE;3.1","GMRANATR;17","GMRAORIG;5","GMRAORDT;4","GMRAOBHX;6" D

        Assume have parsed the "load for edit" - really just reusing the rpc args above ...
        */
        const editOREDITED = {
            GMRATYPE: lastOREDITED.GMRATYPE,
            GMRANATR: lastOREDITED.GMRANATR,
            GMRAORIG: lastOREDITED.GMRAORIG,
            GMRAORDT: lastOREDITED.GMRAORDT,
            GMRAOBHX: lastOREDITED.GMRAOBHX,
        };
        editOREDITED.GMRAGNT = 'STRAWBERRIES^6;GMRD(120.82,'; // can't have two with same comment!

       // and just resulted in all new allergy so didn't bother!

       // TODO: revisit if can edit as even if set old IEN, doesn't seem to work unlike what happens with EIE. MVDM may end up
       // being more flexible.
    });

    it('EIE an Observed Allergy and see side effect document', () => {
       /*
        * EIE: new, novel EIE now BUT assessment and original document still there even though source allergy removed!!!
        * Variation to support EIE - merits own RPC/function but inside create RPC
        * ... EIE Comment is OPTIONAL (ie/ won't be there if not filled in by CPRS)
        *  NOTE: for PROD CLONE, will see name mismatches if any
        * DUZ == Robert ie/ agrees; if disagrees then EIE in someone else's name
        */
        const currentTime = vdmUtils.currentFMDateTime();
       // Note: this EIE doesn't use typed argument values unlike the one at the end of this file.
        const res = rpcFacade.run('ORWDAL32 SAVE ALLERGY', [
            lastAllergyIEN, // allergy to mark EIE
            1, // patient 1
            {
                GMRAERR: 'YES',
                GMRAERRDT: currentTime,
                GMRAERRBY: DUZ, // ignored in emulated path
                GMRAERRCMTS: ['wah, a baaad un!'],
            },
        ]);
        const resCode = parseInt(res.result.split('^')[0], 10);
        expect(resCode).toEqual(0);
       // Now two comments ie/ added EIE comment
        const descrAfterEIE = VDM.describe(`120_8-${lastAllergyIEN}`);
        expect(descrAfterEIE.comments.length).toEqual(2);
        expect(descrAfterEIE.entered_in_error).toEqual(true);

       // now one more (EIE) doc ie/ side effect of marked entered in error is an entered in error document
        const tiuNoteIEN = res.result.split('^')[1];
        const descr = VDM.describe(`8925-${tiuNoteIEN}`);
        expect(descr.document_type.label).toEqual('ADVERSE REACTION_ALLERGY'); // 8925_1-17 - note: no VUID in it or the model for TIU defns (ie/ not vuid'ed!)
       // The allergy to CHOCOLATE was removed on 7/30/16.\nThis reaction was either an erroneous entry or was found\rto no longer be a true allergy.\r\rAuthor's comments:\r\rwah, a baaad un!
        expect(descr.report_text).toMatch(/was removed on/);
        expect(fileman.nextIEN(db, '8925')).toEqual(parseInt(tiuNoteIEN, 10) + 1);
    });

   // Comment in Historical - no side effect doc. EIE historical - side effect doc
    // TODO test failed - needs fix
    xit('EIE an historical allergy and see side effect document', () => {
        const currentTime = vdmUtils.currentFMDateTime();
        const errCmtText = 'wah, a baaad HISTORICAL!';
        const res = rpcFacade.run('ORWDAL32 SAVE ALLERGY', [
            lastHistoricalIEN, // allergy to mark EIE
            1, // patient 1
            {
                GMRAERR: 'YES',
                GMRAERRDT: currentTime,
                GMRAERRBY: DUZ,
                GMRAERRCMTS: [errCmtText],
            },
        ]);
        expect(res.result).toMatch(/0\^\d/);
        const resCode = parseInt(res.result.split('^')[0], 10);
        expect(resCode).toEqual(0);
        const descrAfterEIE = VDM.describe(`120_8-${lastHistoricalIEN}`);
        expect(descrAfterEIE.comments.length).toEqual(1);

       // now one more (EIE) doc ie/ side effect of marked entered in error is an entered in error document
        const tiuNoteIEN = res.result.split('^')[1];
        const descr = VDM.describe(`8925-${tiuNoteIEN}`);
        expect(descr.document_type.label).toEqual('ADVERSE REACTION_ALLERGY'); // 8925_1-17 - note: no VUID in it or the model for TIU defns (ie/ not vuid'ed!)
        expect(descr.report_text).toMatch(/was removed on/);
        expect(descr.report_text).toMatch(errCmtText);
    });

    it('Create Observed Drug Allergy - leads to 120.85 entry', () => {
        const expectedDocumentIEN = fileman.nextIEN(db, '8925');

        const now = moment().format('YYYY-MM-DDTHH:MM:SS');
        const rpcDefn = {
            name: 'ORWDAL32 SAVE ALLERGY',
            args: [
                '',
                patient1IEN,
                {
                    GMRAGNT: 'ACETAMINOPHEN/CODEINE^203;PSNDF(50.6,',
                    GMRATYPE: 'D',
                    GMRANATR: 'P',
                    GMRAORIG: DUZ,
                    GMRAORDT: vdmUtils.toFMDateTime(now),
                    GMRACHT: [
                        vdmUtils.toFMDateTime(now),
                    ],
                    GMRAOBHX: 'o',
                    GMRASYMP: [
                        '51^CHEST PAIN',
                        '1^HIVES',
                    ],
                    GMRACMTS: [
                        "don't give the guy this med! ",
                    ],
                    GMRARDT: vdmUtils.toFMDateTime(now).split('.')[0],
                },
            ],
        };

       // there are reactions
       // serv and ardt - only time of reactions - no severity
        const expectedAllergyIEN = fileman.nextIEN(db, '120.8');
        let res = rpcFacade.run(rpcDefn.name, rpcDefn.args);

       // expect 0^docIEN
        const resCode = parseInt(res.result.split('^')[0], 10);
        expect(resCode).toEqual(0);
        expect(parseInt(res.result.split('^')[1], 10)).toEqual(expectedDocumentIEN);

       // NOTE: do get another TIU document BUT it shares the visit with the Chocolate Allergy? Because created in the same second? ie/ no delay?
       // play with jasmine.clock.tick() etc.

       // DETAIL RPC takes two arguments, the patient id (IEN) and the allergy id (IEN)
        res = rpcFacade.run('ORQQAL DETAIL', ['', expectedAllergyIEN]);

       // See note in MVDM -spec for classes for generics like this one which don't have classes in the defn itself. Native
       // returns classes and 19 lines, but MVDM doesn't yet. Need to fix there.
        if (emulatingOn) { expect(res.result.length).toEqual(17); } else {
            expect(res.result.length).toEqual(19);
            expect(res.result.join('\n')).toMatch('Drug Classes: OPIOID ANALGESICS');
        }

       // REM: no severity as left out above (though CPRS doesn't)
       // TODO/FIND: the native form has Verified: NO ... why?
    });

   /*
    * TODO: Create Observed Food (...) Allergy - comment in 120.85 indicates its reports are only for drugs but work for D, DF (choc above) and O (in a clone)
    * ... what about "F"? Is 120.85 comment wrong -- capture in VDM model.
    */

   /*
    * CPRS ORQQAL LIST (REPORT) returns list of active allergies for a Patient
    * - CPRS (rCover.pas)
    * Lists reactants ie/ what a patient is allergic to
    * ... seems to be the same as RPC GMV ALLERGY used in CPRS GMV_VitalsViewEnter.dll
    */
    it('Use CPRS Allergy List', () => {
       /*
          1^PENICILLINS AND BETA-LACTAM ANTIMICROBIALS^
          3^BADREFS^ <---- corrupt so native only
          4^PENICILLIN^
          5^ACETAMINOPHENone^ <----- corrupt so native only
          6^ASCORBIC ACID^
          <---- no 7 or 8/CHOCOLATE as EIE above - filtered out
          9^ACETAMINOPHEN/CODEINE^^CHEST PAIN;HIVES
        */
        const res = rpcFacade.run('ORQQAL LIST', [patient1IEN]); // for patient
       // emulated excludes invalid items - 2 above
        const expectedNumber = emulatingOn ? 4 : 6;
        expect(res.result.length).toEqual(expectedNumber);
        expect(res.result).toContain('1^PENICILLINS AND BETA-LACTAM ANTIMICROBIALS^');
    });

   /*
    * NKA - adds to the 120.8/120.86 conjoined model.
    *
    * Stages from NKA:
    * - VDM-wise, there is no NKA. There is an absences of 120.8's and a 120.86 entry which indicates no allergies.
    * - can't NKA if 120.8 has an entry (MVDM check)
    *   - tie of 120.86 and 120.8
    * - if no 120.86, can NKA
    * - if then does new allergy, 120.86 changes to assessed as having allergy
    *
    * CPRS RPC Test:
    * - ORQQAL LIST (or LIST REPORT) should show NKA
    *
    * *** Unlike tests above, this purges the results of previous tests
    */
    it('Set Patient as NKA - and then override that', () => {
       /*
        * NKA is marked with the same RPC as allergy create and update - there is a special "list" argument, "GMRANKA"
        * ... should really have its own RPC.
        */
        let res = rpcFacade.run('ORWDAL32 SAVE ALLERGY', ['', patient1IEN, { GMRANKA: 'YES' }]); // for patient 1
        expect(res.result).toEqual("-1^Patient has active allergies - can't mark as NKA");

       // Now let's empty em out ... and try again
        allergyUtils.purgeAllAllergies(db, false);
        res = rpcFacade.run('ORWDAL32 SAVE ALLERGY', ['', patient1IEN, { GMRANKA: 'YES' }]); // for patient 1 again!
        expect(res.result).toEqual(0);

       // There will be no 120.8's and one 120.86 with an assessment of false
        let nextAllergyIEN = fileman.nextIEN(db, '120.8');
        expect(nextAllergyIEN).toEqual(1);
        nextAllergyIEN = fileman.nextIEN(db, '120.86');
        expect(nextAllergyIEN).toEqual(patient1IEN + 1);
        let descr = VDM.describe(`120_86-${nextAllergyIEN - 1}`);
        expect(descr.reaction_assessment).toEqual(false);

       // Do NKA twice - works (not errored) but no new assessment made
        res = rpcFacade.run('ORWDAL32 SAVE ALLERGY', ['', patient1IEN, { GMRANKA: 'YES' }]); // for patient 1 again!
        expect(res.result).toEqual(0);

       /*
        * CPRS: ORQQAL LIST (REPORT) reports NKA
        * Note: JLV doesn't use this. It uses VPR XML which does report NKA (oh the mix of things!)
        */
        res = rpcFacade.run('ORQQAL LIST', [patient1IEN]); // for patient 1
        expect(res.result.length).toEqual(1);
        expect(res.result).toContain('^No Known Allergies');

       // Let's add an allergy (same as first one above)
        const now = moment().format('YYYY-MM-DDTHH:MM:SS');
        const rpcDefn = {
            name: 'ORWDAL32 SAVE ALLERGY',
            args: [
                '',
                patient1IEN,
                {
                    GMRAGNT: 'PENICILLINS AND BETA-LACTAM ANTIMICROBIALS^11;PS(50.605,',
                    GMRATYPE: 'D',
                    GMRANATR: 'U',
                    GMRAORIG: DUZ,
                    GMRAORDT: vdmUtils.toFMDateTime(now),
                    GMRACHT: [
                        vdmUtils.toFMDateTime(now),
                    ],
                    GMRAOBHX: 'h',
                },
            ],
        };

        res = rpcFacade.run(rpcDefn.name, rpcDefn.args);
        expect(res.result).toEqual(0);

       // reaction assessment left but now true
        nextAllergyIEN = fileman.nextIEN(db, '120.86');
        expect(nextAllergyIEN).toEqual(patient1IEN + 1); // still only one
        descr = VDM.describe(`120_86-${nextAllergyIEN - 1}`); // refetch - now true
        expect(descr.reaction_assessment).toEqual(true);
    });

    /*
     * EIE - side effect if last allergy that 120.86 is deleted!
     */
    it('EIE Last Allergy - 120.86 Assessment Deleted but can still see EIE in detail', () => {
       // EIE only allergy - assessment will be removed (not set to False again).
       // ... for patient 1, IEN 1 (only one), date now, by 56
       // ... EIE progress note created by side effect ... and will never be deleted.
       // ... not added to existing Progress Note on allergy as historicals don't get "setup notes"
       // ... ie/ all historical EIEs get notes.
        const expected8925IEN = fileman.nextIEN(db, '8925');
        const currentTime = vdmUtils.currentFMDateTime();
       // Doing a fully typed set of argument values - not drawing off VDM variations (though may change to using update VDM values)
        let res = rpcFacade.run('ORWDAL32 SAVE ALLERGY', [
            1, // allergy to mark EIE
            patient1IEN, // patient 1
            {
                GMRAERR: 'YES',
                GMRAERRDT: currentTime,
                GMRAERRBY: DUZ, // ignored in emulated path
                GMRAERRCMTS: ['oops me baaaaaad!'],
            },
        ]);
        const resCode = parseInt(res.result.split('^')[0], 10);
        const resMsg = res.result.split('^')[1];
        expect(resCode).toEqual(0); // error!
        expect(resMsg).toEqual(expected8925IEN.toString()); // IEN of progress note created

        // { result: [ '^No Allergy Assessment' ] }
        res = rpcFacade.run('ORQQAL LIST', [patient1IEN]); // for patient 1
        expect(res.result[0]).toMatch(/^\^No Allergy Assessment$/);

       // Only EIE Allergy but DETAIL doesn't know - it takes the COMMENT to be an O comment (ie/ doesn't indicate it is an E!)
       /*
         '    Causative agent: PENICILLINS AND BETA-LACTAM ANTIMICROBIALS',
         ' Nature of Reaction: Unknown',
         ' ',
         '       Drug Classes: PENICILLINS AND BETA-LACTAM ANTIMICROBIALS',
         ' ',
         '         Originator: ALEXANDER,ROBERT',
         '         Originated: Feb 18, 2016@17:32',
         '           Verified: <auto-verified>',
         'Observed/Historical: Historical',
         ' ',
         'Comments:',
         '   JUN 30, 2016@23:41:46 by ',
         'oops me baaaaaad! ',
         ' ' ]
       */
        res = rpcFacade.run('ORQQAL DETAIL', [patient1IEN, 1]);
        expect(res.result.join(' | ')).toMatch('oops me baaaaaad!');

       // NOT TRIED: if do two allergies after NKA then first changes assessment date but second doesn't update the date. and if mark first as EIE, the assessment
       // date doesn't change. This would be quite elaborate behavior.
    });

    /*
     * Site Parameters that effect allergies
     *
     * TODO: examine settings and see effect (if want to emulate or not) ie/ will probably fix the response in Allergy Model to match the settings that are
     * are implemented OR may move this up and FORCE this to have certain settings so that native matches MVDM.
     */
    it('Site Parameters for Allergies', () => {
        // result: 'HOSPITAL^6^1^1^1^!^y^0^1^1^1'
        const res = rpcFacade.run('ORWDAL32 SITE PARAMS');
    });

    /* *********************** Isolate Emulator UNSTRUCTURED READ formatter tests ************************* */
    // TODO: ORQQAL DETAIL - non drug (120.82), non classes allergin with two reactions - EMULATOR only so if false then return

   /*
    * TODO: protocol fired from EIE - can't be as OSEHRA's Allergy EIE protocol has no entries.
    */

   /*
    * TODO: more on Reactions with date < now ie/ will show up if date < now
    * ... a lot more on the "data occurred" in Reactions (120.8) - ie/ observed can have prior, historical too? and VDM doesn't store if same.
    */

   /*
    * TODO: Locking test. Allergy using Orders explicit LOCK RPC (ORWDX LOCK) https://github.com/vistadataproject/VDM/issues/40#issuecomment-238730663
    *
    * ... it goes across RPCs (Processes in old MUMPS) as treating the Allergy creation as a transaction. XTMP holds the 'lock'. Must test lock and then
    * see attempt to lock again fail. Then unlock and try again. ... Note: this locks the whole DFN! But won't stop Problem as Problem goes its own way.
    * It'll just lock allergy and Orders.
    * ... we could lock ALL (including problems) in the Patient way ie/ error problem change if patient emulated.
    */

   /*
    * TODO: DUPLICATE REACTION (ex/ Hives). Native will remove but emulated SHOULD exception
    */

    afterAll(() => {
        db.close();
    });
});

