const fs = require('fs');
const path = require('path');
const log = require('loglevel');

const constants = require('../models/constants');

class PaymentGatewayServ {
    constructor () {
        this.axios = global.axiosInstances.axiosPosInstance;
    }

    checkout = async (checkoutData) => {
        try {
            const res = await this.axios.post('peripherals/edcTerminal', checkoutData);
            if (typeof res === 'undefined' || res.status >= 400) { return null; }
            return res;
        } catch (err) {
            log.error(err);
        }
    }

    tagLengthValueData = async (mode, data) => {
        try {
            let tlv = null;
            let structure = null;

            if (['hafil'].includes(process.env.CLIENT_NAME)) {
                structure = {
                    '': 'transaction_type',
                    '02': 'card_number',
                    '04': 'transaction_amount',
                    '05': 'additional_amount',
                    '07': 'date_time',
                    '08': 'version',
                    '14': 'expiry_date',
                    '22': 'entry_mode',
                    '34': 'detailed_exception',
                    '35': 'card_data',
                    '37': 'retrieval_reference_number',
                    '38': 'approval_code',
                    '39': 'response_code',
                    '41': 'terminal_id',
                    '42': 'merchant_id',
                    '53': 'emv_data',
                    '54': 'card_label',
                    '55': 'card_type',
                    '57': 'command_identifier',
                    '58': 'custom_data_2',
                    '59': 'custom_data_3',
                    '61': 'ecr_unique_trace',
                    '62': 'unknown_field_62',
                    '96': 'additional_printing_flag',
                    '97': 'external_device_invoice',
                    '98': 'card_holder_name',
                    '99': 'employee_id'
                };
            }

            if (mode == 'tlv') {
                tlv = '';

                tlv = Object.keys(data).map((m) => (Object.keys(structure).find((f) => structure[f] === m) + String(data[m].length).padStart(2, '0') + data[m])).join('').slice(2);
            }

            if (mode == 'json') {
                tlv = {};

                for (let i=0; i<data.length; i++) {
                    tlv[structure[data[i].tag]] = data[i].value;
                }
            }

            return tlv;
        } catch (err) {
            log.error(err);
        }
    }

    parseTagLengthValueList = async (str) => {
        var headerLength = 4;
        var header = str.slice(headerLength);

        var tags = [{tag: '', length: headerLength, value: str.slice(0, headerLength)}];

        var i = headerLength;
        while (i < str.length) {
            var tag = str.substring(i, i + 2);
            i += 2;
            var valueLength = Number(str.substring(i, i + 2));
            i += 2;
            var value = str.substring(i, i + valueLength);
            i += valueLength;
            tags.push({tag: tag, length: valueLength, value: value});
        }

        return tags;
    }

    parseTagLengthValueCommand = async (tlvData) => {
        let tlvDataHex = tlvData.split('').map((m) => m.charCodeAt(0).toString(16));
        let tlvDataHexWithEtx = [].concat(tlvDataHex, constants.edcTerminalCommands.ETX);
        let dataLrc = 0;
        let tlvDataLrc = tlvDataHexWithEtx.map((m) => String.fromCharCode(parseInt(m, 16))).map((m) => (dataLrc ^= m.charCodeAt(0)));;
        let tlvDataLrcHex = Number(tlvDataLrc[tlvDataLrc.length-1]).toString(16);
        return [].concat(constants.edcTerminalCommands.STX, tlvDataHexWithEtx, tlvDataLrcHex);
    }

    hexStringToTlv = async (hexString) => {
        let hex = hexString.replace(/ /g, '');
        let str = '';
        for (let i = 0; i < hex.length; i += 2) {
            const hexValue = hex.substr(i, 2);
            const decimalValue = parseInt(hexValue, 16);
            str += String.fromCharCode(decimalValue);
        }
        return this.getTlvMessage(str);
    }

    hexStringToArrayBuffer = async (hexString) => {
        return new Uint8Array(hexString.map((m) => parseInt(m, 16)));
    }

    getTlvMessage = async (tlv) => {
        return tlv.slice(1, -2).toString('hex');
    }

    writeCommand = async (serial, command) => {
        return new Promise((resolve, reject) => {
            serial.write(command);
            try {
                serial.once('data', (data) => {
                    resolve(data.toString('hex'));
                });
            } catch (err) {
                reject(err);
            }
        });
    }

    testEdcTerminal = async (type) => {
        try {
            let edcReq = {};
            let edcRes = {};
            if (type == 'C200') {
                edcReq = {
                    transaction_type: 'C200',
                    transaction_amount: '000000000100',
                    command_identifier: '000075',
                    ecr_unique_trace: '00109040112121200001'
                };
                edcRes = {
                    transaction_type: 'R200',
                    transaction_amount: '000000000100',
                    command_identifier: '000075',
                    ecr_unique_trace: '00109040112121200001',
                    card_number: '5413330089010558',
                    date_time: '0401121212',
                    expiry_date: '1010',
                    retrieval_reference_number: '121212121212',
                    approval_code: '000011',
                    response_code: '00',
                    unknown_field_62: '000022',
                    card_holder_name: 'Alex',
                    card_type: 'M'
                };
            }
            if (type == 'C902') {
                edcReq = {
                    transaction_type: 'C902'
                };
                edcRes = {
                    transaction_type: 'R902',
                    response_code: '00'
                };
            }

            const edcReqTlv = await this.tagLengthValueData('tlv', edcReq);
            //log.info('edcReqTlv', edcReqTlv);
            const edcReqCom = await this.parseTagLengthValueCommand(edcReqTlv);
            //log.info('edcReqCom', edcReqCom);
            const edcReqTlvList = await this.parseTagLengthValueList(edcReqTlv);
            const edcReqTlvJson = await this.tagLengthValueData('json', edcReqTlvList);
            //log.info('edcReqTlvList', edcReqTlvList);

            const edcResTlv = await this.tagLengthValueData('tlv', edcRes);
            //log.info('edcResTlv', edcResTlv);
            const edcResCom = await this.parseTagLengthValueCommand(edcResTlv);
            //log.info('edcResCom', edcResCom);
            const edcResTlvList = await this.parseTagLengthValueList(edcResTlv);
            const edcResTlvJson = await this.tagLengthValueData('json', edcResTlvList);
            //log.info('edcResTlvList', edcResTlvList);

            log.info('command', edcReqTlvJson, edcReqCom.join(' '));
            log.info('response', edcResTlvJson, edcResCom.join(' '));

            return edcRes;
        } catch (err) {
            log.error(err);
        }
    }

}

module.exports = PaymentGatewayServ;
