package api.wsit.request_helper;

import api.wsit.product_request.BankGuarantee;
import api.wsit.product_request.ImportLettersOfCredit;
import api.wsit.product_request.SupplyChainFinance;
import com.alibaba.fastjson.JSON;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.json.JSONException;
import org.json.JSONObject;
import security.MessageLevel;
import api.wsit.product_request.execute.ApiSchema;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static security.AuthUtility.generateAuthTokens;
import static security.KeyUtility.*;
import static security.MessageLevel.*;
import static util.HttpClientUtil.doRequest;
import static util.HttpClientUtil.generateRequestHeaders;
import static util.JsonUtil.getJsonObjectFromFile;
import static util.JsonUtil.isValidJson;

public class WsitApiRequest {

    public static void main(String[] args) throws Exception {

        // 1. Prepare Request to HSBC API to HSBC API Services
        System.out.println("----- #Start of API Sample Code Steps# -----");
        String httpMethod = HttpPost.METHOD_NAME; //Input ENUM. Value: POST, PUT, PATCH, GET, DELETE
        String apiSchema = ApiSchema.Edge.SCHEMA_NAME; //Input ENUM. Value: EDGE, GTRF. "EDGE" for API Version >= 3.0.0. Otherwise, for previous version, input "GTRF".
        System.out.println("-----#1. Start of loading Security Credentials-----");
        String bankPgpPublicKeyFile = "src/main/resources/keys/MY SANDBOX PROJECT/hsbc-public.key";  //Input bank public key file location
        String clientPgpPrivateKeyFile = "src/main/resources/keys/MY SANDBOX PROJECT/client-private.key";// Input client private key file location
        String clientPrivateKeyPassphrase = "1password"; //Input passPhrase of your key
        // 1.1 Preparing PGP Keys
        System.out.println("#1.1. Preparing PGP Keys...");
        // 1.1.1 Retrieves Bank PGP Public Keys
        System.out.println("#1.1.1 Retrieving Bank PGP public keys...");
        List<PGPPublicKey> bankPgpPublicKeys = getPublicKeys(bankPgpPublicKeyFile);
        System.out.println("#1.1.2 Setting one of the Bank PGP Public Keys as encryption key...");
        PGPPublicKey bankPgpPublicKey = getEncryptionKey(apiSchema, bankPgpPublicKeys);
        // 1.1.2. Retrieves Client PGP Private Keys
        System.out.println("#1.2.1 Retrieving Client PGP private keys...");
        List<PGPPrivateKey> clientPgpPrivateKeys = getPrivateKeys(clientPgpPrivateKeyFile, clientPrivateKeyPassphrase);
        System.out.println("#1.2.2 Setting one of the Client PGP Private Keys as signature key...");
        PGPPrivateKey clientPgpPrivateKey = clientPgpPrivateKeys.get(0);
        System.out.println("-----End of #1. loading Security Credentials-----\n");

// 2. Prepare Request Payload
System.out.println("-----Start of #2. Preparing the HTTP Request Payload...");
// 2.1 Prepare Request JSON String
/*JSONObject requestJson = new JSONObject() {
    {
        put("paramObjectKey001",new JSONObject(){
            {
                put("paramKey001","paramValue001");
                put("paramKey002","paramValue002");
            }
        });
    }*/
JSONObject requestJson = new JSONObject(getJsonObjectFromFile(BankGuarantee.V1.QueryBankGuaranteeStatus.REQUEST_JSON_FILE).toString());
String textToEncrypt = null;
if (httpMethod != HttpGet.METHOD_NAME) {
    textToEncrypt = requestJson.toString();
    //textToEncrypt = "{\"paramObjectKey001\":{\"paramKey001\": \"paramValue001\",\"paramKey002\": \"paramValue002\",...}}";
}
System.out.println("#2.1. Request Content JSON String: \n" + textToEncrypt);
HashMap<String, String> payloadWrappers = new HashMap<String, String>();
Boolean xHsbcCryptoSignature = false;
switch (apiSchema) {
    case (ApiSchema.Edge.SCHEMA_NAME):
        xHsbcCryptoSignature = true;
        payloadWrappers.put("request", "encryptedRequestBase64");
        payloadWrappers.put("response", "encryptedResponseBase64");
        break;
    case (ApiSchema.Gtrf.SCHEMA_NAME):
        xHsbcCryptoSignature = false;
        payloadWrappers.put("request", null);
        payloadWrappers.put("response", null);
        break;
}
// 2.2 Generate Request Payload
System.out.println("#2.2. Start of Generating Request Payload");
String requestPayload = generateRequestPayload(httpMethod, payloadWrappers, textToEncrypt, bankPgpPublicKey, clientPgpPrivateKey, xHsbcCryptoSignature);
System.out.println("-----End of #2. Preparing the HTTP Request Payload...\n");

        // 3. Generate Authorization Token
        System.out.println("-----Start of #3. Generating Authorization Token-----");
        String clientProfileId = "APLpQnZ6b6ywmicmp7wvg9RjU277rFCF"; // API Profile ID Bank Assigned to you.
        String oboProfileId = "customer001"; //Customer's ID on your platform. Input null if you are a Direct Client.
        HashMap<String, Object> additionalTokenClaims = new HashMap<>();
        Map<String, String> authTokens = generateAuthTokens(httpMethod, apiSchema, clientProfileId, oboProfileId, additionalTokenClaims, requestPayload, bankPgpPublicKey, clientPgpPrivateKey, xHsbcCryptoSignature);
        System.out.println("-----End of of #3. Generating Authorization Token-----\n");

        // 4. Generate Request
        System.out.println("-----Start of #4. Executing API Request-----");
        //4.1 Generate URI Parameters if required:
        HashMap<String, String> requestUriParameters = new HashMap<String, String>() {
            {
                //put("paramKey001", "paramValue001"); //Input only if required
            }
        };
        //4.2 Generate Request Headers
        String clientHostingRegion = "IN"; // Your Platform or Business Operating Region, In ISO3166-alpha-2
        HashMap<String, String> additionalRequestHeaders = new HashMap<String, String>() {
            {
                //put("headerKey001", "headerValue001"); //Input only if required
            }
        };
        HashMap<String, String> requestHeaderJson = generateRequestHeaders(httpMethod, apiSchema, authTokens, clientHostingRegion, oboProfileId, xHsbcCryptoSignature, additionalRequestHeaders);
        //4.3 Send API Request and save the response as String
        //String apiEndpointUrl = "https://sandbox.corporate-api.hsbc.com/......"; //Input your target URL
        String apiEndpointUrl = "https://sandbox.corporate-api.hsbc.com" + SupplyChainFinance.V1.SubmitApprovedInvoices.API_ENDPOINT_URLS.get("SANDBOX"); //Upload Invoice V1
        //String apiEndpointUrl = "https://sandbox.corporate-api.hsbc.com" + SupplyChainFinance.V1.EnquireInvoiceSummaryList.API_ENDPOINT_URLS.get("SANDBOX"); //Upload Invoice V1*/
        String responseMessage = doRequest(httpMethod, apiEndpointUrl, requestUriParameters, requestHeaderJson, requestPayload);
        System.out.println("-----End of #4. Executing API Request-----\n");

        //5. Process Response Message from Bank and save the output as String
        System.out.println("-----Start of #5 Start of Processing API Response-----");
        String processedMessage = processResponseMessage(payloadWrappers, responseMessage, bankPgpPublicKeys, clientPgpPrivateKeys, xHsbcCryptoSignature);
        System.out.println("-----End of #5 Start of Processing API Response-----\n");
        System.out.println("----- #End of API Sample Code Steps# -----");
    }
    public static String generateRequestPayload(String httpMethod, HashMap<String, String> payloadWrappers, String textToEncrypt, PGPPublicKey bankPgpPublicKey, PGPPrivateKey clientPgpPrivateKey, Boolean xHsbcCryptoSignature) {
        String requestPayload = null;
        String base64EncryptedString = null;
        switch (httpMethod) {
            case HttpGet.METHOD_NAME:
                requestPayload = "";
                break;
            case HttpPost.METHOD_NAME:
                String bankPgpKeyId = Long.toHexString(bankPgpPublicKey.getKeyID()).toUpperCase();
                if (xHsbcCryptoSignature) {
                    base64EncryptedString = encryptAndSign(textToEncrypt, bankPgpPublicKey, clientPgpPrivateKey);
                } else if (!(xHsbcCryptoSignature)) {
                    base64EncryptedString = encrypt(textToEncrypt, bankPgpPublicKey);
                }
                System.out.println("  #a. Request Payload: ");
                String payloadWrapper = payloadWrappers.get("request");
                if (payloadWrapper == null) {
                    System.out.println("  #a. API Schema: GTRF - Encrypted Base64 String WITHOUT payload wrapper, used for APIs versions < 3.0.0\n  #b. Encryption Scheme: Bank public key encrypted WITHOUT Client Private Key Signature\n  #c. PGP Key Ids:\n  #c.1. Encryption Key Id (Bank PGP Public Key): "  + bankPgpKeyId);
                    requestPayload = base64EncryptedString;
                } else {
                    System.out.println("  #a. API Schema: EDGE - Encrypted Base64 String WITH payload wrapper, it is used for API version >=3");
                    JSONObject requestPayloadJson = new JSONObject();
                    try {
                        if (xHsbcCryptoSignature == true) {
                            String clientPgpKeyId = Long.toHexString(clientPgpPrivateKey.getKeyID()).toUpperCase();
                            System.out.println("  #b. Encryption Scheme: Bank public key encrypted WITH Client Private Key Signature\n  #c. PGP Key IDs:\n  #c.1. Encryption Key Id (Bank PGP Public Key): "  + bankPgpKeyId + "\n  #c.2. Signing Key Id (Client PGP Private Key): " + clientPgpKeyId);
                        } else {
                            System.out.println("  #b. Encryption Scheme: Bank public key encrypted WITHOUT Client Private Key Signature\n  #c. PGP Key IDs:\n  #c.1. Encryption Key Id (Bank PGP Public Key): "  + bankPgpKeyId);
                        }
                        requestPayloadJson.put(payloadWrapper, base64EncryptedString);
                        requestPayload = requestPayloadJson.toString();
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
        }

        System.out.println("  #d. Request Payload String: \n" + requestPayload + "\n");
        return requestPayload;
    }

    public static String processResponseMessage(HashMap<String, String> payloadWrappers, String responseMessage, List<PGPPublicKey> bankPgpPublicKeys, List<PGPPrivateKey> clientPgpPrivateKeys, Boolean xHsbcCryptoSignature) {
        String payloadWrapper = payloadWrappers.get("response");
        String textToDecrypt = null;
        String processedMessage = null;

        try {
            JSONObject responseMessageJson = new JSONObject(responseMessage);
            if (responseMessageJson.has(payloadWrapper)) {
                System.out.println("#a.1. Response Message Schema: EDGE");
                textToDecrypt = responseMessageJson.getString(payloadWrapper);
            }
            else {
                textToDecrypt = responseMessage;
            }
        } catch (JSONException jsonException) {
            textToDecrypt = responseMessage;
        }
        try {
            if (MessageLevel.isBase64(textToDecrypt)) {
                if (xHsbcCryptoSignature) {
                    System.out.println("#a.2. Encryption Scheme: Client Public Key Encrypted WITH Bank Private Key Signed");
                    processedMessage = decryptAndVerifySignature(textToDecrypt, bankPgpPublicKeys, clientPgpPrivateKeys);
                } else {
                    System.out.println("#a.2. Encryption Scheme: Client Public Key Encrypted WITHOUT Bank Private Key Signed");
                    processedMessage = decrypt(textToDecrypt, clientPgpPrivateKeys);
                }
            }
        } catch (Exception e) {
            processedMessage = responseMessage;
        }
        try {
            System.out.println(new JSONObject(processedMessage).toString(4));
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return processedMessage;
    }
}
