var Q = require('q');
var fhirAssertions = require('@blackpear/fhir-assertions');
var httpMocks = require('node-mocks-http');
var fs = require('fs');

var slotStateTransition = "none";
var patientIdForSlotReservation = "none";
var adhocPatient = "9999999991";

var now = new Date;
var nowISO = now.toISOString();
var today = nowISO.split('T')[0] + 'T00:00:00.000Z';

var DEFAULT_DATA_ACCESS_META_EXTENSION = {
    "extension": [
        {
            "extension": [
                {
                    "valueReference": {
                        "identifier": {
                            "value": "0facd10a-33fc-42e4-a50e-fdf728b0cc32",
                            "system": "https://fhir.blackpear.com/access/service-identifier-system"
                        }
                    },
                    "url": "actor"
                }
            ],
            "url": "https://fhir.hl7.org.uk/STU3/StructureDefinition/Extension-CareConnect-DataProcessor-1"
        },
        {
            "valueReference": {
                "identifier": {
                    "value": "0facd10a-33fc-42e4-a50e-fdf728b0cc32",
                    "system": "https://fhir.blackpear.com/access/service-identifier-system"
                }
            },
            "url": "https://fhir.hl7.org.uk/STU3/StructureDefinition/Extension-CareConnect-DataController-1"
        }
    ]
};

var dataAccessMetaExtension;

function addDataAccessMetaExtensions(body) {

    if (!dataAccessMetaExtension) {
        return;
    }

    if (body.resourceType === 'Bundle') {
        body.entry.forEach(function(entry) {
            entry.content.meta = dataAccessMetaExtension;
        })
    }
    else {
        body.meta = dataAccessMetaExtension;
    }
};

function getRequestHandler(url) {

    function getResource(path){
        var data = fs.readFileSync(__dirname + '/' + path, {format: 'utf8'});
        return JSON.parse(data);
    }

    var filepath = null;

    //
    // for search type operations we should reduce the number of data files used by adding
    // filtering code on slot data? e.g.
    //slots = res.body.entry.filter(function (item) {
    //    return item.content.freeBusyType === 'free';
    // })
    //
    switch (url) {
        case "/Organization":
            filepath = "Organization/response.json";
            break;
        case "/Organization?_count=1000":
            filepath = "Organization/response.json";
            break;
        case "/Organization?identifier=UCC001":
            filepath = "Organization/response.identifier.UCC001.json";
            break;
        case "/Organization?identifier=XXX":
            filepath = "Organization/response.identifier.XXX.json";
            break;
        case "/Organization?identifier=XXXXX":
            filepath = "Organization/response.identifier.XXXXX.json";
            break;
        case "/Organization?identifier=GP0001":
            filepath = "Organization/response.identifier.GP0001.json";
            break;
        case "/Organization/UCC001":
            filepath = "Organization/response.UCC001.json";
            break;
        case "/ValueSet":
            filepath = "ValueSet/response.json";
            break;
        case "/Slot":
            if (slotStateTransition === "none") {
                filepath = "Slot/response.json";
            }
            else if (slotStateTransition === "free=>busy-tentative") {
                filepath = "Slot/response.reserve.json";
            }
            else if (slotStateTransition === "busy-tentative=>free") {
                filepath = "Slot/response.release.json";
            }
            else if (slotStateTransition === "busy-tentative=>busy") {
                filepath = "Slot/response.book.json";
            }
            slotStateTransition = "none";
            break;

        case "/Slot?provider=UCC001":
            filepath = "Slot/response.provider.UCC001.json";
            break;
        case "/Slot?provider=UCC002":
            filepath = "Slot/response.provider.UCC002.json";
            break;
        case "/Slot?start=>=2015-05-15T08:45:00Z":
            filepath = "Slot/response.start.2015-05-15.json";
            break;
        case "/Slot?start=>=2015-05-15T08:45:00Z&start=<2015-05-15T09:00:00Z":
            filepath = "Slot/response.start.2015-05-15.range.json";
            break;
        case "/Slot?start=<" + today + "&_count=5000": // reqd for slot sync
            filepath = "Slot/response.start.2016-02-16.json";
            break;
        case "/Slot?slottype=http://www.e-mis.com/emisopen/MedicalRecord/Slot/Type?assigner:Organization=UCC001|1":
            filepath = "Slot/response.slottype.emis.json";
            break;
        case "/Slot?identifier=http://www.e-mis.com/emisopen/MedicalRecord/Slot/Id|183744":
            filepath = "Slot/response.identifier.183744.json";
            break;
        case "/Slot?identifier=http://www.e-mis.com/emisopen/MedicalRecord/Slot/Id|9999999":
            filepath = "Slot/response.identifier.9999999.json";
            break;
        case "/Slot?identifier=http://www.e-mis.com/emisopen/MedicalRecord/Slot/Id|183747":
            filepath = "Slot/response.identifier.18374.json";
            break;
        case "/Slot?slottype:text=Home Visit":
            filepath = "Slot/response.slottype.homevisit.json";
            break;
        case "/Slot?fbtype=free":
            filepath = "Slot/response.fbtype.free.json";
            break;
        case "/Slot?fbtype=busy-tentative&_count=100":
            filepath = "Slot/response.fbtype.busy-tentative.json";
            break;
        case "/Slot/56b9be56f275d02e5892746b": // busy-tentative slot
            filepath = "Slot/response.56b9be56f275d02e5892746b.json";
            break;
        case "/Slot/56b9be56f275d02e5892746d": // busy slot
            filepath = "Slot/response.56b9be56f275d02e5892746d.json";
            break;
        case "/Slot/56b9be56f275d02e58927471": // free slot. Also used for $cancel appointment test
            if (patientIdForSlotReservation === adhocPatient) {
                if (slotStateTransition === "free=>busy-tentative") {
                    filepath = "Slot/response.56b9be56f275d02e58927471.reserve.adhoc.json";
                }
                else if (slotStateTransition === "busy-tentative=>busy") {
                    filepath = "Slot/response.56b9be56f275d02e58927471.book.adhoc.json";
                }
                else {
                    filepath = "Slot/response.56b9be56f275d02e58927471.json";
                }
                patientIdForSlotReservation = "none";
            }
            else {
                if (slotStateTransition === "free=>busy-tentative") {
                    filepath = "Slot/response.56b9be56f275d02e58927471.reserve.json";
                }
                else if (slotStateTransition === "busy-tentative=>busy") {
                    filepath = "Slot/response.56b9be56f275d02e58927471.book.json";
                }
                else {
                    filepath = "Slot/response.56b9be56f275d02e58927471.json";
                }
            }
            slotStateTransition = "none";
            break;
        case "/Slot/56ef39d8ba0354e404aa51b4":
            filepath = "Slot/response.56ef39d8ba0354e404aa51b4.json";
            break;
        case "/Slot/56ef39d8ba0354e404aa5acd":
            filepath = "Slot/response.56ef39d8ba0354e404aa5acd.json";
            break;
        case "/Appointment?actor:Organization=UCC001":
            filepath = "Appointment/response.json";
            break;
        case "/Appointment?start=>2014-07-09T09:00:00Z&actor:Organization=UCC001":
            filepath = "Appointment/response.start.2014-07-09.json";
            break;
        case "/Appointment?start=>=2014-07-09T09:00:00Z&start=<2014-07-09T09:45:00Z&actor:Organization=UCC001":
            filepath = "Appointment/response.start.2014-07-09.range.json";
            break;
        case "/Appointment?actor:Organization=B12345&actor:Organization=UCC001":
            filepath = "Appointment/response.actor.B12345.json";
            break;
        case "/Appointment/56bcc6a6a1ef27360b93cfa8":
            filepath = "Appointment/response.56bcc6a6a1ef27360b93cfa8.json";
            break;
        case "/Appointment?identifier=http://www.e-mis.com/emisopen/MedicalRecord/Slot/Id|183745":
            filepath = "Appointment/response.identifier.183745.json";
            break;
        case "/Appointment?identifier=http://www.e-mis.com/emisopen/MedicalRecord/Slot/Id|104119":
            filepath = "Appointment/response.identifier.104119.json";
            break;
        case "/Appointment?identifier=http://www.e-mis.com/emisopen/MedicalRecord/Slot/Id|9999999":
            filepath = "Appointment/response.identifer.9999999.json";
            break;
        case "/Appointment?identifier=http://www.e-mis.com/emisopen/MedicalRecord/Slot/Id|183747":
            filepath = "Appointment/reponse.identifier.183747.json";
            break;
        case "/Appointment/56548eb5c59579ab3aaaf292":
            filepath = "Appointment/response.56548eb5c59579ab3aaaf292.json";
            break;
        case "/Appointment/5655d1fcc59579ab3aaaf9a0":
            filepath = "Appointment/response.5655d1fcc59579ab3aaaf9a0.json";
            break;
        case "/Patient/56bcc6a6a1ef27360b93cf8e":
            filepath = "Patient/response.56bcc6a6a1ef27360b93cf8e.json";
            break;
        case "/Patient/56f3dc9cc0f3bde30ba268dc":
            filepath = "Patient/response.56f3dc9cc0f3bde30ba268dc.json";
            break;
        case "/Patient/56ebeccd141737bded6419f9":
            filepath = "Patient/response.56ebeccd141737bded6419f9.json";
            break;
        case "/Patient?identifier=5104721124":
            filepath = "Patient/response.identifier.5104721124.json";
            break;
        case "/Patient?identifier=000000000":
            filepath = "Patient/response.identifier.000000000.json";
            break;
        case "/Patient?birthdate=1986-07-23":
            filepath = "Patient/response.birthdate.1986-07-23.json";
            break;
        case "/Patient?name.family=Caldas De Moura Duarte":
            filepath = "Patient/response.name.family.json";
            break;
        case "/ReferralRequest?patient=56bcc6a6a1ef27360b93cf8e":
            filepath = "ReferralRequest/response.patient.56bcc6a6a1ef27360b93cf8e.json";
            break;
        case "/ReferralRequest?patient=56f3dc9cc0f3bde30ba268dc":
            filepath = "ReferralRequest/response.patient.56f3dc9cc0f3bde30ba268dc.json";
            break;
        case "/ReferralRequest?patient=56ebeccd141737bded6419f9":
            filepath = "ReferralRequest/response.patient.56ebeccd141737bded6419f9.json";
            break;
    }

    var template = {};
    if (filepath) {
        template.status = 200;

        template.body = getResource('../data/pyrusCloud/eRA/' + filepath);
        addDataAccessMetaExtensions(template.body);

        template.headers = {
            'content-location' : null // needs to exist for slot operations, value unimportant
        }
    }
    else {
        template.status = 404;
    }

    return template;
}

function postRequestHandler(url, data) {
    var dummyId = "56bcb2d3a1ef27360b93cdc5"; // shouldn't matter what this is

    var regEx = /^\/(Patient|ReferralRequest|Appointment|Organization)/;
    var resourceType = url.match(regEx)[1];

    var template = {};
    template.headers = {
        'location': 'http://127.0.0.1:3001/fhir/' + resourceType + '/' + dummyId + '/_history/0'
    };

    var isTagging = url.match(/\/(_tags)(\/_delete)?$/);
    if (isTagging) {
        template.status = 200;
    }
    else {
        template.status = 201;
    }

    return template;

}

function putRequestHandler(url, data) {

    function getPatientId(content) {
        var isReservedForPatientExtension = fhirAssertions.assert.extension.hasUrl('http://fhir.blackpear.com/extensions/era#reservedForPatient');
        var match = content.extension.filter(isReservedForPatientExtension);

        if (match) {
            return match[0].valueIdentifier.value;
        }
    }

    //
    // NB can we reduce this code to be Slot ID agnostic as state transition is implied from data.freeBusyType
    // apart from 'free' which can be reached from busy-tentative OR busy but currently tests don't do any checking
    // of slot state once an appointment is cancelled so busy=>free transition doesn't need to be recorded.
    //
    switch(url) {
        case "/Slot/56b9be56f275d02e5892746b":
            if (data.freeBusyType === 'free') { // busy-tentative->free transition
                slotStateTransition = "busy-tentative=>free";
            }
            else if (data.freeBusyType === 'busy') { // busy-tentative->busy transition
                slotStateTransition = "busy-tentative=>busy";
            }
            else {
                throw err;
            }
            break;

        case "/Slot/56b9be56f275d02e58927471": // free->busy-tentative transition
            if (data.freeBusyType === 'busy-tentative') {
                slotStateTransition = "free=>busy-tentative";
                patientIdForSlotReservation = getPatientId(data);
            }
            else if (data.freeBusyType === 'busy') { // busy-tentative->busy transition
                slotStateTransition = "busy-tentative=>busy";
                patientIdForSlotReservation = getPatientId(data);
            }
            else if (data.freeBusyType === 'free') { // busy=>free transition
                slotStateTransition = "none";
            }
            else {
                throw err;
            }
            break;
    }
    return {status:200};
}

function delRequestHandler(url) {
    return {status:200};

}

function processRequest(method, url, data){

    var decodedURL = decodeURIComponent(url); // NB need decode because of '>' & ':' appearing in paths

    var template;
    switch (method) {
        case ('get'):
            template = getRequestHandler(decodedURL);
            break;
        case('post'):
            template = postRequestHandler(decodedURL);
            break;
        case('put'):
            template = putRequestHandler(decodedURL, data);
            break;
        case('del'):
            template = delRequestHandler(decodedURL);
            break;
    }

    var response = httpMocks.createResponse();
    response.body = template.body || undefined;

    // eRA API uses status but also need http statusCode.
    response.status = template.status || 500;
    response.statusCode = response.status;

    response.header = template.headers  || undefined;
    if (response.body)
        response.header['content-length'] = response.body.length;

    // An express app will return both res.header & res.headers, both are used in eRA API
    // so need to set both here too!  e2e test superagent queries res.header
    response.headers = response.header;

    return response;
}

function pyrusCloud(options){

    options = options || {};
    dataAccessMetaExtension = options.dataAccessMetaExtension || DEFAULT_DATA_ACCESS_META_EXTENSION;

    return {
        get: function(url){
            var response = processRequest('get', url);

            if (response.statusCode !== 200){
                var err = new Error();
                err.status = response.statusCode;
                err.message = "cannot GET http://127.0.0.1:3001/fhir" + url + " (" + err.status + ")";
                return Q.reject(err);
            }
            return Q(response);

        },

        post: function(url, data) {
            var response = processRequest('post', url);

            if (response.statusCode > 201){
                var err = new Error();
                err.status = response.statusCode;
                err.message = "cannot POST http://127.0.0.1:3001/fhir" + url + " (" + err.status + ")";
                return Q.reject(err);
            }
            return Q(response);

        },

        put: function(url, data, location) {
            return Q(processRequest('put', url, data));
        },

        del: function(url) {
            return Q(processRequest('del', url));
        }
    }
}

module.exports = pyrusCloud;
