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

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

const InspectionServ = require('../services/inspection');

const FareEnvironmentConfiguration = require('../models/fare-environment-configuration');
const FareTable = require('../models/fare-table');
const EmployeeServ = require('../services/employee');
const AccountHolderServ = require('../services/account-holder');
const PosTransactionCtrl = require('./pos-transaction');

class InspectionCtrl {
    constructor () {
        this.service = new InspectionServ();

        this.fareEnvironmentModel = FareEnvironmentConfiguration;
        this.fareTableModel = FareTable;
        this.employeeServ = new EmployeeServ();
        this.accountHolderServ = new AccountHolderServ();
        this.posTransactionCtrl = new PosTransactionCtrl();
        this.dateTimeFormat = 'DD/MM/YYYY HH:mm:ss';
    }

    payPenalty = async (id, penalty) => {
        try {
            return await this.service.payPenalty(id, penalty);
        } catch (err) {
            log.error(err);
        }
    }

    search = async (req, res) => {
        try {
            const refNumber = req.body.value;
            const penalty = await this.service.getPenalty(refNumber);

            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: (penalty) ? penalty.riderTypeExternalId : 1});

            let penaltyResults = [];
            if (penalty) {
                penaltyResults.push({
                    riderTypeExternalId: penalty.riderTypeExternalId,
                    riderTypeName: (riderType) ? riderType.name : constants.defaults.accountName,
                    riderAccountId: (penalty.hasOwnProperty('riderAccountId')) ? penalty.riderAccountId.toString() : '',
                    riderAccountUid: penalty.riderAccountUid,
                    riderId: penalty.riderId,
                    riderName: penalty.riderName,
                    riderEmail: penalty.riderEmail,
                    riderMobile: penalty.riderMobile,
                    productId: penalty.fineProductId,
                    fineProductName: penalty.fineProductName,
                    issued: moment.utc(penalty.timestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                    refNumber: penalty.id,
                    value: penalty.priceCents / Math.pow(10, global.currentPosData.currencyDecimal),
                    status: penalty.status,
                    reason: penalty.reasonName,
                    comment: penalty.reasonDescription,
                    paymentDate: (penalty.hasOwnProperty('paymentTimestamp')) ? moment.utc(penalty.paymentTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat) : '',
                    riderAddress: penalty.address,
                    description: penalty.description
                });
            } else {
                const penalties = await this.service.getPenaltiesForAccount(refNumber);
                const penaltiesLen = penalties.length;

                for (let i=0; i<penaltiesLen; i++) {
                    penaltyResults.push({
                        riderTypeExternalId: penalties[i].riderTypeExternalId,
                        riderTypeName: (riderType) ? riderType.name : constants.defaults.accountName,
                        riderAccountId: (penalties[i].hasOwnProperty('riderAccountId')) ? penalties[i].riderAccountId.toString() : '',
                        riderAccountUid: penalties[i].riderAccountUid,
                        riderId: penalties[i].riderId,
                        riderName: penalties[i].riderName,
                        riderEmail: penalties[i].riderEmail,
                        riderMobile: penalties[i].riderMobile,
                        productId: penalties[i].fineProductId,
                        fineProductName: penalties[i].fineProductName,
                        issued: moment.utc(penalties[i].timestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                        refNumber: penalties[i].id,
                        value: penalties[i].priceCents / Math.pow(10, global.currentPosData.currencyDecimal),
                        status: penalties[i].status,
                        reason: penalties[i].reasonName,
                        comment: penalties[i].reasonDescription,
                        paymentDate: (penalties[i].hasOwnProperty('paymentTimestamp')) ? moment.utc(penalties[i].paymentTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat) : '',
                        riderAddress: penalty.address,
                        description: penalty.description
                    });
                }
            }
            res.status(200).json(penaltyResults);
        } catch (err) {
            log.error(err);
            res.status(400).json({ error: err.message });
        }
    }

    details = async (req, res) => {
        try {
            const refNumber = req.query.refNumber;
            const penalty = await this.service.getPenalty(refNumber);

            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: penalty.riderTypeExternalId});

            let details = {
                riderTypeExternalId: penalty.riderTypeExternalId,
                riderTypeName: (riderType) ? riderType.name : constants.defaults.accountName,
                riderAccountId: (penalty.hasOwnProperty('riderAccountId')) ? penalty.riderAccountId.toString() : '',
                riderAccountUid: penalty.riderAccountUid,
                riderId: penalty.riderId,
                riderName: penalty.riderName,
                riderEmail: penalty.riderEmail,
                riderMobile: penalty.riderMobile,
                productId: penalty.fineProductId,
                fineProductName: penalty.fineProductName,
                issued: moment.utc(penalty.timestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                refNumber: penalty.id,
                value: penalty.priceCents / Math.pow(10, global.currentPosData.currencyDecimal),
                status: penalty.status,
                reason: penalty.reasonName,
                comment: penalty.reasonDescription,
                paymentDate: (penalty.hasOwnProperty('paymentTimestamp')) ? moment.utc(penalty.paymentTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat) : '',
                riderAddress: penalty.address,
                description: penalty.description
            };

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

    read = async (req, res) => {
        try {
            const refNumber = req.query.refNumber;
            const penalty = await this.service.getPenalty(refNumber);

            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: penalty.riderTypeExternalId});

            let read = {
                riderTypeExternalId: penalty.riderTypeExternalId,
                riderTypeName: (riderType) ? riderType.name : constants.defaults.accountName,
                riderAccountId: (penalty.hasOwnProperty('riderAccountId')) ? penalty.riderAccountId.toString() : '',
                riderAccountUid: penalty.riderAccountUid,
                riderId: penalty.riderId,
                riderName: penalty.riderName,
                riderEmail: penalty.riderEmail,
                riderMobile: penalty.riderMobile,
                productId: penalty.fineProductId,
                fineProductName: penalty.fineProductName,
                issued: moment.utc(penalty.timestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat),
                refNumber: penalty.id,
                value: penalty.priceCents / Math.pow(10, global.currentPosData.currencyDecimal),
                status: penalty.status,
                reason: penalty.reasonName,
                comment: penalty.reasonDescription,
                paymentDate: (penalty.hasOwnProperty('paymentTimestamp')) ? moment.utc(penalty.paymentTimestamp).utcOffset(global.currentPosData.utcOffset).format(this.dateTimeFormat) : '',
                riderAddress: penalty.address,
                description: penalty.description
            };

            // const penaltiesOrdered = read.sort((a, b) => moment.utc(b.issued).diff(moment.utc(a.issued)));
            const penaltiesOrdered = _.orderBy(read, ['transactionDate'], ['desc']);

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

    issue = async (req, res) => {
        try {
            const account = req.body;
            const device = {
                version: global.currentPosData.deviceVersion,
                softwareVersion: global.currentPosData.appVersion,
                id: global.currentPosData.deviceId,
                sharedId: global.currentPosData.deviceSharedId,
                terminal: 2004,
                counter: 123
            };
            const task = {
                taskId: moment.utc().format('X'),
                inspectionType: 'ridersInspection'
            };
            const penalty = {
                ticket: moment.utc().format('X'),
                id: moment.utc().format('X'),
                reasonId: 2
            };

            const deviceType = constantsXml.deviceType.mobileInspectionDevice;
            const inspector = await this.employeeServ.getUsersForType(deviceType);
            const accountDetailsShort = await this.accountHolderServ.getAccountDetailsShort(account.short);

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const routeDef = await this.fareEnvironmentModel.Routes.findOne({fareEnvironmentConfigurationId: activeConfig._id}).sort({externalId: 1});
            const transitTypeDef = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id}).sort({externalId: 1});
            const product = await this.fareTableModel.Products.findOne({fareEnvironmentConfigurationId: activeConfig._id, productType: constants.productType.absolutePenalty});

            await this.posTransactionCtrl.create({
                function: 'penaltyIssue',
                component: {
                    deviceType: deviceType,
                    deviceVersion: device.version,
                    deviceSoftwareVersion: device.softwareVersion,
                    deviceId: device.id,
                    deviceSharedId: device.sharedId,
                    deviceComponentType: constantsXml.componentType.banknoteCashbox,
                    deviceComponentId: device.id,
                    terminalId: device.terminal,
                    deviceTransactionCounter: device.counter,
                    faretableTimestamp: global.currentPosData.faretableTimestamp,
                    operatorRef: inspector[0].id,
                    operatorTypeExternalId: inspector[0].employeeRoleExternalId,
                    operatorTypeName: inspector[0].employeeRoleName,
                    operationMode: constantsXml.operationMode.inspection
                },
                parameters: {
                    avl: {
                        externalId: routeDef.externalId,
                        name: routeDef.name,
                        destination: routeDef.destination,
                        variant: routeDef.variant,
                        version: routeDef.version,
                        vehicleLocationLongitude: global.currentPosData.locationLongitude,
                        vehicleLocationLatitude: global.currentPosData.locationLatitude
                    },
                    fare: {
                        externalId: transitTypeDef.externalId,
                        name: transitTypeDef.name,
                        fareSystemModel: transitTypeDef.fareSystemModel,
                        routeSystemType: transitTypeDef.routeSystemType
                    },
                    task: {
                        id: task.taskId,
                        type: task.inspectionType
                    },
                    penalty: {
                        ticketNumber: penalty.ticket,
                        penaltyId: penalty.id,
                        riderId: accountDetailsShort.uid,
                        riderAccountId: account.short,
                        riderName: accountDetailsShort.fullName,
                        riderAddress: accountDetailsShort.addressLine1,
                        riderEmail: accountDetailsShort.emailAddress,
                        penaltyReasonId: penalty.reasonId,
                        penaltyProductId: product.externalId,
                        penaltyAmount: product.priceInCents
                    }
                }
            });

            const issue = JSON.stringify(Object.assign({}, device, inspector[0], assignment, penalty, accountDetailsShort, product));

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

}

module.exports = InspectionCtrl;
