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 countries = require('../files/countries3.json');

const BookingServ = require('../services/booking');
const ReasonsServ = require('../services/reasons');
const FinancialTransactionServ = require('../services/financial-transaction');
const FareEnvironmentConfiguration = require('../models/fare-environment-configuration');

class BookingCtrl {
    constructor () {
        this.service = new BookingServ();

        this.reasonsServ = new ReasonsServ();
        this.financialTransactionServ = new FinancialTransactionServ();
        this.fareEnvironmentModel = FareEnvironmentConfiguration;
    }

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

            const createBooking = await this.service.createBooking(booking);

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

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

            const booking = await this.service.getBookingByReference(reference);

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

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

            const booking = await this.service.getBookingByFareItemIssuedNumber(fareItemIssuedNumber);

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

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

            const booking = await this.service.getBookingByDocumentReference(documentReference);

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

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

            const booking = await this.service.getBookingByETicketReference(eTicketReference);

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

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

            const booking = await this.service.getBookingByPassengerFareItemIssuedNumber(fareItemIssuedNumber);

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

    cancelBooking = async (req, res) => {
        try {
            const reference = req.body.reference;
            const reasonId = req.body.reasonId;

            const cancelBooking = await this.service.cancelBooking(reference, reasonId);

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

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

            const booking = await this.service.getBookingByFareItemIssuedNumber(fareItemIssuedNumber);

            const timestampFilter = moment.utc(booking.passengers[0].creationTimestamp).startOf('day').toISOString();

            const bookingTransactionLog = await this.service.getTicketTransactionLog(fareItemIssuedNumber, timestampFilter);

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

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

            let countriesList = [];
            const countriesLen = country.length;
            for (let i=0; i<countriesLen; i++) {
                countriesList.push({
                    code: country[i].alpha3,
                    name: country[i].name
                });
            }

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

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

            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 });
        }
    }

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

            const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            const origStation = await this.fareEnvironmentModel.Stations.findOne({fareEnvironmentConfigurationId: activeConfig._id, stationCode: qrCodeData.origStationCode});
            const destStation = await this.fareEnvironmentModel.Stations.findOne({fareEnvironmentConfigurationId: activeConfig._id, stationCode: qrCodeData.destStationCode});
            const origStop = await this.fareEnvironmentModel.Stops.findOne({fareEnvironmentConfigurationId: activeConfig._id, stopCode: qrCodeData.origStopCode});
            const destStop = await this.fareEnvironmentModel.Stops.findOne({fareEnvironmentConfigurationId: activeConfig._id, stopCode: qrCodeData.destStopCode});
            const transitType = await this.fareEnvironmentModel.TransitTypes.findOne({fareEnvironmentConfigurationId: activeConfig._id, name: qrCodeData.routeTypeName});
            const route = await this.fareEnvironmentModel.Routes.findOne({fareEnvironmentConfigurationId: activeConfig._id, name: qrCodeData.route, destination: qrCodeData.routeDestination});

            let fareItemType = enumsXml.fareItemType[qrCodeData.fareItemType];
            let fareItemCategory = enumsXml.fareItemCategoryType[qrCodeData.fareItemCategory];
            let statusRemainingRides = 1;

            const issuingTimestamp = moment.utc(qrCodeData.timestamp).format(this.dateTTimeFormat);
            const expirationTimestamp = moment.utc(qrCodeData.ticketExpiryTimestamp).format(this.dateTTimeFormat);
            //const tripTimestamp = moment.utc(0).format(this.dateTTimeFormat);
            const boardingTimestamp = moment.utc(qrCodeData.tripStartTimestamp).format(this.dateTTimeFormat);
            const alightingTimestamp = moment.utc(qrCodeData.tripEndTimestamp).format(this.dateTTimeFormat);

            const qrCodeOneway = {
                qrCodeIssuingTimestamp: issuingTimestamp,
                qrCodeExpirationTimestamp: expirationTimestamp,
                qrCodeIssuingTerminalId: qrCodeData.terminalId,
                qrCodeMerchantOrganizationId: 0,
                fareItemExternalId: qrCodeData.fareItemExternalId,
                fareItemTypeShortNumber: fareItemType,
                fareItemCategoryShortNumber: fareItemCategory,
                fareItemIssuedNumber: qrCodeData.fareItemIssuedNumber,
                fareItemIssuedStatus: enumsXml.fareItemStatus.active,
                issuedBoardingStation: (origStation) ? origStation.machineId : 0,
                issuedAlightingStation: (destStation) ? destStation.machineId : 0,
                issuedBoardingStop: (origStop) ? origStop.machineId : 0,
                issuedAlightingStop: (destStop) ? destStop.machineId : 0,
                issuedRouteType: (transitType) ? transitType.shortNumber : 0,
                contractCategoryShortNumber: 0,
                fareItemStatus: enumsXml.fareItemStatus.active,
                fareItemStatusLastChangedTimestamp: issuingTimestamp,
                fareItemStatusTransfersLeft: 0,
                fareItemStatusRemainingRides: statusRemainingRides,
                fareItemExpirationTimestamp: expirationTimestamp,
                accountRiderType: 0,
                accountId: 0,
                issuedTripId: 0,
                issuedRoute: (route) ? route.machineId : 0,
                issuedTripStartTimestamp: boardingTimestamp,
                issuedTripEndEstimatedTimestamp: alightingTimestamp
            };

            const qrCode = await this.financialTransactionServ.generateQrCode(qrCodeOneway);

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

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

            //const activeConfig = await this.fareEnvironmentModel.Configuration.findOne({distributionVersion: global.currentPosData.distributionVersion, isActive: true});
            //const vatRates = await this.fareTableModel.VatRates.find({fareEnvironmentConfigurationId: activeConfig._id});
            //const vatRate = vatRates.find((f) => f.isDefault == true);

            const issuingTimestamp = moment.utc(qrCodeData.timestamp);

            const qrCodeTax = {
                organizationName: qrCodeData.organizationName,
                organizationVatNumber: qrCodeData.organizationVatNumber,
                timestamp: issuingTimestamp.format(this.dateTTimeFormat),
                invoiceTotal: (qrCodeData.totalCent / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal),
                vatTotal: (Math.round(qrCodeData.totalCent  * (qrCodeData.vatRate / 100) / (1 + (qrCodeData.vatRate / 100))) / Math.pow(10, global.currentPosData.currencyDecimal)).toFixed(global.currentPosData.currencyDecimal)
            };

            const qrCode = await this.financialTransactionServ.generateTaxQrCode(qrCodeTax);

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

}

module.exports = BookingCtrl;
