const fs = require('fs');
const path = require('path');
const moment = require('moment');
const _ = require('lodash'); // TODO
const log = require('loglevel');
const formData = require('form-data');

const constants = require('../models/constants');
const constantsXml = require('../models/constants-xml');
const enums = require('../models/enums');
const enumsXml = require('../models/enums-xml');
const countries = require('../files/countries.json');

const AccountHolderServ = require('../services/account-holder');

const AccountPermissionServ = require('../services/account-permission');
const ReasonsServ = require('../services/reasons');
const PosTransactionCtrl = require('./pos-transaction');
const FareEnvironmentConfiguration = require('../models/fare-environment-configuration');
const FareTable = require('../models/fare-table');

class AccountHolderCtrl {
    constructor () {
        this.service = new AccountHolderServ();

        this.accountPermissionServ = new AccountPermissionServ();
        this.reasonsServ = new ReasonsServ();
        this.posTransactionCtrl = new PosTransactionCtrl();
        this.fareEnvironmentModel = FareEnvironmentConfiguration;
        this.fareTableModel = FareTable;
        this.dateTTimeFormat = 'YYYY-MM-DD[T]HH:mm:ss';
        this.dateTimeFormat = 'DD/MM/YYYY HH:mm:ss';
        this.dateRFormat = 'YYYY-MM-DD';
        this.dateFormat = 'DD/MM/YYYY';

        this.searchBase = (['oman'].includes(process.env.CLIENT_NAME)) ? true : false;
    }

    search = async (req, res) => {
        try {
            const value = req.body.value;

            let accountHoldersList = [];
            const accountHolders = (this.searchBase) ? await this.service.searchBase(value) : await this.service.search(value);
            if (accountHolders) {
                const accountHoldersLen = accountHolders.length;
                for (let i=0; i<accountHoldersLen; i++) {
                    const accountHolderOwner = (this.searchBase) ? accountHolders[i].linkedAccounts[0] : accountHolders[i].linkedAccounts.find((f) => f.isOwner == true);
                    accountHoldersList.push({
                        id: accountHolders[i].uid,
                        phone: accountHolders[i].phoneNumber,
                        email: accountHolders[i].emailAddress,
                        name: accountHolders[i].fullName,
                        shortId: (accountHolderOwner) ? accountHolderOwner.shortId : 0,
                        accountIntegrityId: (accountHolderOwner) ? accountHolderOwner.uid : 0,
                    });
                }
            }
            res.status(200).json(accountHoldersList);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getAccount = async (req, res) => {
        try {
            const accountId = req.query.id;

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const riderTypes = await this.fareTableModel.RiderTypes.find({fareEnvironmentConfigurationId: activeConfig._id});
            const accountDetails = await this.service.getAccountDetails(accountId);
            const accountDetailsSecond = await this.service.getAccountDetailsSecond(accountId);
            const accountOwner = accountDetails.linkedAccounts.find((f) => f.isOwner == true);
            const accountContracts = await this.service.getAccountContracts(accountOwner.uid);

            let account = {};

            if (accountDetails) {
                // setBasicAccountDetails //
                account.name = accountDetails.fullName;
                account.address = accountDetails.addressLine1;
                account.address2 = accountDetails.addressLine2;
                account.country = (accountDetails.country) ? accountDetails.country.toUpperCase() : '';

                const country = countries.countries.find((f) => f.code == account.country);

                account.countryFull = (country) ? country.translations['en-GB'] : '';

                account.email = accountDetails.emailAddress;
                account.city = accountDetails.city;
                account.phone = accountDetails.phoneNumber;
                account.id = accountDetails.uid;
                account.version = accountDetails.version;
                account.title = accountDetails.personType;
                account.gender = accountDetails.personGender;
                if (accountDetails.dayOfBirth) {
                    account.birthday = moment.utc(accountDetails.dayOfBirth).format(this.dateFormat);
                }
                // setBasicAccountDetails //

                const linkedAccounts = accountDetails.linkedAccounts.filter((f) => f.isOwner == false);
                if (linkedAccounts) {
                    account.linkedAccounts = [];
                    const permissions = await this.accountPermissionServ.accountPermissions();

                    let linkedAccountsLen = linkedAccounts.length;
                    for (let i=0; i<linkedAccountsLen; i++) {
                        const riderType = riderTypes.find((f) => f.externalId == linkedAccounts[i].riderTypeId);
                        const permission = permissions.find((f) => f.uid == linkedAccounts[i].permissionUid);

                        account.linkedAccounts.push({
                            shortId: linkedAccounts[i].shortId,
                            type: (riderType) ? riderType.name : '',
                            status: (accountOwner.statusType == constantsXml.blockStatusType.notBlocked) ? 'Active' : accountOwner.statusType,
                            name: linkedAccounts[i].fullName,
                            permission: (permission) ? permission.name : '',
                            permissionLevel: permission.level,
                            id: linkedAccounts[i].uid,
                            accountIntegrityId: linkedAccounts[i].accountHolderUid,
                            statusLink: linkedAccounts[i].status
                        });
                    }
                }

                const linkedAccountsSecond = accountDetailsSecond;
                if (linkedAccountsSecond) {
                    account.linkedAccountsSecond = [];
                    const permissions = await this.accountPermissionServ.accountPermissions();

                    let linkedAccountsSecondLen = linkedAccountsSecond.length;
                    for (let i=0; i<linkedAccountsSecondLen; i++) {
                        const riderType = riderTypes.find((f) => f.externalId == linkedAccountsSecond[i].riderTypeId);
                        const permission = permissions.find((f) => f.uid == linkedAccountsSecond[i].permissionUid);

                        account.linkedAccountsSecond.push({
                            shortId: linkedAccountsSecond[i].shortId,
                            type: (riderType) ? riderType.name : '',
                            status: (accountOwner.statusType == constantsXml.blockStatusType.notBlocked) ? 'Active' : accountOwner.statusType,
                            name: linkedAccountsSecond[i].fullName,
                            permission: (permission) ? permission.name : '',
                            permissionLevel: permission.level,
                            id: linkedAccountsSecond[i].uid,
                            accountIntegrityId: linkedAccountsSecond[i].accountHolderUid,
                            statusLink: linkedAccountsSecond[i].status
                        });
                    }
                }

                if (accountDetails.smartCards) {
                    account.smartCards = [];
                    for (let i=0; i<accountDetails.smartCards.length; i++) {
                        account.smartCards.push({
                            uid: accountDetails.smartCards[i].uid,
                            printedId: accountDetails.smartCards[i].printedId,
                            blockStatus: (accountDetails.smartCards[i].status == constantsXml.blockStatusType.notBlocked) ? 'Active' : accountDetails.smartCards[i].status,
                            identifierMediaType: accountDetails.smartCards[i].type,
                            validFrom: (accountDetails.smartCards[i].issuingDateTime) ? moment.utc(accountDetails.smartCards[i].issuingDateTime).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat): '',
                            validTo: (accountDetails.smartCards[i].expiryDate) ? moment.utc(accountDetails.smartCards[i].expiryDate).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat) : ''
                        });
                    }
                }

                if (accountOwner) {
                    const riderType = riderTypes.find((f) => f.externalId == accountOwner.riderTypeId);

                    account.isGroup = (riderType) ? riderType.isGroupFare : false;
                    account.shortId = accountOwner.shortId;
                    account.accountIntegrityId = accountOwner.uid;
                    account.riderTypeId = accountOwner.riderTypeId;
                    account.targetRiderTypeId = accountOwner.riderTypeId;
                    account.type = (riderType) ? riderType.name.toUpperCase() : '';
                    account.credit = accountOwner.storedValue / Math.pow(10, global.currentPosData.currencyDecimal);
                    account.blocked = (accountOwner.statusType == constantsXml.blockStatusType.blocked);
                    account.linkedAccountNum = linkedAccounts.length;
                    account.status = (accountOwner.statusType == constantsXml.blockStatusType.notBlocked) ? 'Active' : accountOwner.statusType;

                    if (accountContracts) {
                        account.contracts = [];
                        account.contractsNum = accountContracts.length;

                        for (let i=0; i<accountContracts.length; i++) {
                            account.contracts.push({
                                id: accountContracts[i].contractId,
                                instanceId: accountContracts[i].instanceId,
                                name: accountContracts[i].fareItemName,
                                nameTranslated: accountContracts[i].fareItemNameTranslations.find((f) => f.languageCode == global.currentPosData.secondLanguage.split('-')[0]).translation,
                                type: accountContracts[i].fareProductType,
                                status: accountContracts[i].fareItemStatusType,
                                validFrom: (accountContracts[i].creationTimestamp) ? moment.utc(accountContracts[i].creationTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat): '',
                                validTo: (accountContracts[i].expirationTimestamp) ? moment.utc(accountContracts[i].expirationTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat) : '',
                                numberOfRides: (accountContracts[i].remainingRides) ? accountContracts[i].remainingRides : 0
                            });
                        }
                    }

                    if (accountOwner.purchases) {
                        const tickets = accountOwner.purchases.filter((f) => f.fareItemType != constantsXml.fareItemType.creditEpurseAccount);

                        account.paymentsNum = (accountOwner.purchases).length;
                        account.ticketsNum = tickets.length;

                        const transactions = await this.setTransactions(accountOwner.purchases);
                        account.purchases = {};
                        for (let i = 0; i < 12; i++) {
                            let date = moment.utc().subtract(i, 'months').format('MMMM YYYY').toString();

                            account.purchases[date] = transactions.filter((f) => (moment.utc(f.transactionDate).format('MMMM') == date.split(' ')[0]) && (moment.utc(f.transactionDate).format('YYYY') == date.split(' ')[1]));
                        }

                        account.transactionTypes = transactions.map((m) => m.transactionType).filter((v, i, s) => s.indexOf(v) === i);
                        account.purchasesMonths = Object.keys(account.purchases);

                        const ticketsModel = await this.setTickets(tickets, account.shortId);

                        account.activeTickets = ticketsModel.activeTickets;
                        account.usedTickets = ticketsModel.usedTickets;
                    }

                    if (accountOwner.identifiers) {
                        account.identifiers = [];
                        let identifiersLen = accountOwner.identifiers.length;
                        for (let i=0; i<identifiersLen; i++) {
                            account.identifiers.push({
                                uid: accountOwner.identifiers[i].uid,
                                version: accountOwner.identifiers[i].version,
                                expiryTimestamp: moment.utc(accountOwner.identifiers[i].expiryTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                                blockStatus: accountOwner.identifiers[i].blockStatus,
                                isPermanentBlock: accountOwner.identifiers[i].isPermanentBlock,
                                blockReasonId: accountOwner.identifiers[i].blockReasonId,
                                identifierMediaType: accountOwner.identifiers[i].identifierMediaType,
                                validFrom: (accountOwner.identifiers[i].validFrom) ? moment.utc(accountOwner.identifiers[i].validFrom).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat): '',
                                validTo: (accountOwner.identifiers[i].validTo) ? moment.utc(accountOwner.identifiers[i].validTo).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat) : ''
                            });
                        }
                    }

                    if (accountOwner.accessDevices) {
                        account.devices = [];

                        let accessDevicesLen = accountOwner.accessDevices.length;
                        for (let i=0; i<accessDevicesLen; i++) {
                            if (accountOwner.accessDevices[i].accessDeviceType == 'MOBILE') {
                                account.devices.push({
                                    id: accountOwner.accessDevices[i].uid,
                                    name: `${accountOwner.accessDevices[i].name} ${accountOwner.accessDevices[i].deviceModel}`,
                                    os: accountOwner.accessDevices[i].operatingSystem,
                                    status: !accountOwner.accessDevices[i].isBlocked ? 'Active' : 'Blocked',
                                    blocked: accountOwner.accessDevices[i].isBlocked,
                                    lastAccessDate: moment.utc(accountOwner.accessDevices[i].lastAccessTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat)
                                });
                            }
                        }

                        account.devicesNum = account.devices.length;
                    }

                    if (accountOwner.fines) {
                        account.penalties = [];

                        let accountPenaltiesLen = accountOwner.fines.length;
                        for (let i=0; i<accountPenaltiesLen; i++) {
                            account.penalties.push({
                                riderTypeExternalId: accountOwner.fines[i].riderTypeExternalId,
                                riderTypeName: (riderType) ? riderType.name : constants.defaults.accountName,
                                riderAccountId: (accountOwner.fines[i].hasOwnProperty('riderAccountId')) ? accountOwner.fines[i].riderAccountId.toString() : '',
                                riderAccountUid: accountOwner.fines[i].riderAccountUid,
                                riderId: accountOwner.fines[i].riderId,
                                riderName: accountOwner.fines[i].riderName,
                                riderEmail: accountOwner.fines[i].riderEmail,
                                riderMobile: accountOwner.fines[i].riderMobile,
                                productId: accountOwner.fines[i].fineProductId,
                                fineProductName: accountOwner.fines[i].fineProductName,
                                issued: moment.utc(accountOwner.fines[i].timestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                                refNumber: accountOwner.fines[i].id,
                                value: accountOwner.fines[i].priceCents / Math.pow(10, global.currentPosData.currencyDecimal),
                                status: accountOwner.fines[i].status,
                                reason: accountOwner.fines[i].reasonName,
                                comment: accountOwner.fines[i].reasonDescription,
                                paymentDate: (accountOwner.fines[i].hasOwnProperty('paymentTimestamp')) ? moment.utc(accountOwner.fines[i].timestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat) : '',
                                riderAddress: accountOwner.fines[i].address,
                                description: accountOwner.fines[i].description
                            });
                        }
                        account.penaltiesNum = accountPenaltiesLen;
                    }
                    // account.penalties = [
                    //     {
                    //         "refNumber": "202106011545",
                    //         "issued": "10/10/2022 11:11:00",
                    //         "reason": "Damage of property",
                    //         "price": 10,
                    //         "priceCent": 10000,
                    //         "comment": "Damage of property",
                    //         "status": "Paid",
                    //         "account": null,
                    //         "paymentDate": "10/10/2022 11:30:00",
                    //         "productId": 102217,
                    //         "issuedDate": "2022-10-10T10:10:00+04:00",
                    //         "fineProductName": "No ticket",
                    //         "riderTypeExternalId": 1,
                    //         "riderTypeName": "Adult",
                    //         "riderAccountId": 364494975,
                    //         "riderAccountUid": "AzjKRIetE32CLwzZi6Luig9qgw4",
                    //         "riderEmail": "klemen.budja@lit-transit.com",
                    //         "riderMobile": null,
                    //         "riderName": "Klemen Budja",
                    //         "riderId": "1",
                    //         "riderAddress": "Planjava 4",
                    //         "description": null
                    //     },
                    //     {
                    //         "refNumber": "9312023310457",
                    //         "issued": "10/10/2022 11:11:00",
                    //         "reason": "Damage of property",
                    //         "price": 10,
                    //         "priceCent": 10000,
                    //         "comment": "Damage of property",
                    //         "status": "Pending",
                    //         "account": null,
                    //         "paymentDate": null,
                    //         "productId": 102217,
                    //         "issuedDate": "2022-10-10T10:10:00+04:00",
                    //         "fineProductName": "No ticket",
                    //         "riderTypeExternalId": 1,
                    //         "riderTypeName": "Adult",
                    //         "riderAccountId": 364494975,
                    //         "riderAccountUid": "AzjKRIetE32CLwzZi6Luig9qgw4",
                    //         "riderEmail": "klemen.budja@lit-transit.com",
                    //         "riderMobile": null,
                    //         "riderName": "Klemen Budja",
                    //         "riderId": "1",
                    //         "riderAddress": "Planjava 4",
                    //         "description": null
                    //     }
                    // ];
                    // account.penaltiesNum = 2;
                }

                if (accountDetails.identities) {
                    account.identities = [];

                    let accountIdentitiesLen = accountDetails.identities.length;
                    for (let i=0; i<accountIdentitiesLen; i++) {
                        account.identities.push({
                            id: accountDetails.identities[i].uid,
                            type: accountDetails.identities[i].accountHolderIdentityType,
                            value: accountDetails.identities[i].value,
                            version: accountDetails.identities[i].version,
                            accountId: accountDetails.identities[i].accountHolderUid,
                            isPrimary: accountDetails.identities[i].isPrimary,
                            password: accountDetails.identities[i].password,
                            changeEventType: ''
                        });
                    }
                }
                account.image = await this.service.getAccountPhoto(account.id);

                account.documentToProveStatus = [];
                account.personalDocument = [];

                if (accountOwner.verificationRequests) {
                    account.allVerificationRequests = [];

                    let accountVerificationRequestsLen = accountOwner.verificationRequests.length;
                    for (let i=0; i<accountVerificationRequestsLen; i++) {

                        let accountVerificationRequest = {
                            uid: accountOwner.verificationRequests[i].uid,
                            description: accountOwner.verificationRequests[i].description,
                            expirationTimestamp: (accountOwner.verificationRequests[i].concessionExpirationTimestamp) ? moment.utc(accountOwner.verificationRequests[i].concessionExpirationTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateFormat) : '',
                            version: accountOwner.verificationRequests[i].version,
                            concessionExpirationTimestamp: (accountOwner.verificationRequests[i].concessionExpirationTimestamp) ? moment.utc(accountOwner.verificationRequests[i].concessionExpirationTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateFormat) : '',
                            lastChangeTime: (accountOwner.verificationRequests[i].revisedTimestamp) ? moment.utc(accountOwner.verificationRequests[i].revisedTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateFormat) : moment.utc(accountOwner.verificationRequests[i].CreationTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateFormat),
                            canCorrect: accountOwner.verificationRequests[i].canCorrectRequest,
                            status: accountOwner.verificationRequests[i].accountVerificationStatus,
                            concessionStartTimestamp: (accountOwner.verificationRequests[i].concessionStartTimestamp) ? moment.utc(accountOwner.verificationRequests[i].concessionStartTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateFormat) : '',
                            targetRiderTypeId: accountOwner.verificationRequests[i].targetRiderTypeId,
                            accountType: accountOwner.verificationRequests[i].accountType,
                            currentRiderTypeId: accountOwner.verificationRequests[i].currentRiderTypeId,
                            accountIdentityProofs: accountOwner.verificationRequests[i].accountIdentityProofs
                        };

                        accountVerificationRequest.date = moment.utc(accountVerificationRequest.lastChangeTime, this.dateTimeFormat).utcOffset(global.currentPosData.utcOffset).format(this.dateFormat);

                        const riderType = riderTypes.find((f) => f.externalId == accountVerificationRequest.targetRiderTypeId);
                        if (riderType) {
                            accountVerificationRequest.targetRider = riderType.name;
                        }

                        account.allVerificationRequests.push(accountVerificationRequest);
                    }

                    //add the latest verification request
                    account.allVerificationRequests = account.allVerificationRequests.sort((a, b) => a.lastChangeTime - b.lastChangeTime);
                    account.verificationRequest = account.allVerificationRequests[0];

                    let latestVerificationRequest = accountOwner.verificationRequests.find((f) => f.uid == account.verificationRequest.uid);
                    account.targetRiderTypeId = (latestVerificationRequest) ? latestVerificationRequest.targetRiderTypeId : '';

                    if (latestVerificationRequest && (latestVerificationRequest.accountVerificationStatus == constants.accountVerificationStatus.pending || latestVerificationRequest.accountVerificationStatus == constants.accountVerificationStatus.rejected || latestVerificationRequest.accountVerificationStatus == constants.accountVerificationStatus.verified)) {
                        const verificationDocsForRider = await this.getVerificationDocuments(latestVerificationRequest.targetRiderTypeId);
                        const proofs = latestVerificationRequest.accountIdentityProofs;

                        if (proofs && proofs.length && account.targetRiderTypeId) {
                            if (verificationDocsForRider) {

                                let proofsLen = proofs.length;
                                for (let i=0; i<proofsLen; i++) {
                                    let docForRider = verificationDocsForRider.find((f) => f.id == proofs[i].verificationDocumentId);
                                    let docForRiderElem = docForRider.verificationElements;

                                    let verificationDoc = {
                                        id: proofs[i].uid,
                                        requestId: latestVerificationRequest.uid,
                                        version: proofs[i].version,
                                        verficationDocumentId: proofs[i].verificationDocumentId,
                                        name: docForRider.type,
                                        type: 'document',
                                        isPersonal: docForRider.isPersonal
                                    };

                                    let docDetails = proofs[i].identityProofDocumentDetails;

                                    verificationDoc.elements = [];
                                    verificationDoc.photoElements = [];

                                    let docDetailsLen = docDetails.length;
                                    for (let j=0; j<docDetailsLen; j++) {
                                        let docElement = docForRiderElem.find((f) => f.id == docDetails[j].documentElementId);
                                        let element = {
                                            id: docDetails[j].uid,
                                            fileType: docDetails[j].fileType,
                                            value: docDetails[j].value,
                                            verficationElementId: docDetails[j].documentElementId,
                                            name: docElement.name,
                                            type: docElement.type
                                        };

                                        if (element.type == 'DATE' || element.type == 'DATE_PAST_ONLY' || element.type == 'DATE_FUTURE_ONLY') {
                                            element.value = moment.utc(element.value).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat);
                                        }

                                        if (docDetails[j].fileType == 'jpg' || docDetails[j].fileType == 'png') {
                                            verificationDoc.photoElements.push(element);
                                        } else {
                                            verificationDoc.elements.push(element);
                                        }
                                    }

                                    if (verificationDoc.isPersonal) {
                                        account.personalDocument.push(verificationDoc);
                                    } else {
                                        account.documentToProveStatus.push(verificationDoc);
                                    }

                                }
                            }
                        }

                        if (latestVerificationRequest.accountVerificationStatus == constants.accountVerificationStatus.rejected) {
                            const rejectionDate = moment.utc(latestVerificationRequest.revisedTimestamp).toISOString();

                            if (rejectionDate >= moment.utc().subtract(30, 'days').toISOString()) {
                                account.rejected = true;
                                account.rejectionDate = moment.utc(rejectionDate).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat);
                            }
                        } else if (latestVerificationRequest.accountVerificationStatus == constants.accountVerificationStatus.verified) {
                            account.verified = true;
                            account.expirationDate = moment.utc(account.verificationRequest.expirationTime).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat);

                            if (moment.utc(latestVerificationRequest.concessionStartTimestamp).toISOString() > moment.utc().toISOString()) {
                                account.verifiedFuture = true;
                            }
                        } else {
                            account.pending = true;
                        }
                    }

                }
            }

            res.status(200).json(account);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getVerificationDocuments = async (targetRiderTypeId) => {
        const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
        const riderTypes = await this.fareTableModel.RiderTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, externalId: targetRiderTypeId});
        const verificationDocuments = (riderTypes) ? riderTypes.verificationDocuments : [];

        let result = [];

        let verificationDocumentsLen = verificationDocuments.length;
        for (let i=0; i<verificationDocumentsLen; i++) {
            let documents = await this.fareEnvironmentModel.VerificationDocuments.find({fareEnvironmentConfigurationId: activeConfig._id, id: {$in: verificationDocuments[i].verificationDocumentId}});

            let documentsLen = documents.length;
            for (let j=0; j<documentsLen; j++) {

                let docModel = {
                    id: documents[j].id,
                    type: documents[j].type,
                    description: documents[j].description,
                    isPersonal: documents[j].isPersonal,
                    verificationElements: []
                };

                if ((documents[j].verificationDocumentElementIds).length) {
                    let elements = await this.fareEnvironmentModel.VerificationDocumentElements.find({fareEnvironmentConfigurationId: activeConfig._id, id: {$in: documents[j].verificationDocumentElementIds}});

                    let elementsLen = elements.length;
                    for (let k=0; k<elementsLen; k++) {

                        docModel.verificationElements.push({
                            id: elements[k].id,
                            type: elements[k].type,
                            description: elements[k].description,
                            name: elements[k].name
                        });
                    }
                }

                result.push(docModel);
            }
        }

        return result;
    }

    setTransactions = async (purchases) => {
        const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
        const stops = await this.fareEnvironmentModel.Stops.find({fareEnvironmentConfigurationId: activeConfig._id});

        let result = [];
        let purchasesLen = purchases.length;

        if (purchases && purchasesLen) {
            for (let i=0; i<purchasesLen; i++) {
                let transaction = {};

                transaction.transactionDateFormated = moment.utc(purchases[i].purchaseTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat);
                transaction.transactionDate = moment.utc(purchases[i].purchaseTimestamp).toISOString();
                transaction.amount = purchases[i].price / Math.pow(10, global.currentPosData.currencyDecimal);
                transaction.isInvalid = (purchases[i].fareItemStatusType == 'invalid') ? true : false;

                let paymentType = 'Credit card';
                switch (purchases[i].paymentInstrumentType) {
                    case constantsXml.paymentInstrument.prepayCash:
                        paymentType = 'Cash';
                    break;
                    case constantsXml.paymentInstrument.prepayCreditCard:
                        paymentType = 'Credit card';
                    break;
                    case constantsXml.paymentInstrument.prepayDebitCard:
                        paymentType = 'Debit card';
                    break;
                    case constantsXml.paymentInstrument.prepayAccountEPurse:
                        paymentType = 'Account';
                    break;
                }

                transaction.paymentInstrumentId = purchases[i].paymentInstrumentId;
                transaction.fareItemIssuedNumber = purchases[i].fareItemIssuedNumber;

                let itemInfo = '';
                let group = (purchases[i].groupPurchases.length > 0) ? `(${purchases[i].groupPurchases[0].numberOfRiders} passengers)` : '';
                let type = 'TICKET';

                // constantsXml.fareItemType.journeyTicket,
                // constantsXml.fareItemType.journeyGroupTicket,
                // constantsXml.fareItemType.journeyElectronicTicket,
                // constantsXml.fareItemType.journeyGroupElectronicTicket,
                // constantsXml.fareItemType.singleRideTicket,
                // constantsXml.fareItemType.returnSingleRideTicket,
                // constantsXml.fareItemType.singleRideGroupTicket,
                // constantsXml.fareItemType.returnSingleRideGroupTicket,
                // constantsXml.fareItemType.singleRideElectronicTicket,
                // constantsXml.fareItemType.returnSingleRideElectronicTicket,
                // constantsXml.fareItemType.singleRideGroupElectronicTicket,
                // constantsXml.fareItemType.returnSingleRideGroupElectronicTicket,
                // constantsXml.fareItemType.productTicket,
                // constantsXml.fareItemType.productAccount,
                // constantsXml.fareItemType.creditEpurseAccount

                // if (purchases[i].fareItemType == constantsXml.fareItemType.productAccount) {
                //     type = 'BAGGAGE';
                // } else {
                //     type = 'TICKET';
                // }

                switch (purchases[i].fareSystemModelType) {
                    case constantsXml.fareSystemModel.flat:
                        itemInfo = `${purchases[i].routeTypeName} - ${purchases[i].riderTypeName} ${group}`;
                    break;
                    case constantsXml.fareSystemModel.zones:
                        itemInfo = `${purchases[i].routeTypeName} - ${purchases[i].zonesLeft} zone - ${purchases[i].riderTypeName} ${group}`;
                    break;
                    case constantsXml.fareSystemModel.distanceBased:
                    case constantsXml.fareSystemModel.distanceBasedStaged:
                    case constantsXml.fareSystemModel.fromToStation:
                        let boardingStation = stops.find((f) => f.stationCode == purchases[i].boardingStationCode);
                        let boardingStationName = (boardingStation) ? boardingStation.stopName.substring(0, 1).toUpperCase() + boardingStation.stopName.substring(1).toLowerCase() : '';
                        let fareType = (purchases[i].fareItemType.includes('return')) ? constantsXml.fareType.return : constantsXml.fareType.oneway;
                        let direction = (purchases[i].fareItemType.includes('return')) ? '⇆' : '→';
                        let alightingStation = stops.find((f) => f.stationCode == purchases[i].alightingStationCode);
                        let alightingStationName = (alightingStation) ? alightingStation.stopName.substring(0, 1).toUpperCase() + alightingStation.stopName.substring(1).toLowerCase() : '';

                        itemInfo = `${purchases[i].routeTypeName} - ${fareType} - ${purchases[i].riderTypeName} ${group}`;
                        itemInfo += `\n${boardingStationName} ${direction} ${alightingStationName}`;
                    break;
                }

                if (purchases[i].fareItemCategoryType == constantsXml.fareItemCategory.fareProduct) {
                    itemInfo = `${purchases[i].routeTypeName} - ${purchases[i].fareItemName} - ${purchases[i].riderTypeName} ${group}`;
                }

                if (purchases[i].fareItemCategoryType == constantsXml.fareItemCategory.fareEpurse) {
                    if (purchases[i].fareItemType == constantsXml.fareItemType.creditEpurseAccount) {
                        type = 'TOP UP';
                    } else {
                        type = 'Cancellation TOP UP';
                    }

                    itemInfo = 'Stored value';
                }

                transaction.transactionType = type;
                transaction.paymentType = paymentType;
                transaction.transactionDetails = itemInfo;
                transaction.transactionId = purchases[i].paymentInstrumentTransactionId;
                transaction.location = purchases[i].deviceType;
                transaction.tapIn = purchases[i].boardingStationCode;
                transaction.tapOut = purchases[i].alightingStationCode;
                transaction.paymentInstrumentType = paymentType;

                result.push(transaction);
            }

            // result.filter(i => moment.utc(i.transactionDate).format('X') >= moment.utc().subtract(365, 'days').format('X')).sort((a, b) => moment.utc(b.transactionDate).diff(moment.utc(a.transactionDate)));
            // result.sort((a, b) => moment.utc(b.transactionDate).diff(moment.utc(a.transactionDate)));

            // result.filter((f) => moment.utc().diff(moment.utc(f.transactionDate), 'days') < 365);
            result = _.orderBy(result, ['transactionDate'], ['desc']);
        }

        return result;
    }

    setTickets = async (tickets, account) => {
        const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
        const stops = await this.fareEnvironmentModel.Stops.find({fareEnvironmentConfigurationId: activeConfig._id});

        let allTickets = {};

        allTickets.activeTickets = [];
        allTickets.usedTickets = [];

        if (tickets && tickets.length) {
            let ticketsLen = tickets.length;
            for (let i=0; i<ticketsLen; i++) {
                let itemInfo = '';
                let group = (tickets[i].groupPurchases.length > 0) ? `(${tickets[i].groupPurchases[0].numberOfRiders} passengers)` : '';
                let status = '';
                let hasLuggage = false;

                switch (tickets[i].fareSystemModelType) {
                    case constantsXml.fareSystemModel.flat:
                        itemInfo = `${tickets[i].routeTypeName} - ${tickets[i].riderTypeName} ${group}`;
                    break;
                    case constantsXml.fareSystemModel.zones:
                        itemInfo = `${tickets[i].routeTypeName} - ${tickets[i].zonesLeft} zone - ${tickets[i].riderTypeName} ${group}`;
                    break;
                    case constantsXml.fareSystemModel.distanceBased:
                    case constantsXml.fareSystemModel.distanceBasedStaged:
                    case constantsXml.fareSystemModel.fromToStation:
                        let boardingStation = stops.find((f) => f.stationCode == tickets[i].boardingStationCode);
                        let boardingStationName = (boardingStation) ? boardingStation.stopName.substring(0, 1).toUpperCase() + boardingStation.stopName.substring(1).toLowerCase() : '';
                        let fareType = (tickets[i].fareItemType.includes('return')) ? constantsXml.fareType.return : constantsXml.fareType.oneway;
                        let direction = (tickets[i].fareItemType.includes('return')) ? '⇆' : '→';
                        let alightingStation = stops.find((f) => f.stationCode == tickets[i].alightingStationCode);
                        let alightingStationName = (alightingStation) ? alightingStation.stopName.substring(0, 1).toUpperCase() + alightingStation.stopName.substring(1).toLowerCase() : '';

                        itemInfo = `${tickets[i].routeTypeName} - ${fareType} - ${tickets[i].riderTypeName} ${group}`;
                        itemInfo += `\n${boardingStationName} ${direction} ${alightingStationName}`;
                    break;
                }

                if (tickets[i].fareItemCategoryType == constantsXml.fareItemCategory.fareProduct) {
                    itemInfo = `${tickets[i].routeTypeName} - ${tickets[i].fareItemName} - ${tickets[i].riderTypeName} ${group}`;
                }

                if (tickets[i].fareItemStatusType == 'consumed') {
                    status = 'Validated';
                } else if (moment.utc(tickets[i].expirationTimestamp).toISOString() < moment.utc().toISOString()) {
                    status = 'Expired';
                } else {
                    status = tickets[i].fareItemStatusType;
                }

                let fareTime = (tickets[i].relatedPurchaseInfo) ? tickets[i].relatedPurchaseInfo.purchaseTimestamp : '';
                let fareNumber = (tickets[i].relatedPurchaseInfo) ? tickets[i].relatedPurchaseInfo.fareItemIssuedNumber : '';

                let accountTicket = {
                    type: itemInfo,
                    boughtTime: moment.utc(tickets[i].purchaseTimestamp).utcOffset(global.currentPosData.utcOffset).toISOString(),
                    boughtTimeSecond: moment.utc(tickets[i].purchaseTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                    number: tickets[i].fareItemIssuedNumber,
                    status: status,
                    hasLuggage: hasLuggage,
                    riderTypeId: tickets[i].riderTypeId,
                    riderName: tickets[i].riderTypeName,
                    routeTypeId: tickets[i].routeTypeId,
                    expirationTimestamp: moment.utc(tickets[i].expirationTimestamp).toISOString(),
                    fareItemIssuedNumber: tickets[i].fareItemIssuedNumber,
                    price: tickets[i].price,
                    remainingRides: (tickets[i].remainingRides) ? tickets[i].remainingRides : 0,
                    transfersLeft: (tickets[i].transfersLeft) ? tickets[i].transfersLeft : 0,
                    zonesLeft: (tickets[i].zonesLeft) ? tickets[i].zonesLeft : 0,
                    fareItemType: tickets[i].fareItemType,
                    purchaseTimestamp: moment.utc(tickets[i].purchaseTimestamp).toISOString(),
                    accountId: account,
                    boughtDate: moment.utc(tickets[i].purchaseTimestamp).utcOffset(global.currentPosData.utcOffset).toISOString().slice(0,-5),
                    relatedPurchaseInfo: {
                        purchaseTimestamp: moment.utc(fareTime).toISOString(),
                        fareItemIssuedNumber: fareNumber
                    }
                };

                if ((tickets[i].fareItemStatusType == 'active' || tickets[i].fareItemStatusType == 'pending') && moment.utc(tickets[i].expirationTimestamp).toISOString() > moment.utc().toISOString()) {
                    allTickets.activeTickets.push(accountTicket);
                } else {
                    allTickets.usedTickets.push(accountTicket);
                }
            }
        }

        allTickets.activeTickets = _.orderBy(allTickets.activeTickets, ['boughtTime', 'number'], ['desc', 'asc']);
        allTickets.usedTickets = _.orderBy(allTickets.usedTickets, ['boughtTime', 'number'], ['desc', 'asc']);

        return allTickets;
    }

    resetAccess = async (req, res) => {
        try {
            const identity = req.body;

            const updateData = {
                isPrimary: identity.isPrimary,
                version: identity.version,
                value: identity.value,
                password: identity.password,
                changeEventType: identity.changeEventType
            };

            const identityUpdate = await this.service.resetAccess(identity.id, updateData);

            res.status(200).json(identityUpdate);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getAccountDetails = async (req, res) => {
        try {
            const id = req.body.id;

            const account = await this.service.getAccountDetails(id);

            res.status(200).json(account);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getAccountDetailsPhone = async (req, res) => {
        try {
            const phone = req.query.phone;

            const account = (phone != 'null') ? await this.service.getAccountDetailsPhone(phone) : null;

            const phoneExists = (account && account.phoneNumber) ? true : false;

            res.status(200).json(phoneExists);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getAccountDetailsEmail = async (req, res) => {
        try {
            const email = req.query.email;

            const account = (email != 'null') ? await this.service.getAccountDetailsEmail(email) : null;

            const emailExists = (account && account.emailAddress) ? true : false;

            res.status(200).json(emailExists);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    create = async (req, res) => {
        try {
            const account = req.body;

            const accountHolder = {
                version: 0,
                firstName: account.name.substr(0, account.name.indexOf(' ')),
                lastName: account.name.substr(account.name.indexOf(' ') + 1),
                fullName: account.name,
                dayOfBirth: (account.birthday) ? moment.utc(account.birthday, this.dateFormat).toISOString() : null,
                //businessOrganizationName: account.businessOrganizationName,
                //businessOrganizationId: account.businessOrganizationId,
                addressLine1: account.address,
                //addressLine2: account.address2,
                city: account.city,
                //postalCode: account.postalCode,
                country: account.country,
                personGender: account.gender.toUpperCase(),
                personType: account.title.toUpperCase(),
                accountType: constants.accountType.regular,
                concessionExpirationTimestamp: moment.utc().toISOString(),
                languageCode: (account.languageCode) ? account.languageCode : 'en-GB',
                emailAddress: (account.email) ? account.email : null,
                phoneNumber: (account.phone) ? account.phone : null,
                identityType: (account.email) ? constants.identityType.email : constants.identityType.phone,
                identityValue: (account.email) ? account.email : account.phone,
                riderTypeId: (account.riderTypeId) ? account.riderTypeId : constants.defaults.accountId
            };

            const createAccountHolder = await this.service.create(accountHolder);

            //const createdAccountHolder = await this.service.getAccountDetails(createAccountHolder.uid);
            //const accountId = createdAccountHolder.linkedAccounts.filter((f) => f.isOwner == true);

            if (account.image) {
                const photoArray = account.image.split(',')[1];

                const photo = account.image;
                let imageData = photo.split(',')[1];
                let imageType = photo.split(';')[0].split('/')[1];
                let formContent = new formData();
                formContent.append('file', Buffer.from(imageData, 'base64'));
                formContent.append('name', 'photo'+createAccountHolder.uid+'.'+imageType);

                const uploadPhoto = await this.service.uploadPhoto(createAccountHolder.uid, imageType, formContent);
            }

            if (account.phone && accountHolder.identityValue != account.phone) {
                let identity = {
                    accountHolderUid: createAccountHolder.uid,
                    accountHolderIdentityType: identityType.phone,
                    value: account.phone
                };

                await this.service.addIdentity(identity);
            }

            account.id = createAccountHolder.uid;

            res.status(200).json(account);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    edit = async (req, res) => {
        try {
            const uid = req.body.id;
            const account = req.body;

            const accountHolder = await this.service.getAccountDetails(uid);

            let accountHolderEdit = {
                version: accountHolder.version,
                firstName: account.name.substr(0, account.name.indexOf(' ')),
                lastName: account.name.substr(account.name.indexOf(' ') + 1),
                fullName: account.name,
                dayOfBirth: (account.birthday) ? moment.utc(account.birthday, this.dateFormat).toISOString() : moment.utc(accountHolder.dayOfBirth).toISOString(),
                businessOrganizationName: accountHolder.businessOrganizationName,
                businessOrganizationId: accountHolder.businessOrganizationId,
                addressLine1: account.address,
                addressLine2: accountHolder.addressLine2,
                city: accountHolder.city,
                postalCode: accountHolder.postalCode,
                country: account.country,
                personGender: account.gender.toUpperCase(),
                personType: account.title.toUpperCase()
                // accountType: constants.accountType.regular,
                // concessionExpirationTimestamp: moment.utc(account.concessionExpirationTimestamp).toISOString(),
                // languageCode: 'en-GB',
                // emailAddress: account.emailAddress,
                // phoneNumber: account.phoneNumber,
                // identityType: (account.email) ? constants.identityType.email : constants.identityType.phone,
                // identityValue: (account.email) ? account.email : account.phone,
                // riderTypeId: (account.riderTypeId) ? account.riderTypeId : 0
            };

            const accountHolderEdited = await this.service.edit(uid, accountHolderEdit);

            res.status(200).json(account);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getAccountPhoto = async (req, res) => {
        try {
            const id = req.body.id;

            const accountPhoto = await this.service.getAccountPhoto(id);

            res.status(200).json(accountPhoto);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    uploadPhoto = async (req, res) => {
        try {
            const photo = req.body.content;
            let imageData = photo.split(',')[1];
            let imageType = photo.split(';')[0].split('/')[1];
            let formContent = new formData();
            formContent.append('file', Buffer.from(imageData, 'base64'));
            formContent.append('name', 'photo'+req.body.account+'.'+imageType);

            const uploadPhoto = await this.service.uploadPhoto(req.body.account, imageType, formContent);

            res.status(200).json(uploadPhoto);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    addIdentity = async (req, res) => {
        try {
            const identity = req.body;

            const identityObject = {
                accountHolderUid: identity.accountId,
                accountHolderIdentityType: identity.type,
                value: identity.value
            };

            const addIdentity = await this.service.addIdentity(identityObject);

            res.status(200).json(addIdentity);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    removeIdentity = async (req, res) => {
        try {
            const id = req.body.id;
            const version = req.body.version;
            const reason = req.body.reason;

            const removeIdentity = await this.service.removeIdentity(id, version, reason);

            res.status(200).json(removeIdentity);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getAccountPurchases = async (req, res) => {
        try {
            const id = req.body.id;
            const days = req.body.days;

            const getAccountPurchases = await this.service.getAccountPurchases(id, days);

            res.status(200).json(getAccountPurchases);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getVerificationRequests = async (req, res) => {
        try {
            const id = req.body.id;

            const getVerificationRequests = await this.service.getVerificationRequests(id);

            res.status(200).json(getVerificationRequests);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getVerificationRequest = async (req, res) => {
        try {
            const uid = req.body.uid;

            const getVerificationRequest = await this.service.getVerificationRequest(uid);

            res.status(200).json(getVerificationRequest);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    updateVerificationRequest = async (req, res) => {
        try {
            const uid = req.body.uid;
            const request = req.body.request;

            const updateVerificationRequest = {
                version: (request.version) ? request.version : 0,
                description: request.description,
                accountVerificationStatus: request.accountVerificationStatus,
                concessionStartTimestamp: moment.utc(request.concessionStartTimestamp).toISOString(),
                concessionExpirationTimestamp: moment.utc(request.concessionExpirationTimestamp).toISOString(),
                canCorrectRequest: request.canCorrectRequest
            };

            const updateVerificationReq = await this.service.addVerificationRequest(uid, updateVerificationRequest);

            res.status(200).json(updateVerificationReq);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    updateVerificationRequestProofs = async (req, res) => {
        try {
            const uid = req.body.uid;
            const proofs = req.body.proofs;

            const updateVerificationRequestProofs = {
                identityProofUpdateDetails: [{
                    description: proofs.description,
                    verificationStatus: proofs.verificationStatus,
                    verificationDocumentId: (proofs.verificationDocumentId) ? proofs.verificationDocumentId : 0,
                    uid: proofs.uid,
                    version: (proofs.version) ? proofs.version : 0,
                    identityProofDocumentDetails: [{
                        fileType: proofs.fileType,
                        value: proofs.value,
                        documentElementId: proofs.documentElementId,
                        uid: proofs.uid
                    }]
                }]
            };

            const updateVerificationReq = await this.service.updateVerificationRequestProofs(uid, updateVerificationRequestProofs);

            res.status(200).json(updateVerificationReq);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    deleteVerificationRequestProof = async (req, res) => {
        try {
            const uid = req.body.uid;
            const version = req.body.version;
            const reason = req.body.reason;

            const deleteVerificationReq = await this.service.deleteVerificationRequestProof(uid, version, reason);

            res.status(200).json(deleteVerificationReq);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    addVerificationRequestProof = async (req, res) => {
        try {
            const uid = req.body.uid;
            const version = req.body.version;
            const proof = req.body.proof;

            const addVerificationRequestProof = {
                accountIdentityProofs: [{
                    description: proof.description,
                    verificationStatus: proof.verificationStatus,
                    verificationDocumentId: (proof.verificationDocumentId) ? proof.verificationDocumentId : 0,
                    identityProofDocumentDetails: [{
                        fileType: proof.fileType,
                        value: proof.value,
                        documentElementId: proof.documentElementId
                    }]
                }]
            };

            const addVerificationReq = await this.service.addVerificationRequestProof(uid, version, addVerificationRequestProof);

            res.status(200).json(addVerificationReq);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getAllAccountPurchases = async (req, res) => {
        try {
            const id = req.body.id;

            const getAllAccountPurchases = await this.service.getAllAccountPurchases(id);

            res.status(200).json(getAllAccountPurchases);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    isIdentityAvailable = async (req, res) => {
        try {
            const value = req.body.value;

            const isIdentityAvailable = await this.service.isIdentityAvailable(value);

            res.status(200).json(isIdentityAvailable);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getAccessDevice = async (req, res) => {
        try {
            const uid = req.body.uid;

            const getAccessDevice = await this.service.getAccessDevice(uid);

            res.status(200).json(getAccessDevice);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    updateDevice = async (req, res) => {
        try {
            const uid = req.body.uid;
            const accountAccess = req.body.accountAccess;

            const account = {
                lastAccessTimestamp: moment.utc(accountAccess.lastAccessTimestamp).toISOString(),
                lastIpAddress: accountAccess.lastIpAddress,
                version: (accountAccess.version) ? accountAccess.version : 0,
                blockedTimestamp: moment.utc(accountAccess.blockedTimestamp).toISOString(),
                isBlocked: accountAccess.isBlocked,
                reasonId: (accountAccess.reasonId) ? accountAccess.reasonId : 0
            };

            const updateDevice = await this.service.updateDevice(uid, account);

            res.status(200).json(updateDevice);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    updateIdentity = async (req, res) => {
        try {
            const uid = req.body.uid;
            const identity = req.body.identity;

            const updateIdentity = await this.service.updateIdentity(uid, identity);

            res.status(200).json(updateIdentity);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getIdentity = async (req, res) => {
        try {
            const uid = req.body.uid;

            const getIdentity = await this.service.getIdentity(uid);

            res.status(200).json(getIdentity);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    viewRequest = async (req, res) => {
        try {
            const accountId = req.query.accountId;
            const requestId = req.query.requestId;

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const riderTypes = await this.fareTableModel.RiderTypes.find({fareEnvironmentConfigurationId: activeConfig._id});
            const accountDetails = await this.service.getAccountDetails(accountId);
            const accountOwner = accountDetails.linkedAccounts.find((f) => f.isOwner == true);

            let account = {};
            let verificationDocsForRider = [];

            if (accountDetails) {
                // setBasicAccountDetails //
                account.name = accountDetails.fullName;
                if (accountDetails.dayOfBirth) {
                    account.birthday = moment.utc(accountDetails.dayOfBirth).format(this.dateRFormat);
                }

                account.address = accountDetails.addressLine1;
                account.country = (accountDetails.country) ? accountDetails.country.toUpperCase() : '';

                const country = countries.countries.find((f) => f.code == account.country);

                account.countryFull = (country) ? country.name : '';

                account.email = accountDetails.emailAddress;
                account.city = accountDetails.city;
                account.phone = accountDetails.phoneNumber;
                account.id = accountDetails.uid;
                account.version = accountDetails.version;
                account.title = accountDetails.personType;
                // setBasicAccountDetails //

                if (accountOwner != null) {
                    const riderType = riderTypes.find((f) => f.externalId == accountOwner.riderTypeId);
                    account.shortId = accountOwner.shortId;
                    account.type = (riderType) ? riderType.name.toUpperCase() : '';
                }

                account.image = await this.service.getAccountPhoto(account.id);

                account.documentToProveStatus = [];
                account.personalDocument = [];

                let verificationRequest = await this.service.getVerificationRequest(requestId);

                account.targetRiderTypeId = verificationRequest.targetRiderTypeId;
                account.verificationRequest = {
                    uid: verificationRequest.uid,
                    description: verificationRequest.description,
                    expirationTimestamp: moment.utc(verificationRequest.concessionExpirationTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateFormat),
                    version: verificationRequest.version,
                    concessionExpirationTimestamp: (verificationRequest.concessionExpirationTimestamp) ? moment.utc(verificationRequest.concessionExpirationTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateFormat) : '',
                    lastChangeTime: (verificationRequest.revisedTimestamp) ? moment.utc(verificationRequest.revisedTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateFormat) : moment.utc(verificationRequest.creationTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateFormat),
                    canCorrect: verificationRequest.canCorrectRequest,
                    status: verificationRequest.accountVerificationStatus,
                    concessionStartTimestamp: (verificationRequest.concessionStartTimestamp) ? moment.utc(verificationRequest.concessionStartTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateFormat) : ''
                };
                account.verificationRequest.date = moment.utc(account.verificationRequest.lastChangeTime).format(this.dateFormat);

                let targetRider = riderTypes.find((f) => f.externalId == verificationRequest.targetRiderTypeId);
                if (targetRider != null) {
                    account.verificationRequest.targetRider = targetRider.name;
                }

                const documentIds = targetRider.verificationDocuments.map((m) => m.verificationDocumentId);
                if (documentIds) {
                    let documents = await this.fareEnvironmentModel.VerificationDocuments.find({fareEnvironmentConfigurationId: activeConfig._id, id: {$in: documentIds}});

                    let documentsLen = documents.length;
                    for (let i=0; i<documentsLen; i++) {

                        let docModel = {
                            id: documents[i].id,
                            type: documents[i].type,
                            description: documents[i].description,
                            isPersonal: documents[i].isPersonal,
                            verificationElements: []
                        };

                        if ((documents[i].verificationDocumentElementIds).length > 0) {
                            let elements = await this.fareEnvironmentModel.VerificationDocumentElements.find({fareEnvironmentConfigurationId: activeConfig._id, id: {$in: documents[i].verificationDocumentElementIds}});

                            let elementsLen = elements.length;
                            for (let j=0; j<elementsLen; j++) {

                                docModel.verificationElements.push({
                                    id: elements[j].id,
                                    name: elements[j].name,
                                    description: elements[j].description,
                                    type: elements[j].type
                                });
                            }
                        }

                        verificationDocsForRider.push(docModel);
                    }
                }

                let proofs = verificationRequest.accountIdentityProofs;
                if (proofs.length > 0 && account.targetRiderTypeId > 0) {
                    if (verificationDocsForRider != null) {

                        let proofsLen = proofs.length;
                        for (let i=0; i<proofsLen; i++) {

                            let doc = verificationDocsForRider.find((f) => f.id == proofs[i].verificationDocumentId);
                            if (doc) {
                                let proofDetails = proofs[i].identityProofDocumentDetails;
                                let elements = doc.verificationElements;

                                let proofDetailsLen = proofDetails.length;
                                for (let j=0; j<proofDetailsLen; j++) {
                                    let elem = elements.find((f) => f.id == proofDetails[j].documentElementId);

                                    if (elem) {
                                        if (elem.type == 'DATE' || elem.type == 'DATE_PAST_ONLY' || elem.type == 'DATE_FUTURE_ONLY') {
                                            elem.value = moment.utc(proofDetails[j].value).utcOffset(global.currentPosData.utcOffset).format(this.dateFormat);
                                        } else {
                                            elem.value = proofDetails[j].value;
                                        }
                                    }
                                }
                            }

                        }
                    }
                }
            }

            res.status(200).json({account: account, verificationDocuments: verificationDocsForRider});
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    block = async (req, res) => {
        try {
            const block = req.body;

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const transitTypeDef = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id}).sort({externalId: 1});

            await this.posTransactionCtrl.create({
                function: 'blockUnblock',
                component: 'ticketPrinter',
                parameters: {
                    fare: {
                        externalId: transitTypeDef.externalId,
                        name: transitTypeDef.name,
                        fareSystemModel: transitTypeDef.fareSystemModel,
                        routeSystemType: transitTypeDef.routeSystemType
                    },
                    financial: {
                        activityType: constantsXml.activityType.manageAccount,
                        fareItemCategory: constantsXml.fareItemCategory.blockType,
                        fareItemType: constantsXml.fareItemType.blockAccount,
                        fareItemExternalId: 0,
                        fareItemIssuedNumber: 0,
                        fareItemIssuedExpirationTimeStamp: moment.utc().format(this.dateTTimeFormat),
                        fareItemName: constantsXml.fareItemName.na,
                        payingRiderTypeExternalId: block.account.riderTypeId,
                        payingRiderTypeName: block.account.type,
                        payingRiderAccountId: block.account.shortId,
                        cumulativeFareOrProductPrice: 0,
                        penaltyFee: 0,
                        paymentInstrument: constantsXml.paymentInstrument.none,
                        paymentMethod: constantsXml.paymentMethod.none,
                        currencyCode: global.currentPosData.currencyCode,
                        reasonInformation: {
                            reasonId: block.reason.id,
                            reasonName: block.reason.name
                        }
                    }
                }
            });

            res.status(200).json(true);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    unblock = async (req, res) => {
        try {
            const unblock = req.body;

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const transitTypeDef = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id}).sort({externalId: 1});

            await this.posTransactionCtrl.create({
                function: 'blockUnblock',
                component: 'ticketPrinter',
                parameters: {
                    fare: {
                        externalId: transitTypeDef.externalId,
                        name: transitTypeDef.name,
                        fareSystemModel: transitTypeDef.fareSystemModel,
                        routeSystemType: transitTypeDef.routeSystemType
                    },
                    financial: {
                        activityType: constantsXml.activityType.manageAccount,
                        fareItemCategory: constantsXml.fareItemCategory.blockType,
                        fareItemType: constantsXml.fareItemType.unblockAccount,
                        fareItemExternalId: 0,
                        fareItemIssuedNumber: 0,
                        fareItemIssuedExpirationTimeStamp: moment.utc().format(this.dateTTimeFormat),
                        fareItemName: constantsXml.fareItemName.na,
                        payingRiderTypeExternalId: unblock.account.riderTypeId,
                        payingRiderTypeName: unblock.account.type,
                        payingRiderAccountId: unblock.account.shortId,
                        cumulativeFareOrProductPrice: 0,
                        penaltyFee: 0,
                        paymentInstrument: constantsXml.paymentInstrument.none,
                        paymentMethod: constantsXml.paymentMethod.none,
                        currencyCode: global.currentPosData.currencyCode,
                        reasonInformation: {
                            reasonId: unblock.reason.id,
                            reasonName: unblock.reason.name
                        }
                    }
                }
            });

            res.status(200).json(true);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    blockDevice = async (req, res) => {
        try {
            const deviceId = req.query.deviceId;
            const reasonId = req.query.reasonId;

            const deviceAccess = await this.service.getAccessDevice(deviceId);

            const account = {
                lastAccessTimestamp: moment.utc(deviceAccess.lastAccessTimestamp).toISOString(),
                lastIpAddress: deviceAccess.lastIpAddress,
                version: deviceAccess.version,
                isBlocked: true,
                blockedTimestamp: moment.utc().toISOString(),
                reasonId: reasonId
            };

            const deviceAccessStatus = await this.service.updateDevice(deviceId, account);

            res.status(200).json(deviceAccessStatus && deviceAccessStatus.isBlocked);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    unblockDevice = async (req, res) => {
        try {
            const deviceId = req.query.deviceId;
            const reasonId = req.query.reasonId;

            const deviceAccess = await this.service.getAccessDevice(deviceId);

            const account = {
                lastAccessTimestamp: moment.utc(deviceAccess.lastAccessTimestamp).toISOString(),
                lastIpAddress: deviceAccess.lastIpAddress,
                version: deviceAccess.version,
                isBlocked: false,
                blockedTimestamp: moment.utc().toISOString(),
                reasonId: reasonId
            };

            const deviceAccessStatus = await this.service.updateDevice(deviceId, account);

            res.status(200).json(deviceAccessStatus && !deviceAccessStatus.isBlocked);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    blockReasons = async (req, res) => {
        try {
            const reasonConfiguration = await this.reasonsServ.reasonConfiguration();
            const reasonType = reasonConfiguration.reasonTypes.find(x => x.name == constants.reasonTypes.accountBlock);
            const reasonTypeId = (reasonType) ? reasonType.id : 0;
            const reasons = reasonConfiguration.reasons.filter((f) => f.reasonTypeId == reasonTypeId);

            let reasonsList = [];
            if (reasons) {
                const reasonsLen = reasons.length;
                for (let i=0; i<reasonsLen; i++) {
                    reasonsList.push({
                        id: reasons[i].id,
                        name: reasons[i].name,
                        tag: reasons[i].tag,
                        nameTranslated: reasons[i].nameTranslations.find((f) => f.languageCode == global.currentPosData.secondLanguage.split('-')[0]).translation
                    });
                }
            }

            const reasonsListOrdered = reasonsList.sort((a, b) => a.id - b.id);

            res.status(200).json(reasonsListOrdered);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    unblockReasons = async (req, res) => {
        try {
            const reasonConfiguration = await this.reasonsServ.reasonConfiguration();
            const reasonType = reasonConfiguration.reasonTypes.find((f) => f.name == constants.reasonTypes.accountUnblock);
            const reasonTypeId = (reasonType) ? reasonType.id : 0;
            const reasons = reasonConfiguration.reasons.filter((f) => f.reasonTypeId == reasonTypeId);

            let reasonsList = [];
            if (reasons) {
                const reasonsLen = reasons.length;
                for (let i=0; i<reasonsLen; i++) {
                    reasonsList.push({
                        id: reasons[i].id,
                        name: reasons[i].name,
                        tag: reasons[i].tag,
                        nameTranslated: reasons[i].nameTranslations.find((f) => f.languageCode == global.currentPosData.secondLanguage.split('-')[0]).translation
                    });
                }
            }

            const reasonsListOrdered = reasonsList.sort((a, b) => a.id - b.id);

            res.status(200).json(reasonsListOrdered);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    blockDeviceReasons = async (req, res) => {
        try {
            const reasonConfiguration = await this.reasonsServ.reasonConfiguration();
            const reasonType = reasonConfiguration.reasonTypes.find((f) => f.name == constants.reasonTypes.deviceBlock);
            const reasonTypeId = (reasonType) ? reasonType.id : 0;
            const reasons = reasonConfiguration.reasons.filter((f) => f.reasonTypeId == reasonTypeId);

            let reasonsList = [];
            if (reasons) {
                const reasonsLen = reasons.length;
                for (let i=0; i<reasonsLen; i++) {
                    reasonsList.push({
                        id: reasons[i].id,
                        name: reasons[i].name,
                        tag: reasons[i].tag,
                        nameTranslated: reasons[i].nameTranslations.find((f) => f.languageCode == global.currentPosData.secondLanguage.split('-')[0]).translation
                    });
                }
            }

            const reasonsListOrdered = reasonsList.sort((a, b) => a.id - b.id);

            res.status(200).json(reasonsListOrdered);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    unblockDeviceReasons = async (req, res) => {
        try {
            const reasonConfiguration = await this.reasonsServ.reasonConfiguration();
            const reasonType = reasonConfiguration.reasonTypes.find((f) => f.name == constants.reasonTypes.deviceUnblock);
            const reasonTypeId = (reasonType) ? reasonType.id : 0;
            const reasons = reasonConfiguration.reasons.filter((f) => f.reasonTypeId == reasonTypeId);

            let reasonsList = [];
            if (reasons) {
                const reasonsLen = reasons.length;
                for (let i=0; i<reasonsLen; i++) {
                    reasonsList.push({
                        id: reasons[i].id,
                        name: reasons[i].name,
                        tag: reasons[i].tag,
                        nameTranslated: reasons[i].nameTranslations.find((f) => f.languageCode == global.currentPosData.secondLanguage.split('-')[0]).translation
                    });
                }
            }

            const reasonsListOrdered = reasonsList.sort((a, b) => a.id - b.id);

            res.status(200).json(reasonsListOrdered);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    countries = async (req, res) => {
        try {
            const country = countries.countries;

            let countriesList = [];
            const countriesLen = country.length;
            for (let i=0; i<countriesLen; i++) {
                countriesList.push({
                    code: country[i].code,
                    name: country[i].translations['en-GB'],
                    callingCode: country[i].callingCode
                });
            }

            res.status(200).json(countriesList);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    checkIdentity = async (req, res) => {
        try {
            const value = req.body.value;

            const isIdentityAvailable = await this.service.isIdentityAvailable(value);

            res.status(200).json(isIdentityAvailable);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    changeType = async (req, res) => {
        try {
            const account = req.body;
            let documents = [].concat(account.personalDocument, account.documentToProveStatus);
            let addVerification = account;

            if (documents.length) {
                let verificationRequest = {
                    concessionExpirationTimestamp: (account.expirationDate) ? moment.utc(account.expirationDate, this.dateFormat).toISOString() : moment.utc().toISOString(),
                    concessionStartTimestamp: (account.startExpirationDate) ? moment.utc(account.startExpirationDate, this.dateFormat).toISOString() : moment.utc().toISOString(),
                    targetRiderTypeId: account.targetRiderTypeId,
                    description: '',
                    accountVerificationStatus: constants.accountVerificationStatus.verified,
                    canCorrectRequest: true,
                    accountType: constants.accountType.regular,
                    accountIdentityProofs: []
                };

                const documentsLen = documents.length;
                for (let i=0; i<documentsLen; i++) {
                    let identityProof = {
                        verificationDocumentId: documents[i].id,
                        description: '',
                        verificationStatus: constants.accountVerificationProofStatus.passed,
                        identityProofDocumentDetails: []
                    };

                    const documentElementsLen = (documents[i].elements).length;
                    for (let j=0; j<documentElementsLen; j++) {
                        let proofDetail = {};
                        proofDetail.documentElementId = documents[i].elements[j].id;

                        if (documents[i].elements[j].type == 'IMAGE') {
                            proofDetail.value = documents[i].elements[j].value.split(',')[1];
                            proofDetail.fileType = documents[i].elements[j].value.split(';')[0].split('/')[1];
                        } else if (documents[i].elements[j].type == 'DATE' || documents[i].elements[j].type == 'DATE_PAST_ONLY' || documents[i].elements[j].type == 'DATE_FUTURE_ONLY') {
                            proofDetail.value = moment.utc(documents[i].elements[j].value, this.dateFormat).format(this.dateRFormat);
                        } else {
                            proofDetail.value = documents[i].elements[j].value;
                        }

                        identityProof.identityProofDocumentDetails.push(proofDetail);
                    }

                    verificationRequest.accountIdentityProofs.push(identityProof);
                }

                addVerification = await this.service.addVerificationRequest(account.accountIntegrityId, verificationRequest);
            }

            res.status(200).json(true);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    cancelRequest = async (req, res) => {
        try {
            const account = req.body;

            const verificationRequest = {
                version: account.version,
                description: account.description,
                accountVerificationStatus: account.status,
                canCorrectRequest: account.canCorrect,
                concessionExpirationTimestamp: moment.utc(account.expirationTimestamp, this.dateTimeFormat).toISOString(),
                concessionStartTimestamp: moment.utc(account.concessionStartTimestamp, this.dateTimeFormat).toISOString()
            };

            const updateVerificationRequest = await this.service.updateVerificationRequest(account.uid, verificationRequest);

            res.status(200).json(updateVerificationRequest);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    verificationDocuments = async (req, res) => {
        try {
            const riderId = req.query.riderType;

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const riderTypes = await this.fareTableModel.RiderTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider', externalId: riderId});
            const documentIds = (riderTypes) ? riderTypes.verificationDocuments : [];

            let verificationDocuments = [];
            let documentIdsLen = documentIds.length;
            if (documentIdsLen) {
                for (let i=0; i<documentIdsLen; i++) {
                    let documents = await this.fareEnvironmentModel.VerificationDocuments.findOne({fareEnvironmentConfigurationId: activeConfig._id, id: documentIds[i].verificationDocumentId});

                    let verificationDocument = {
                        id: documents.id,
                        type: documents.type,
                        description: documents.description,
                        isPersonal: documents.isPersonal,
                        verificationElements: []
                    };

                    if ((documents.verificationDocumentElementIds).length) {
                        let elementArray = documents.verificationDocumentElementIds;
                        let elements = await this.fareEnvironmentModel.VerificationDocumentElements.find({fareEnvironmentConfigurationId: activeConfig._id, id: {$in: elementArray}});

                        const elementsLen = elements.length;
                        for (let j=0; j<elementsLen; j++) {
                            verificationDocument.verificationElements.push({
                                id: elements[j].id,
                                name: elements[j].name,
                                description: elements[j].description,
                                type: elements[j].type
                            });
                        }
                    }

                    verificationDocuments.push(verificationDocument);

                }
            }

            const verificationDocumentsOrdered = verificationDocuments.sort((a, b) => a.id - b.id);

            res.status(200).json(verificationDocumentsOrdered);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    addDocument = async (req, res) => {
        try {
            const document = req.body.document;

            let identityProof = {
                description: '',
                verificationStatus: constants.accountVerificationStatus.pending,
                verificationDocumentId: document.verficationDocumentId,
                identityProofDocumentDetails: []
            };

            const documentElementsLen = (document.elements).length;
            for (let i=0; i<documentElementsLen; i++) {
                identityProof.IdentityProofDocumentDetails.push({
                    documentElementId: document.elements[i].verficationElementId,
                    value: document.elements[i].value,
                    fileType: (document.elements[i].fileType == 'IMAGE') ? 'jpg' : ''
                });
            }

            const addVerificationRequestProof = await this.service.addVerificationRequestProof(document.requestId, document.requestVersion, identityProof);

            res.status(200).json(addVerificationRequestProof);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    deleteDocument = async (req, res) => {
        try {
            const document = req.body.document;

            const deleteVerificationRequestProof = await this.service.deleteVerificationRequestProof(document.id, document.version, document.reason);

            res.status(200).json(deleteVerificationRequestProof);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    setPrimary = async (req, res) => {
        try {
            const identity = req.body;

            const updateData = {
                isPrimary: true,
                version: identity.version,
                value: identity.value,
                password: identity.password,
                changeEventType: identity.changeEventType
            };

            const identityUpdate = await this.service.resetAccess(identity.id, updateData);

            res.status(200).json(identityUpdate);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    verify = async (req, res) => {
        try {
            const verificationSubmit = req.body.verificationSubmit;

            const request = verificationSubmit.request;

            let verificationRequest = {
                version: request.version,
                description: request.description,
                accountVerificationStatus: (request.isOk) ? constants.accountVerificationStatus.verified : constants.accountVerificationStatus.rejected,
                canCorrectRequest: (request.isOk) ? true : request.canCorrect,
                caseoncessionStartTimestamp: (request.concessionStartTimestamp && moment.utc(request.concessionStartTimestamp).toISOString() == moment.utc().toISOString()) ? moment.utc().toISOString() : moment.utc(request.concessionStartTimestamp).toISOString(),
                concessionExpirationTimestamp: (request.concessionExpirationTimestamp) ? moment.utc(request.concessionExpirationTimestamp).toISOString() : ''
            };

            let verificationResponse = await this.service.updateVerificationRequest(request.uid, verificationRequest);
            let proofs = verificationResponse.accountIdentityProofs;

            let documents = [];
            documents.push(verificationSubmit.personalDocs);
            documents.push(verificationSubmit.statusProofDocs);

            const proofsLen = proofs.length;
            for (let i=0; i<proofsLen; i++) {

                let doc = documents.find((f) => f.id == proofs[i].uid);
                proofs[i].verificationStatus = (doc.notOk) ? constants.accountVerificationProofStatus.failed : constants.accountVerificationProofStatus.passed;
                proofs[i].description = doc.description;

                let dateElements = doc.Elements.find((f) => f.type == 'DATE');
                let areDateElements = dateElements.find((f) => f.verficationElementId);
                if (areDateElements.length) {
                    const proofDetailsLen = (proofs[i].identityProofDocumentDetails).length;
                    for (let j=0; j<proofDetailsLen; j++) {

                        if (areDateElements.find((f) => f == proofs[i].identityProofDocumentDetails[j].documentElementId)) {
                            proofs[i].value = moment.utc(proofs[i].identityProofDocumentDetails[j].documentElementId).format('yyyy-MM-dd');
                        }
                    }
                }
            }

            const updateVerificationRequestProofs = await this.service.updateVerificationRequestProofs(request.uid, verificationResponse.version, proofs);

            res.status(200).json(updateVerificationRequestProofs);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    permissions = async (req, res) => {
        try {
            const accountPermissions = await this.accountPermissionServ.accountPermissions();

            const accountPermissionsOrdered = accountPermissions.sort((a, b) => a.level - b.level);

            res.status(200).json(accountPermissionsOrdered);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    linkAccount = async (req, res) => {
        try {
            const request = req.body;

            const account = await this.service.getAccountDetails(request.accountId);

            let linkAccount = {
                linkAccountUid: request.linkedAccountId,
                permissionUid: request.permission
            };

            const linkedAccount = await this.service.linkAccount(account.uid, account.version, linkAccount);

            res.status(200).json(linkedAccount);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    unlinkAccount = async (req, res) => {
        try {
            const request = req.body;

            const account = await this.service.getAccountDetails(request.accountId);

            const unLinkAccount = await this.service.unlinkAccount(account.uid, account.version, request.linkedAccountId);

            res.status(200).json(true);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    defaultRider = async (req, res) => {
        try {
            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            let riderType = await this.fareTableModel.RiderTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider', isDefault: true});

            if (!riderType) {
                riderType = {
                    externalId: constants.defaults.accountId,
                    name: constants.defaults.accountName
                };
            }
            res.status(200).json(riderType);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    editRequest = async (req, res) => {
        try {
            const verificationSubmit = req.body.verificationSubmit;

            const request = verificationSubmit.request;

            let verificationRequest = {
                version: request.version,
                description: request.description,
                accountVerificationStatus: request.status,
                canCorrectRequest: request.canCorrect,
                caseoncessionStartTimestamp: (request.concessionStartTimestamp) ? moment.utc(request.concessionStartTimestamp).toISOString() : '',
                concessionExpirationTimestamp: (request.concessionExpirationTimestamp) ? moment.utc(request.concessionExpirationTimestamp).toISOString() : ''
            };

            let verificationResponse = await this.service.updateVerificationRequest(request.uid, verificationRequest);
            let proofs = verificationResponse.accountIdentityProofs;

            let documents = [];
            documents.push(verificationSubmit.personalDocs);
            documents.push(verificationSubmit.statusProofDocs);

            const proofsLen = proofs.length;
            for (let i=0; i<proofsLen; i++) {
                let doc = documents.find((f) => f.id == proofs[i].verificationDocumentId);

                const proofDetailsLen = (proofs[i].identityProofDocumentDetails).length;
                for (let j=0; j<proofDetailsLen; j++) {

                    let element = doc.elements.find((f) => f == proofs[i].IdentityProofDocumentDetails[j].documentElementId);
                    if (element) {
                        if (element.type == 'DATE' || element.type == 'DATE_PAST_ONLY' || element.type == 'DATE_FUTURE_ONLY') {
                            item.value = moment.utc(element.value).format(this.dateRFormat);
                        } else if (element.Type == 'IMAGE') {
                            item.fileType = 'jpg';
                            item.value = element.value;
                        } else {
                            item.value = element.value;
                        }
                    }
                }
            }

            const updateVerificationRequestProofs = await this.service.updateVerificationRequestProofs(request.uid, verificationResponse.version, proofs);

            res.status(200).json(updateVerificationRequestProofs);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    topupRange = async (req, res) => {
        try {
            const riderTypeId = req.query.riderType;
            const decimalPlaces = req.query.decimalPlaces;

            const topupRange = await this.topupProduct(riderTypeId);

            res.status(200).json(topupRange);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    topupProduct = async (riderTypeId) => {
        try {
            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const riderType = await this.fareTableModel.RiderTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, type: 'Rider', externalId: riderTypeId});

            let topupRange = {};

            if (riderType.storedValue) {
                const product = await this.fareTableModel.Products.findOne({fareEnvironmentConfigurationId: activeConfig._id, productType: constants.productType.topUpRelative, purchasableAccess: {$in: String(riderType.externalId)}, 'sellableAccess.deviceType': constantsXml.deviceType.officePos, $or:[{sellingStartDay: {$eq: null}}, {sellingStartDay: {$lte: moment.utc().date()}}, {sellingEndDay: {$eq: null}}, {sellingEndDay: {$gte: moment.utc().date()}}]});
                const mediapriceaccounts = await this.fareTableModel.MediaPriceAccounts.findOne({fareEnvironmentConfigurationId: activeConfig._id, riderTypeExternalIds: {$in: String(riderType.externalId)}});
                const smartcardprice = await this.fareTableModel.SmartCardPrices.findOne({fareEnvironmentConfigurationId: activeConfig._id, productEntityClass: constants.productEntityClass.smartCardPrice, purchasableAccess: {$in: String(riderType.externalId)}});

                const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});
                const vatRate = (vatRates.find((f) => [constants.productType.topUpRelative].includes(f.associatedProductTypes))) ? vatRates.find((f) => [constants.productType.topUpRelative].includes(f.associatedProductTypes)) : vatRates.find((f) => f.isDefault == true);

                if (product) {
                    topupRange.id = product.externalId;
                    topupRange.name = product.name;
                    topupRange.type = product.productType;
                    topupRange.duration = (product.duration) ? product.duration : 36525; // 100 years
                    topupRange.min = (product.minimumPrice) ? (product.minimumPrice / Math.pow(10, global.currentPosData.currencyDecimal)) : 0;
                    topupRange.max = (product.maximumPrice) ? (product.maximumPrice / Math.pow(10, global.currentPosData.currencyDecimal)) : 0;
                    topupRange.exist = true;
                }

                if (mediapriceaccounts) {
                    topupRange.accountMinBalance = mediapriceaccounts.minBalanceInCents / Math.pow(10, global.currentPosData.currencyDecimal);
                    topupRange.accountMaxBalance = mediapriceaccounts.maxBalanceInCents / Math.pow(10, global.currentPosData.currencyDecimal);
                }

                if (smartcardprice) {
                    topupRange.smartcardPrice = smartcardprice.priceInCents / Math.pow(10, global.currentPosData.currencyDecimal);
                    topupRange.smartcardReplacementPrice = smartcardprice.replacementPrice / Math.pow(10, global.currentPosData.currencyDecimal);
                    topupRange.smartcardMinBalance = smartcardprice.minimumBalance / Math.pow(10, global.currentPosData.currencyDecimal);
                    topupRange.smartcardMaxBalance = smartcardprice.maximumBalance / Math.pow(10, global.currentPosData.currencyDecimal);
                }

                topupRange.vatRate = (vatRate) ? vatRate.percents : 0;
            }

            return topupRange;
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    registerCard = async (req, res) => {
        try {
            const accountUid = req.body.id;
            const accountShortId = req.body.shortId;
            const cardId = req.body.uid;
            const currentTimestamp = moment.utc().format(this.dateTTimeFormat);

            /*const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const transitTypeDef = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id}).sort({externalId: 1});

            await this.posTransactionCtrl.create({
                function: 'smartcard',
                component: 'ticketPrinter',
                parameters: {
                    fare: {
                        externalId: transitTypeDef.externalId,
                        name: transitTypeDef.name,
                        fareSystemModel: transitTypeDef.fareSystemModel,
                        routeSystemType: transitTypeDef.routeSystemType
                    },
                    financial: {
                        activityType: constantsXml.activityType.manageAccount,
                        fareItemCategory: constantsXml.fareItemCategory.accountIdentifierMedia,
                        fareItemType: constantsXml.fareItemType.registerAndAttachAccountIdentifierMedia,
                        fareItemExternalId: 1,
                        fareItemIssuedNumber: parseInt(cardId),
                        fareItemIssuedExpirationTimeStamp: currentTimestamp,
                        fareItemName: constantsXml.fareItemName.na,
                        payingRiderTypeExternalId: 1,
                        payingRiderTypeName: constantsXml.riderType.adult,
                        payingRiderAccountId: accountShortId,
                        cumulativeFareOrProductPrice: 0,
                        penaltyFee: 0,
                        paymentInstrument: constantsXml.paymentInstrument.none,
                        paymentMethod: constantsXml.paymentMethod.none,
                        currencyCode: global.currentPosData.currencyCode,

                        accountIdentifierMediaId: cardId,
                        accountIdentifierMediaType: constantsXml.mediaType.staticNonTransportationNfcTag,
                        accountIdentifierMediaTechnologyType: constantsXml.mediaTechnologyType.nfcNtag
                    }
                }
            });*/

            const registerCard = await this.service.registerCard(accountUid, cardId);

            res.status(200).json(true);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    deregisterCard = async (req, res) => {
        try {
            const accountUid = req.body.id;
            const accountShortId = req.body.shortId;
            const cardId = req.body.uid;
            const currentTimestamp = moment.utc().format(this.dateTTimeFormat);

            /*const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const transitTypeDef = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id}).sort({externalId: 1});

            await this.posTransactionCtrl.create({
                function: 'smartcard',
                component: 'ticketPrinter',
                parameters: {
                    fare: {
                        externalId: transitTypeDef.externalId,
                        name: transitTypeDef.name,
                        fareSystemModel: transitTypeDef.fareSystemModel,
                        routeSystemType: transitTypeDef.routeSystemType
                    },
                    financial: {
                        activityType: constantsXml.activityType.manageAccount,
                        fareItemCategory: constantsXml.fareItemCategory.accountIdentifierMedia,
                        fareItemType: constantsXml.fareItemType.deAttachAccountIdentifierMedia,
                        fareItemExternalId: 1,
                        fareItemIssuedNumber: parseInt(cardId),
                        fareItemIssuedExpirationTimeStamp: currentTimestamp,
                        fareItemName: constantsXml.fareItemName.na,
                        payingRiderTypeExternalId: 1,
                        payingRiderTypeName: constantsXml.riderType.adult,
                        payingRiderAccountId: accountShortId,
                        cumulativeFareOrProductPrice: 0,
                        penaltyFee: 0,
                        paymentInstrument: constantsXml.paymentInstrument.none,
                        paymentMethod: constantsXml.paymentMethod.none,
                        currencyCode: global.currentPosData.currencyCode,

                        accountIdentifierMediaId: cardId,
                        accountIdentifierMediaType: constantsXml.mediaType.staticNonTransportationNfcTag,
                        accountIdentifierMediaTechnologyType: constantsXml.mediaTechnologyType.nfcNtag
                    }
                }
            });*/

            const deregisterCard = await this.service.deregisterCard(accountUid, cardId);

            res.status(200).json(true);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    getTicketTransactionLog = async (req, res) => {
        try {
            const fareItemIssuedNumber = req.body.fareItemIssuedNumber;

            const getTicketTransactionLog = await this.service.getTicketTransactionLog(fareItemIssuedNumber);

            res.status(200).json(getTicketTransactionLog);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

}

module.exports = AccountHolderCtrl;
