package com.pinelabs.service;

import com.pinelabs.exception.InputException;
import com.pinelabs.model.EncryptionPojo;
import com.pinelabs.utils.*;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class EncryptionServiceImpl implements EncryptionService {

    public static final String BASE_64 = "base64";
    public static final String HEX = "hex";

    @Override
    public Map processEncryption(EncryptionPojo encPojo) throws Exception {

        Map<String, String> map = new HashMap<>();
        boolean isPinRequest = Boolean.parseBoolean(encPojo.getIsPinRequestV());
        String sourceBlock = encPojo.getSourceBlock();


        String desKey = TripleDES.generateNewSecretKey(encPojo.getKeyLength()).toUpperCase();
        String correctedDesKey = desKey;
        if (encPojo.getKeyLength() == 112) {
            correctedDesKey = desKey.substring(0, 32);
        }
        map.put("DES", correctedDesKey);  // deskKey remove while passing to API call, required for decryption of response


        /*      // commented code for Json Payload encryption
          String inputData = "";
          String dataUnderDES = "";
          String varDataUnderDES=null;
          boolean isUnicodeRequest=Boolean.parseBoolean(encPojo.getIsUnicodeRequest());
        try {
            inputData = InputDataReader.getInputJsonData(encPojo);

        } catch (Exception e) {
            throw new InputException("Please check your input.. Unable to create proper json string..");
        }
        if(isUnicodeRequest){
            varDataUnderDES = Hex.encodeHexString(TripleDES.encryptData(desKey, DataUtils.adjustUnicodePadding(inputData, 8)));
        }else{
            varDataUnderDES = Hex.encodeHexString(TripleDES.encryptData(desKey, DataUtils.adjustPadding(inputData)));
        }*/

        String derEncodeDesKey = "";
        derEncodeDesKey = DataUtils.getderEncodeDesKey(encPojo.getKeyLength(), correctedDesKey);

        // get Rsa
        String varDesUnderRsaPk = RSA.encryptMessage("RSA/ECB/OAEPWithSHA-1AndMGF1Padding",
                                                     RSAKeyReader.getRSAPublicKeyInfo(), derEncodeDesKey);
        String desUnderRsaPk = "";
        if (HEX.equals(encPojo.getEncodingType())) {
         //   dataUnderDES = varDataUnderDES;
            desUnderRsaPk = varDesUnderRsaPk;
        } else if (BASE_64.equals(encPojo.getEncodingType())) {
        //    dataUnderDES = Base64.encodeBase64String(Hex.decodeHex(varDataUnderDES.toCharArray()));
            desUnderRsaPk = Base64.encodeBase64String(Hex.decodeHex(varDesUnderRsaPk.toCharArray()));
        }
        String data = "";
        if (isPinRequest) {
            if ("".equals(sourceBlock.trim())) {
                throw new InputException("pin block not specified");
            }
            String encSourceBlock = Hex.encodeHexString(TripleDES.encryptData(desKey, Hex.decodeHex(sourceBlock.toCharArray())));

            data = String.format("{sourceBlock:%s,encKey:%s}",
                                 encSourceBlock.toUpperCase(),  desUnderRsaPk.toUpperCase());
            map.put("data", data);
            return map;
        }

        /*  //  Json Payload response without pinblock request

        if (HEX.equals(encPojo.getEncodingType())) {
            data = String.format("{data:%s,encKey:%s}", dataUnderDES.toUpperCase(),
                                 desUnderRsaPk.toUpperCase());
            map.put("data", data);
        } else if (BASE_64.equals(encPojo.getEncodingType())) {
            data = String.format("{data:%s,encKey:%s}", dataUnderDES, desUnderRsaPk);
            map.put("data", data);
        }*/

        return map;
    }

    @Override
    public String processPinBlock(EncryptionPojo enPojo) throws InputException {
        String pin = enPojo.getClearPin();
        String deviceNumber = enPojo.getDeviceNumber();
        if (("".equals(pin)) || ("".equals(deviceNumber))) {
            throw new InputException("Option: clear pin Or device number not specified");
        } else {
            if (pin.length() > 12) {
                throw new InputException("PIN length not supported");
            }
            String pinBlock = String.format("%s%d%s", new Object[] { "0", Integer.valueOf(pin.length()), pin });

            while (pinBlock.length() != 16) {
                pinBlock = pinBlock + "F";
            }
            int deviceLen = deviceNumber.length();
            String pan = "0000" + deviceNumber.substring(deviceLen - 13, deviceLen - 1);

            return DataUtils.xorHex(pinBlock, pan);
        }
    }

    @Override
    public String processDecryption(EncryptionPojo enPojo) throws Exception {
        String desKey = enPojo.getDesKey();
        String encData = enPojo.getEncryptData();
        String encodingType = enPojo.getEncodingType();
        boolean isUnivcodeRequest=Boolean.valueOf(enPojo.getIsUnicodeRequest());
        byte[] varEncData = null;
        String clearData = "";
        if (HEX.equals(encodingType)) {
            varEncData = encData.getBytes();
        } else if (BASE_64.equals(encodingType)) {
            varEncData = Base64.decodeBase64(encData); //
        }
        if ("".equals(desKey) || "".equals(encData)) {
            throw new InputException("des-key Or enc-data not specified");
        } else {
            String correctedDesKey;
            switch (desKey.length()) {
                case 48:
                    if(isUnivcodeRequest){
                        byte[] response =TripleDES.decryptData(desKey, varEncData);
                        byte[] responseString = new String(response, StandardCharsets.ISO_8859_1).getBytes(StandardCharsets.ISO_8859_1);
                        clearData=new String(responseString,"UTF-8");
                    }else{
                        clearData = new String(TripleDES.decryptData(desKey, varEncData));
                    }
                    return DataUtils.formatTheResponse(clearData);
                case 32:
                    correctedDesKey = String.valueOf(desKey) + desKey.substring(0, 16);
                    clearData = new String(TripleDES.decryptData(correctedDesKey, varEncData));
                    return clearData;
                default:
                    clearData = "Invalid DES key";
                    throw new InputException(clearData);
            }
        }
    }



}
