import { randomInt } from 'crypto';
import { isFunction, snakeCase } from 'lodash';
import Chance from 'chance';
import Sequelize from 'sequelize';
import { inspect } from 'util';
import { formatISO9075 } from 'date-fns';
import { ADMINISTRATION_FREQUENCIES, BLOOD_TYPES, CURRENTLY_AT_TYPES, DAYS_OF_WEEK, DIAGNOSIS_CERTAINTY_VALUES, DRUG_ROUTE_VALUES, EDUCATIONAL_ATTAINMENT_TYPES, ENCOUNTER_TYPE_VALUES, IMAGING_REQUEST_STATUS_TYPES, LAB_REQUEST_STATUSES, MARITAL_STATUS_VALUES, NOTE_TYPE_VALUES, PROGRAM_DATA_ELEMENT_TYPE_VALUES, REFERENCE_TYPE_VALUES, REGISTRATION_STATUSES, REPEAT_FREQUENCY, REPEAT_FREQUENCY_VALUES, SEX_VALUES, TITLES, VACCINE_CATEGORIES_VALUES, VACCINE_RECORDING_TYPES, VISIBILITY_STATUSES } from '@tamanu/constants';
import { toDateString, toDateTimeString } from '@tamanu/utils/dateTime';
import { fakeUUID } from '@tamanu/utils/generateId';
import { FhirAddress, FhirAnnotation, FhirCodeableConcept, FhirContactPoint, FhirExtension, FhirHumanName, FhirIdentifier, FhirImmunizationPerformer, FhirImmunizationProtocolApplied, FhirPatientLink, FhirReference } from '@tamanu/shared/services/fhirTypes';
import { DRUGS } from '@tamanu/database/demoData/drugs';
import { ALLERGIES } from '@tamanu/database/demoData/allergies';
import { DIAGNOSES } from '@tamanu/database/demoData/diagnoses';
import { TRIAGE_DIAGNOSES } from '@tamanu/database/demoData/triageDiagnoses';
import { VILLAGES } from '@tamanu/database/demoData/villages';
import { PROCEDURE_TYPES } from '@tamanu/database/demoData/procedureTypes';
import { X_RAY_IMAGING_AREAS, CT_SCAN_IMAGING_AREAS, ULTRASOUND_IMAGING_AREAS } from '@tamanu/database/demoData/imagingAreas';
const DRUG_NAMES = DRUGS.map((d)=>d.name);
const ALLERGY_NAMES = ALLERGIES.map((a)=>a.name);
const DIAGNOSIS_NAMES = DIAGNOSES.map((d)=>d.name.split('\t')[0]);
const TRIAGE_REASON_NAMES = TRIAGE_DIAGNOSES.map((t)=>t.name);
const VILLAGE_NAMES = VILLAGES.map((v)=>v.name);
const PROCEDURE_TYPE_NAMES = PROCEDURE_TYPES.map((p)=>p.name.split('\t')[1] || p.name);
const X_RAY_AREA_NAMES = X_RAY_IMAGING_AREAS.map((a)=>a.name);
const CT_SCAN_AREA_NAMES = CT_SCAN_IMAGING_AREAS.map((a)=>a.name);
const ULTRASOUND_AREA_NAMES = ULTRASOUND_IMAGING_AREAS.map((a)=>a.name);
// this file is most commonly used within tests, but also outside them
// jest won't always be defined, in which case we can use a random seed
export const chance = new Chance(global.jest?.getSeed() ?? randomInt(2 ** 42));
export function fakeScheduledVaccine(prefix = 'test-') {
    const id = fakeUUID();
    return {
        id: `${prefix}scheduledVaccine_${id}`,
        weeksFromBirthDue: chance.pickone([
            0,
            6,
            10,
            14,
            24,
            36,
            52,
            78,
            104,
            260
        ]),
        weeksFromLastVaccinationDue: null,
        index: chance.integer({
            min: 0,
            max: 50
        }),
        vaccineId: null,
        visibilityStatus: VISIBILITY_STATUSES.CURRENT,
        sortIndex: 0,
        category: chance.pickone(VACCINE_CATEGORIES_VALUES),
        label: chance.pickone([
            'BCG',
            'Hepatitis B',
            'OPV',
            'IPV',
            'DTP',
            'Measles',
            'Rubella',
            'MMR',
            'Tetanus',
            'Pneumococcal',
            'Rotavirus',
            'HPV',
            'Yellow Fever',
            'Typhoid',
            'Influenza',
            'Varicella',
            'Hepatitis A',
            'Meningococcal',
            'Japanese Encephalitis'
        ]),
        doseLabel: chance.pickone([
            'Dose 1',
            'Dose 2',
            'Dose 3',
            'Dose 4',
            'Booster',
            'Birth dose',
            'Annual'
        ])
    };
}
export function fakeSurvey(prefix = 'test-') {
    const id = fakeUUID();
    const surveyName = chance.pickone([
        'Maternal Health Assessment',
        'Nutrition Screening',
        'Mental Health Questionnaire',
        'Chronic Disease Follow-up',
        'Community Health Survey',
        'Immunisation Checklist',
        'Antenatal Care Visit',
        'Postnatal Care Assessment',
        'TB Screening Form',
        'Malaria Case Investigation',
        'NCD Risk Assessment',
        'Child Growth Monitoring',
        'Family Planning Counselling',
        'HIV Testing & Counselling',
        'Outbreak Investigation Form',
        'Patient Discharge Summary'
    ]);
    return {
        id: `${prefix}survey_${id}`,
        programId: null,
        surveyType: 'programs',
        isSensitive: false,
        code: `SRV-${chance.hash({
            length: 6
        }).toUpperCase()}`,
        name: surveyName
    };
}
export function fakeSurveyScreenComponent(prefix = 'test-') {
    const id = fakeUUID();
    const { text, detail } = chance.pickone([
        {
            text: 'Systolic blood pressure (mmHg)',
            detail: 'Measure after 5 min rest, use left arm'
        },
        {
            text: 'Diastolic blood pressure (mmHg)',
            detail: 'Record seated reading'
        },
        {
            text: 'Temperature (°C)',
            detail: 'Use tympanic or oral thermometer'
        },
        {
            text: 'Weight (kg)',
            detail: 'Remove shoes and heavy clothing'
        },
        {
            text: 'Height (cm)',
            detail: 'Patient should be standing straight'
        },
        {
            text: 'Heart rate (bpm)',
            detail: 'Count for 60 seconds at radial pulse'
        },
        {
            text: 'Respiratory rate',
            detail: 'Count breaths per minute at rest'
        },
        {
            text: 'Oxygen saturation (%)',
            detail: 'Use pulse oximeter on index finger'
        },
        {
            text: 'Blood glucose (mmol/L)',
            detail: 'Record fasting or random, note which'
        },
        {
            text: 'MUAC (cm)',
            detail: 'Mid-upper arm circumference, left arm'
        },
        {
            text: 'Pain score (0-10)',
            detail: '0 = no pain, 10 = worst imaginable'
        },
        {
            text: 'Urine dipstick result',
            detail: 'Record protein, glucose, blood, leukocytes'
        },
        {
            text: 'Fundal height (cm)',
            detail: 'Measure from pubic symphysis'
        },
        {
            text: 'Oedema',
            detail: 'Check ankles, shins, and sacral area'
        },
        {
            text: 'Clinical notes',
            detail: 'Free text observations'
        },
        {
            text: 'Presenting complaint',
            detail: "Chief complaint in patient's own words"
        }
    ]);
    return {
        id: `${prefix}surveyScreenComponent_${id}`,
        surveyId: null,
        dataElementId: null,
        screenIndex: chance.integer({
            min: 0,
            max: 5
        }),
        componentIndex: chance.integer({
            min: 0,
            max: 10
        }),
        options: '{"foo":"bar"}',
        calculation: '',
        text,
        visibilityCriteria: '',
        validationCriteria: '',
        detail,
        config: '{}'
    };
}
export function fakeProgramDataElement(prefix = 'test-') {
    const id = fakeUUID();
    const { name: elementName, indicator } = chance.pickone([
        {
            name: 'Systolic Blood Pressure',
            indicator: 'Vital Signs'
        },
        {
            name: 'Diastolic Blood Pressure',
            indicator: 'Vital Signs'
        },
        {
            name: 'Body Temperature',
            indicator: 'Vital Signs'
        },
        {
            name: 'Respiratory Rate',
            indicator: 'Vital Signs'
        },
        {
            name: 'Oxygen Saturation',
            indicator: 'Vital Signs'
        },
        {
            name: 'Pulse Rate',
            indicator: 'Vital Signs'
        },
        {
            name: 'Body Weight',
            indicator: 'Anthropometry'
        },
        {
            name: 'Body Height',
            indicator: 'Anthropometry'
        },
        {
            name: 'BMI',
            indicator: 'Anthropometry'
        },
        {
            name: 'MUAC',
            indicator: 'Anthropometry'
        },
        {
            name: 'Head Circumference',
            indicator: 'Anthropometry'
        },
        {
            name: 'Haemoglobin Level',
            indicator: 'Lab Results'
        },
        {
            name: 'Blood Glucose',
            indicator: 'Lab Results'
        },
        {
            name: 'Malaria RDT Result',
            indicator: 'Lab Results'
        },
        {
            name: 'HIV Test Result',
            indicator: 'Lab Results'
        },
        {
            name: 'Urine Protein',
            indicator: 'Lab Results'
        },
        {
            name: 'Cough Duration',
            indicator: 'Symptoms'
        },
        {
            name: 'Fever Duration',
            indicator: 'Symptoms'
        },
        {
            name: 'Pain Score',
            indicator: 'Symptoms'
        },
        {
            name: 'Nausea Severity',
            indicator: 'Symptoms'
        },
        {
            name: 'Pregnancy Status',
            indicator: 'Reproductive Health'
        },
        {
            name: 'Gestational Age',
            indicator: 'Reproductive Health'
        },
        {
            name: 'Fundal Height',
            indicator: 'Reproductive Health'
        },
        {
            name: 'Gravidity',
            indicator: 'Reproductive Health'
        }
    ]);
    return {
        id: `${prefix}programDataElement_${id}`,
        type: chance.pickone(PROGRAM_DATA_ELEMENT_TYPE_VALUES),
        code: `PDE-${chance.hash({
            length: 6
        }).toUpperCase()}`,
        name: elementName,
        indicator,
        defaultText: chance.pickone([
            'Enter measured value',
            'Select from options below',
            'Record observation here',
            'Measured at time of visit',
            'Ask patient directly',
            'Refer to lab slip',
            'Use standard equipment'
        ]),
        defaultOptions: ''
    };
}
export function fakeReferenceData(prefix = 'test-') {
    const id = fakeUUID();
    const REFERENCE_DATA_OPTIONS = [
        {
            type: 'drug',
            names: DRUG_NAMES
        },
        {
            type: 'allergy',
            names: ALLERGY_NAMES
        },
        {
            type: 'diagnosis',
            names: DIAGNOSIS_NAMES
        },
        {
            type: 'triageReason',
            names: TRIAGE_REASON_NAMES
        },
        {
            type: 'village',
            names: VILLAGE_NAMES
        },
        {
            type: 'xRayImagingArea',
            names: X_RAY_AREA_NAMES
        },
        {
            type: 'ctScanImagingArea',
            names: CT_SCAN_AREA_NAMES
        },
        {
            type: 'ultrasoundImagingArea',
            names: ULTRASOUND_AREA_NAMES
        },
        {
            type: 'procedureType',
            names: PROCEDURE_TYPE_NAMES
        },
        {
            type: 'division',
            names: [
                'Northern Division',
                'Southern Division',
                'Eastern Division',
                'Western Division',
                'Central Division',
                'Highlands Division',
                'Islands Division',
                'Coastal Division'
            ]
        },
        {
            type: 'subdivision',
            names: [
                'Kairuku District',
                'Rigo District',
                'Abau District',
                'Goilala District',
                'North Coast',
                'South Coast',
                'Upper Valley',
                'Lower Valley'
            ]
        },
        {
            type: 'ethnicity',
            names: [
                'Melanesian',
                'Polynesian',
                'Micronesian',
                'Papuan',
                'Chinese',
                'European',
                'Mixed Heritage',
                'Indian',
                'Filipino'
            ]
        },
        {
            type: 'nationality',
            names: chance.n(()=>chance.country({
                    full: true
                }), 20)
        },
        {
            type: 'occupation',
            names: chance.n(()=>chance.profession(), 20)
        },
        {
            type: 'religion',
            names: [
                'Catholic',
                'Lutheran',
                'United Church',
                'Seventh Day Adventist',
                'Anglican',
                'Pentecostal',
                'Baptist',
                'Evangelical'
            ]
        },
        {
            type: 'labTestCategory',
            names: [
                'Haematology',
                'Biochemistry',
                'Microbiology',
                'Serology',
                'Urinalysis',
                'Parasitology',
                'Immunology',
                'Cytology'
            ]
        }
    ];
    const { type, names } = chance.pickone(REFERENCE_DATA_OPTIONS);
    const name = chance.pickone(names);
    return {
        id: `${prefix}referenceData_${id}`,
        type,
        visibilityStatus: VISIBILITY_STATUSES.CURRENT,
        name,
        code: `REF-${chance.hash({
            length: 6
        }).toUpperCase()}`
    };
}
export function fakeUser(prefix = 'test-') {
    const id = fakeUUID();
    const firstName = chance.first();
    const lastName = chance.last();
    return {
        id: `${prefix}user_${id}`,
        displayId: chance.hash({
            length: 5
        }).toUpperCase(),
        email: chance.email(),
        displayName: `${firstName} ${lastName}`,
        role: 'practitioner'
    };
}
export function fakeProgram(prefix = 'test-') {
    const id = fakeUUID();
    const programName = chance.pickone([
        'Malaria Control Program',
        'Maternal Health Program',
        'Tuberculosis Program',
        'HIV/AIDS Program',
        'Child Health Program',
        'Nutrition Program',
        'Non-Communicable Disease Program',
        'Expanded Programme on Immunization',
        'Reproductive Health Program',
        'Mental Health Program',
        'Neglected Tropical Diseases Program',
        'Water & Sanitation Program',
        'Community Health Worker Program',
        'Outbreak Surveillance Program'
    ]);
    return {
        id: `${prefix}program_${id}`,
        name: programName,
        code: `PRG-${chance.hash({
            length: 6
        }).toUpperCase()}`
    };
}
export function fakeAdministeredVaccine(prefix = 'test-', scheduledVaccineId) {
    const id = fakeUUID();
    return {
        id: `${prefix}administeredVaccine_${id}`,
        encounterId: null,
        scheduledVaccineId,
        date: formatISO9075(fakeDate()),
        batch: `${chance.character({
            alpha: true
        }).toUpperCase()}${chance.natural({
            min: 1000,
            max: 9999
        })}`,
        status: chance.pickone(Object.values(VACCINE_RECORDING_TYPES)),
        reason: chance.pickone([
            'Routine schedule',
            'Catch-up',
            'Post-exposure',
            'Travel requirement',
            'Outbreak response',
            'School entry requirement',
            'Occupational health',
            'Maternal immunisation',
            'Campaign',
            ''
        ])
    };
}
export function fakeEncounter(prefix = 'test-') {
    const id = fakeUUID();
    const startDate = fakeDate();
    const endDate = new Date(startDate.getTime() + chance.integer({
        min: 1,
        max: 14
    }) * 86400000);
    return {
        deviceId: null,
        surveyResponses: [],
        administeredVaccines: [],
        encounterType: chance.pickone(ENCOUNTER_TYPE_VALUES),
        startDate: formatISO9075(startDate),
        endDate: formatISO9075(endDate),
        id: `${prefix}encounter_${id}`,
        reasonForEncounter: chance.pickone([
            'Routine check-up',
            'Fever and headache',
            'Follow-up visit',
            'Injury assessment',
            'Prenatal care',
            'Chest pain',
            'Abdominal pain',
            'Vaccination',
            'Persistent cough',
            'Skin rash',
            'Diarrhoea and vomiting',
            'Wound dressing',
            'Medication review',
            'Shortness of breath',
            'Joint pain',
            'Eye infection',
            'Ear pain',
            'Dental referral',
            'Post-surgical review',
            'Counselling session',
            'Growth monitoring',
            'Lab result follow-up'
        ])
    };
}
export function fakeSurveyResponse(prefix = 'test-') {
    const id = fakeUUID();
    const startTime = fakeDate();
    const endTime = new Date(startTime.getTime() + chance.integer({
        min: 1,
        max: 60
    }) * 60000);
    return {
        answers: [],
        encounterId: null,
        surveyId: null,
        startTime: toDateTimeString(startTime),
        endTime: toDateTimeString(endTime),
        result: Math.round(chance.floating({
            min: 0,
            max: 100
        }) * 10) / 10,
        id: `${prefix}surveyResponse_${id}`
    };
}
export function fakeSurveyResponseAnswer(prefix = 'test-') {
    const id = fakeUUID();
    const clampedNormal = (mean, dev, min, max, decimals = 0)=>{
        const val = Math.max(min, Math.min(max, chance.normal({
            mean,
            dev
        })));
        return decimals > 0 ? val.toFixed(decimals) : Math.round(val).toString();
    };
    const SURVEY_ANSWER_OPTIONS = [
        {
            name: 'Blood pressure',
            body: ()=>`${clampedNormal(120, 15, 80, 180)}/${clampedNormal(80, 10, 40, 110)}`
        },
        {
            name: 'Temperature',
            body: ()=>clampedNormal(37.0, 0.5, 35.5, 41.0, 1)
        },
        {
            name: 'Weight',
            body: ()=>clampedNormal(70, 15, 30, 150, 1)
        },
        {
            name: 'Height',
            body: ()=>clampedNormal(165, 10, 140, 200)
        },
        {
            name: 'Heart rate',
            body: ()=>clampedNormal(75, 12, 40, 150)
        },
        {
            name: 'SpO2',
            body: ()=>`${clampedNormal(97, 2, 85, 100)}%`
        },
        {
            name: 'Respiratory rate',
            body: ()=>clampedNormal(16, 3, 10, 35)
        },
        {
            name: 'Blood glucose',
            body: ()=>clampedNormal(5.5, 2.0, 2.0, 20.0, 1)
        },
        {
            name: 'MUAC',
            body: ()=>clampedNormal(25, 4, 10, 35, 1)
        },
        {
            name: 'Pain score',
            body: ()=>clampedNormal(3, 2.5, 0, 10)
        },
        {
            name: 'Haemoglobin',
            body: ()=>clampedNormal(13.0, 2.0, 5.0, 19.0, 1)
        },
        {
            name: 'Gestational age (weeks)',
            body: ()=>clampedNormal(28, 8, 4, 42)
        },
        {
            name: 'Fundal height',
            body: ()=>clampedNormal(28, 7, 12, 42)
        },
        {
            name: 'Malaria RDT',
            body: ()=>chance.pickone([
                    'Positive',
                    'Negative'
                ])
        },
        {
            name: 'HIV test result',
            body: ()=>chance.pickone([
                    'Reactive',
                    'Non-reactive',
                    'Indeterminate'
                ])
        },
        {
            name: 'Oedema',
            body: ()=>chance.pickone([
                    'None',
                    'Mild (+)',
                    'Moderate (++)',
                    'Severe (+++)'
                ])
        },
        {
            name: 'Notes',
            body: ()=>chance.pickone([
                    'Patient reports feeling better',
                    'No complaints today',
                    'Mild discomfort noted',
                    'Awaiting lab results',
                    'Referred for further investigation',
                    'Condition stable, continue treatment',
                    'Patient counselled on medication adherence',
                    'Wound healing well'
                ])
        }
    ];
    const answer = chance.pickone(SURVEY_ANSWER_OPTIONS);
    const { name } = answer;
    const body = answer.body();
    return {
        id: `${prefix}surveyResponseAnswer_${id}`,
        dataElementId: null,
        responseId: null,
        name,
        body
    };
}
export function fakeEncounterDiagnosis(prefix = 'test-') {
    const id = fakeUUID();
    return {
        certainty: chance.pickone(DIAGNOSIS_CERTAINTY_VALUES),
        date: formatISO9075(fakeDate()),
        isPrimary: chance.bool(),
        encounterId: null,
        diagnosisId: null,
        id: `${prefix}encounterDiagnosis_${id}`
    };
}
export function fakePrescription(prefix = 'test-') {
    const id = fakeUUID();
    const date = fakeDate();
    const endDate = new Date(date.getTime() + chance.integer({
        min: 1,
        max: 30
    }) * 86400000);
    return {
        date: formatISO9075(date),
        endDate: formatISO9075(endDate),
        id: `${prefix}prescription_${id}`,
        note: chance.pickone([
            'Take with food',
            'Avoid alcohol during course',
            'Review in 2 weeks',
            'Monitor for side effects',
            'Reduce dose if drowsy',
            'Continue until course complete',
            'Take on an empty stomach',
            'Do not crush or chew',
            'Store in refrigerator',
            'Apply to affected area only',
            'Shake well before use',
            'Complete full course even if symptoms improve',
            'Take at bedtime',
            'Avoid direct sunlight while using'
        ]),
        indication: chance.pickone([
            'Bacterial infection',
            'Pain management',
            'Hypertension',
            'Type 2 diabetes',
            'Inflammation',
            'Acid reflux',
            'Malaria treatment',
            'Asthma',
            'Anxiety',
            'Anaemia',
            'Fungal infection',
            'Fever',
            'Allergic reaction',
            'Wound prophylaxis',
            'Tuberculosis',
            'HIV antiretroviral therapy'
        ]),
        route: chance.pickone(DRUG_ROUTE_VALUES)
    };
}
const CURRENT_YEAR = new Date().getFullYear();
export const fakeDate = ()=>chance.date({
        year: chance.integer({
            min: CURRENT_YEAR - 5,
            max: CURRENT_YEAR
        })
    });
export const fakeString = (model, { fieldName }, id)=>`${model.name}.${fieldName}.${id}`;
export const fakeDateTimeString = ()=>toDateTimeString(fakeDate());
export const fakeDateString = ()=>toDateString(fakeDate());
export const fakeInt = ()=>chance.integer({
        min: 0,
        max: 10
    });
export const fakeFloat = ()=>chance.floating({
        min: 0,
        max: 1000,
        fixed: 2
    });
export const fakeBool = ()=>chance.bool();
const FIELD_HANDLERS = {
    'TIMESTAMP WITH TIME ZONE': fakeDate,
    'TIMESTAMP WITHOUT TIME ZONE': fakeDate,
    DATETIME: fakeDate,
    TIMESTAMP: fakeDate,
    // custom type used for datetime string storage
    date_time_string: fakeDateTimeString,
    DATETIMESTRING: fakeDateTimeString,
    // custom type used for date string storage
    date_string: fakeDateString,
    DATESTRING: fakeDateString,
    'VARCHAR(19)': fakeDateString,
    'VARCHAR(255)': fakeString,
    // fallback for all other varchar lengths
    'VARCHAR(N)': (model, attrs, id, length)=>fakeString(model, attrs, id).slice(0, length),
    TEXT: fakeString,
    INTEGER: fakeInt,
    FLOAT: fakeFloat,
    DECIMAL: fakeFloat,
    'TINYINT(1)': fakeBool,
    BOOLEAN: fakeBool,
    ENUM: (_, { type })=>chance.pickone(type.values),
    UUID: ()=>fakeUUID()
};
const IGNORED_FIELDS = [
    'createdAt',
    'updatedAt',
    'deletedAt',
    'updatedAtSyncTick'
];
const MODEL_SPECIFIC_OVERRIDES = {
    Facility: ()=>{
        const facilityType = chance.pickone([
            'hospital',
            'clinic',
            'health_centre',
            'aid_post',
            'dispensary',
            'district_hospital',
            'provincial_hospital',
            'urban_clinic'
        ]);
        const namePrefixGenerators = [
            ()=>chance.city(),
            ()=>`${chance.last()} Memorial`,
            ()=>`St. ${chance.first()}`,
            ()=>`${chance.city()} District`,
            ()=>'Central',
            ()=>'National',
            ()=>`Port ${chance.last()}`,
            ()=>chance.company()
        ];
        const namePrefix = chance.pickone(namePrefixGenerators)();
        const nameSuffix = {
            hospital: 'Hospital',
            clinic: 'Clinic',
            health_centre: 'Health Centre',
            aid_post: 'Aid Post',
            dispensary: 'Dispensary',
            district_hospital: 'District Hospital',
            provincial_hospital: 'Provincial Hospital',
            urban_clinic: 'Urban Clinic'
        }[facilityType];
        return {
            name: `${namePrefix} ${nameSuffix}`,
            email: chance.email(),
            contactNumber: chance.phone(),
            streetAddress: chance.address(),
            cityTown: chance.city(),
            division: chance.province({
                full: true
            }),
            type: facilityType
        };
    },
    ImagingRequest: ()=>{
        const status = chance.pickone(Object.values(IMAGING_REQUEST_STATUS_TYPES));
        const isCancelled = status === IMAGING_REQUEST_STATUS_TYPES.CANCELLED;
        return {
            status,
            reasonForCancellation: isCancelled ? chance.pickone([
                'duplicate',
                'entered-in-error'
            ]) : null
        };
    },
    LabTestType: ()=>{
        const suffix = chance.hash({
            length: 4
        });
        const { code: baseCode, name: baseName, unit } = chance.pickone([
            {
                code: 'WBC',
                name: 'White Blood Cell Count',
                unit: 'x10^9/L'
            },
            {
                code: 'RBC',
                name: 'Red Blood Cell Count',
                unit: 'x10^12/L'
            },
            {
                code: 'HGB',
                name: 'Haemoglobin',
                unit: 'g/dL'
            },
            {
                code: 'HCT',
                name: 'Haematocrit',
                unit: '%'
            },
            {
                code: 'PLT',
                name: 'Platelet Count',
                unit: 'x10^9/L'
            },
            {
                code: 'MCV',
                name: 'Mean Corpuscular Volume',
                unit: 'fL'
            },
            {
                code: 'GLU',
                name: 'Blood Glucose',
                unit: 'mmol/L'
            },
            {
                code: 'HbA1c',
                name: 'Glycated Haemoglobin',
                unit: '%'
            },
            {
                code: 'CREAT',
                name: 'Creatinine',
                unit: 'umol/L'
            },
            {
                code: 'BUN',
                name: 'Blood Urea Nitrogen',
                unit: 'mmol/L'
            },
            {
                code: 'ALT',
                name: 'Alanine Aminotransferase',
                unit: 'IU/L'
            },
            {
                code: 'AST',
                name: 'Aspartate Aminotransferase',
                unit: 'IU/L'
            },
            {
                code: 'ALP',
                name: 'Alkaline Phosphatase',
                unit: 'IU/L'
            },
            {
                code: 'TBIL',
                name: 'Total Bilirubin',
                unit: 'umol/L'
            },
            {
                code: 'TSH',
                name: 'Thyroid Stimulating Hormone',
                unit: 'mIU/L'
            },
            {
                code: 'CRP',
                name: 'C-Reactive Protein',
                unit: 'mg/L'
            },
            {
                code: 'ESR',
                name: 'Erythrocyte Sedimentation Rate',
                unit: 'mm/hr'
            },
            {
                code: 'Na',
                name: 'Sodium',
                unit: 'mmol/L'
            },
            {
                code: 'K',
                name: 'Potassium',
                unit: 'mmol/L'
            },
            {
                code: 'Cl',
                name: 'Chloride',
                unit: 'mmol/L'
            },
            {
                code: 'Ca',
                name: 'Calcium',
                unit: 'mmol/L'
            },
            {
                code: 'CHOL',
                name: 'Total Cholesterol',
                unit: 'mmol/L'
            },
            {
                code: 'TRIG',
                name: 'Triglycerides',
                unit: 'mmol/L'
            },
            {
                code: 'UA',
                name: 'Uric Acid',
                unit: 'umol/L'
            },
            {
                code: 'mRDT',
                name: 'Malaria Rapid Diagnostic Test',
                unit: ''
            },
            {
                code: 'HIV-Ab',
                name: 'HIV Antibody Screen',
                unit: ''
            },
            {
                code: 'HBsAg',
                name: 'Hepatitis B Surface Antigen',
                unit: ''
            },
            {
                code: 'URINE-MC',
                name: 'Urine Microscopy & Culture',
                unit: ''
            }
        ]);
        const code = `${baseCode}-${suffix}`;
        const name = `${baseName} (${suffix})`;
        return {
            code,
            name,
            unit,
            isSensitive: false,
            externalCode: chance.pickone([
                code,
                null
            ]),
            availableFacilities: null
        };
    },
    LabTestPanel: ()=>({
            availableFacilities: null
        }),
    LabRequest: ()=>{
        const status = chance.pickone(Object.values(LAB_REQUEST_STATUSES));
        const isCancelled = status === LAB_REQUEST_STATUSES.CANCELLED;
        return {
            status,
            reasonForCancellation: isCancelled ? chance.pickone([
                'duplicate',
                'entered-in-error'
            ]) : null
        };
    },
    LabTest: ()=>({
            referenceRangeMin: null,
            referenceRangeMax: null
        }),
    Patient: ()=>{
        const sex = chance.pickone(Object.values(SEX_VALUES));
        const nameGender = sex === 'male' || sex === 'female' ? sex : chance.pickone([
            'male',
            'female'
        ]);
        return {
            displayId: chance.hash({
                length: 4
            }).toUpperCase().concat(chance.integer({
                min: 10000000,
                max: 99999999
            }).toString()),
            sex,
            firstName: chance.first({
                gender: nameGender
            }),
            middleName: chance.first({
                gender: nameGender
            }),
            lastName: chance.last(),
            culturalName: chance.first({
                gender: nameGender
            }),
            dateOfBirth: toDateString(chance.birthday({
                type: 'adult'
            })),
            dateOfDeath: null,
            email: chance.email()
        };
    },
    PatientAdditionalData: ({ id, patientId })=>{
        const commonId = id || patientId || fakeUUID();
        return {
            id: commonId,
            patientId: commonId,
            placeOfBirth: chance.city(),
            bloodType: chance.pickone(Object.values(BLOOD_TYPES)),
            primaryContactNumber: chance.phone(),
            secondaryContactNumber: chance.phone(),
            maritalStatus: chance.pickone(Object.values(MARITAL_STATUS_VALUES)),
            cityTown: chance.city(),
            streetVillage: chance.street(),
            educationalLevel: chance.pickone(Object.values(EDUCATIONAL_ATTAINMENT_TYPES)),
            socialMedia: `@${chance.first().toLowerCase()}${chance.last().toLowerCase()}${chance.integer({
                min: 1,
                max: 99
            })}`,
            title: chance.pickone(Object.values(TITLES)),
            birthCertificate: `BC${chance.natural({
                min: 1000000,
                max: 9999999
            })}`,
            drivingLicense: `L${chance.natural({
                min: 100000,
                max: 999999
            })}`,
            passport: chance.character({
                alpha: true
            }).toUpperCase() + chance.natural({
                min: 10000000,
                max: 99999999
            }).toString(),
            emergencyContactName: chance.name(),
            emergencyContactNumber: chance.phone(),
            secondaryVillageId: null,
            updatedAtByField: null
        };
    },
    PatientFacility: ({ patientId = fakeUUID(), facilityId = fakeUUID() })=>{
        return {
            id: `${patientId};${facilityId}`,
            patientId,
            facilityId
        };
    },
    PatientDeathData: ()=>{
        const options = [
            'yes',
            'no',
            'unknown',
            null
        ];
        return {
            wasPregnant: chance.pickone(options),
            pregnancyContributed: chance.pickone(options),
            recentSurgery: chance.pickone(options),
            stillborn: chance.pickone(options),
            autopsyRequested: chance.pickone(options),
            autopsyFindingsUsed: chance.pickone(options),
            multiplePregnancy: chance.pickone(options)
        };
    },
    PatientProgramRegistration: ({ patientId, programRegistryId })=>({
            id: `${patientId.replaceAll(';', ':')};${programRegistryId.replaceAll(';', ':')}`,
            registrationStatus: REGISTRATION_STATUSES.ACTIVE
        }),
    Prescription: ()=>({
            frequency: chance.pickone(Object.values(ADMINISTRATION_FREQUENCIES)),
            discontinued: false,
            endDate: null,
            idealTimes: null
        }),
    User: ()=>({
            email: chance.email({
                length: 20
            }),
            displayId: chance.hash({
                length: 5
            }),
            displayName: chance.name(),
            role: 'practitioner'
        }),
    ReferenceData: ()=>({
            type: chance.pickone(REFERENCE_TYPE_VALUES),
            availableFacilities: null
        }),
    Role: ()=>({
            name: `${snakeCase(chance.profession())}_${chance.hash({
                length: 8
            })}`
        }),
    Survey: ()=>({
            isSensitive: false,
            visibilityCriteria: null,
            notifyEmailAddresses: []
        }),
    SurveyScreenComponent: ()=>({
            calculation: null,
            visibilityCriteria: null,
            config: null,
            options: null
        }),
    Encounter: ()=>({
            encounterType: chance.pickone(ENCOUNTER_TYPE_VALUES)
        }),
    Note: ()=>({
            // This is a hack because the type of Note.id is UUID, whereas tests might create ids of the form:
            // Note.id.123e4567-e89b-12d3-a456-426614174000
            // Setting id: undefined allows the model to create a default uuid and therefore avoid erroring
            // It will be fixed properly as part of EPI-160
            id: undefined,
            noteTypeId: chance.pickone(NOTE_TYPE_VALUES),
            revisedById: undefined
        }),
    Location: ()=>({
            maxOccupancy: chance.pickone([
                1,
                null
            ])
        }),
    ProgramRegistry: ()=>({
            currentlyAtType: chance.pickone(Object.values(CURRENTLY_AT_TYPES))
        }),
    AppointmentSchedule: ()=>{
        const frequency = chance.pickone(REPEAT_FREQUENCY_VALUES);
        const endsMode = chance.pickone([
            'on',
            'after'
        ]);
        return {
            frequency,
            daysOfWeek: [
                chance.pickone(DAYS_OF_WEEK)
            ],
            nthWeekday: frequency === REPEAT_FREQUENCY.MONTHLY ? chance.integer({
                min: -1,
                max: 4
            }) : null,
            ...endsMode === 'on' ? {
                untilDate: fakeDateTimeString()
            } : {
                occurrenceCount: chance.integer({
                    min: 1,
                    max: 99
                })
            }
        };
    },
    ChangeLog: ()=>({
            recordId: fakeUUID(),
            id: fakeUUID(),
            tableOid: chance.integer({
                min: 10000,
                max: 99999
            }),
            tableSchema: chance.pickone([
                'public',
                'fhir',
                'logs'
            ]),
            tableName: chance.pickone([
                'patients',
                'encounters',
                'lab_requests',
                'lab_tests',
                'imaging_requests',
                'survey_responses',
                'survey_response_answers',
                'administered_vaccines',
                'encounter_diagnoses',
                'prescriptions',
                'reference_data',
                'users',
                'patient_additional_data',
                'notes',
                'appointments',
                'locations'
            ]),
            loggedAt: fakeDateTimeString(),
            recordCreatedAt: fakeDateTimeString(),
            recordUpdatedAt: fakeDateTimeString(),
            updatedByUserId: fakeUUID(),
            recordUpdate: true
        }),
    SurveyResponse: ()=>({
            // editedTime implies “patched after original survey response submission”; must start as NULL
            editedTime: null
        }),
    SurveyResponseAnswer: ()=>({
            // editedTime implies “patched after original survey response submission”; must start as NULL
            editedTime: null
        })
};
const fhirArray = (fakeFn)=>(...args)=>chance.n(()=>fakeFn(...args), chance.integer({
            min: 0,
            max: 3
        }));
const FHIR_MODELS_HANDLERS = {
    FhirPatient: {
        identifier: fhirArray((...args)=>FhirIdentifier.fake(...args)),
        name: fhirArray((...args)=>FhirHumanName.fake(...args)),
        telecom: fhirArray((...args)=>FhirContactPoint.fake(...args)),
        address: fhirArray((...args)=>FhirAddress.fake(...args)),
        link: fhirArray((...args)=>FhirPatientLink.fake(...args)),
        extension: fhirArray((...args)=>FhirExtension.fake(...args))
    },
    FhirServiceRequest: {
        identifier: fhirArray((...args)=>FhirIdentifier.fake(...args)),
        category: fhirArray((...args)=>FhirCodeableConcept.fake(...args)),
        order_detail: fhirArray((...args)=>FhirCodeableConcept.fake(...args)),
        location_code: fhirArray((...args)=>FhirCodeableConcept.fake(...args)),
        code: (...args)=>FhirCodeableConcept.fake(...args),
        subject: (...args)=>FhirReference.fake(...args),
        requester: (...args)=>FhirReference.fake(...args)
    },
    FhirDiagnosticReport: {
        extension: fhirArray((...args)=>FhirExtension.fake(...args)),
        identifier: fhirArray((...args)=>FhirIdentifier.fake(...args)),
        code: (...args)=>FhirCodeableConcept.fake(...args),
        subject: (...args)=>FhirReference.fake(...args),
        performer: fhirArray((...args)=>FhirReference.fake(...args)),
        result: fhirArray((...args)=>FhirReference.fake(...args))
    },
    FhirImmunization: {
        vaccine_code: (...args)=>FhirCodeableConcept.fake(...args),
        patient: (...args)=>FhirReference.fake(...args),
        encounter: (...args)=>FhirReference.fake(...args),
        site: fhirArray((...args)=>FhirCodeableConcept.fake(...args)),
        performer: fhirArray((...args)=>FhirImmunizationPerformer.fake(...args)),
        protocol_applied: fhirArray((...args)=>FhirImmunizationProtocolApplied.fake(...args))
    },
    FhirImagingStudy: {
        identifier: fhirArray((...args)=>FhirIdentifier.fake(...args)),
        basedOn: fhirArray((...args)=>FhirReference.fake(...args)),
        note: fhirArray((...args)=>FhirAnnotation.fake(...args))
    }
};
export const fake = (model, passedOverrides = {})=>{
    const id = fakeUUID();
    const record = {};
    const modelOverridesFn = MODEL_SPECIFIC_OVERRIDES[model.name];
    const modelOverrides = modelOverridesFn ? modelOverridesFn(passedOverrides) : {};
    const overrides = {
        ...modelOverrides,
        ...passedOverrides
    };
    const overrideFields = Object.keys(overrides);
    function fakeField(name, attribute) {
        const { type, fieldName, defaultValue } = attribute;
        if (overrideFields.includes(fieldName)) {
            return overrides[fieldName];
        }
        if (attribute.references) {
            // null out id fields
            return null;
        }
        if (IGNORED_FIELDS.includes(fieldName)) {
            // ignore metadata fields
            return undefined;
        }
        if (fieldName === 'id') {
            return fakeUUID();
        }
        if (fieldName === 'visibilityStatus') {
            return VISIBILITY_STATUSES.CURRENT;
        }
        if (type instanceof Sequelize.ARRAY && type.type) {
            return Array(chance.integer({
                min: 0,
                max: 3
            })).fill(0).map(()=>fakeField(name, {
                    ...attribute,
                    type: type.type
                }));
        }
        if (defaultValue) {
            if (defaultValue === Sequelize.NOW || defaultValue === Sequelize.UUIDV4) {
                return undefined;
            }
            return isFunction(defaultValue) ? defaultValue() : defaultValue;
        }
        if (type instanceof Sequelize.BLOB) {
            return Buffer.from('test');
        }
        if (FIELD_HANDLERS[type]) {
            return FIELD_HANDLERS[type](model, attribute, id);
        }
        if (type.type && FIELD_HANDLERS[type.type]) {
            return FIELD_HANDLERS[type.type](model, attribute, id);
        }
        if (type instanceof Sequelize.STRING && type.options.length) {
            return FIELD_HANDLERS['VARCHAR(N)'](model, attribute, id, type.options.length);
        }
        if (type?.key === 'JSONB' && FHIR_MODELS_HANDLERS[model.name]?.[fieldName]) {
            return FHIR_MODELS_HANDLERS[model.name][fieldName](model, attribute, id);
        }
        if (type?.key === 'JSONB') {
            return {
                test: 'test'
            };
        }
        // if you hit this error, you probably need to add a new field handler or a model-specific override
        throw new Error(`Could not fake field ${model.name}.${name} of type ${type} / ${type.type} / ${inspect(type)}`);
    }
    for (const [name, attribute] of Object.entries(model.getAttributes())){
        const fakeValue = fakeField(name, attribute);
        if (fakeValue !== undefined) record[name] = fakeValue;
    }
    return record;
};

//# sourceMappingURL=fake.js.map