package api.wsit.product_request.execute;
/*
 * ***************************************************************
 * , Copyright. HSBC Holdings plc ccyy ALL RIGHTS RESERVED.
 *
 * This software is only to be used for the purpose for which it
 * has been provided. No part of it is to be reproduced,
 * disassembled, transmitted, stored in a retrieval system or
 * translated in any human or computer language in any way or
 * for any other purposes whatsoever without the prior written
 * consent of HSBC Holdings plc.
 * ***************************************************************
 */

import api.wsit.product_request.BankGuarantee;
import api.wsit.product_request.BuyerLoans;
import com.alibaba.fastjson.JSON;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.ContentType;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.json.JSONObject;
import util.ApiSchema;
import util.Projects;

import java.security.Security;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import static security.AuthUtility.generateAuthTokens;
import static security.KeyUtility.*;
import static security.MessageLevel.encrypt;
import static util.HttpClientUtil.*;
import static util.JsonUtil.getJsonObjectFromFile;
import static api.wsit.request_helper.WsitApiRequest.generateRequestPayload;
import static api.wsit.request_helper.WsitApiRequest.processResponseMessage;


public class ApiRequestExecutor {

    /**
     * used to fix java.security.NoSuchProviderException: no such provider: BC
     */
    static {
        try {
            Security.addProvider(new BouncyCastleProvider());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public ApiRequestExecutor() {
    }

    public static void main(String[] args) throws Exception {
//        HashMap apiSettings = BuyerLoans.V1.SubmitBuyerLoanApplications.API_SETTINGS;
        HashMap apiSettings = BankGuarantee.V3.QueryBankGuaranteeStatus.API_SETTINGS;
        execute(apiSettings);
    }

    public static void execute(HashMap<String, Object> apiSettings) throws Exception {
        System.out.println("----- #Start of API Sample Code Steps# -----");
        System.out.println("-----#0. Start of Reading Active Project-----");
        HashMap<String, String> activeProjectSettings = Projects.LoadActiveProjectSettings();
        String httpMethod = apiSettings.get("httpMethod").toString();
        String contentType = apiSettings.get("contentType").toString();
        String apiSchema = apiSettings.get("apiSchema").toString(); //Input "EDGE" for API Version >= 3.0.0. Otherwise, for previous version, input "GTRF".
        //System.out.println("  #a. API Product Category: " + apiSettings.get("apiProductCategory").toString() + "\n  #b. API Version: " + apiSettings.get("apiVersion").toString() + "\n  #c. API Name: " + apiSettings.get("apiName").toString() + "\n  #d. HTTP Method: " + httpMethod + "\n  #e. Content-Type: " + contentType + "\n");
        System.out.println("  #a. API Product Category: " + apiSettings.get("apiProductCategory").toString());
        System.out.println("  #b. API Version: " + apiSettings.get("apiVersion").toString());

                System.out.println("-----#1. Start of loading Security Credentials-----");
        String bankPgpPublicKeyFile = activeProjectSettings.get("bankPublicKeyFile");
        String clientPgpPrivateKeyFile = activeProjectSettings.get("clientPrivateKeyFile");
        String clientPrivateKeyPassphrase = activeProjectSettings.get("clientPrivateKeyPassphrase"); //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);
        System.out.println(bankPgpPublicKey.getKeyID());
        // 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
        String textToEncrypt = null;
        if (httpMethod != HttpGet.METHOD_NAME && contentType.equals(ContentType.APPLICATION_JSON.getMimeType())) {
            textToEncrypt = getJsonObjectFromFile(apiSettings.get("requestJsonFile").toString()).toString();
        }
        /*String claimsToEncrypt = new JSONObject(apiSettings.get("additionalTokenClaims")).toString();
        System.out.println("#################"+claimsToEncrypt);*/
        System.out.println("#2.1. Request Content JSON String: \n" + textToEncrypt);
        HashMap<String, String> payloadWrappers = (HashMap<String, String>) apiSettings.get("payloadWrappers");
        Boolean xHsbcCryptoSignature = Boolean.parseBoolean(apiSettings.get("xHsbcCryptoSignature").toString());
        // 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 = activeProjectSettings.get("clientProfileId"); // API Profile ID Bank Assigned to you.
        String oboProfileId = activeProjectSettings.get("oboProfileId"); //Customer's ID on your platform. Input null if you are a Direct Client.
        HashMap<String, Object> additionalTokenClaims = (HashMap<String, Object>) apiSettings.get("additionalTokenClaims");
        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 = (HashMap<String, String>) apiSettings.get("requestUriParameters");
        //4.2 Generate Request Headers
        String clientHostingRegion = activeProjectSettings.get("clientHostingRegion"); // Your Platform or Business Operating Region, In ISO3166-alpha-2
        HashMap<String, String> additionalRequestHeaders = (HashMap<String, String>) apiSettings.get("additionalRequestHeaders");
        HashMap<String, String> requestHeaderJson = null;
        if (contentType.equals(ContentType.MULTIPART_FORM_DATA.getMimeType())) {
            requestHeaderJson = generateRequestHeadersForFile(httpMethod, apiSchema, authTokens, clientHostingRegion, oboProfileId, xHsbcCryptoSignature, additionalRequestHeaders);
            requestPayload = JSON.toJSONString(apiSettings.get("requestAttachment"));
        } else {
            requestHeaderJson = generateRequestHeaders(httpMethod, apiSchema, authTokens, clientHostingRegion, oboProfileId, xHsbcCryptoSignature, additionalRequestHeaders);
        }
        //4.3 Send API Request and save the response as String
        String targetHost = activeProjectSettings.get("targetHost");
        HashMap <String, String> apiEndpointUrls = (HashMap<String, String>) apiSettings.get("apiEndpointUrls");
        String apiEndpointUrl = activeProjectSettings.get("baseUrl") + apiEndpointUrls.get(targetHost); //Input your target URL
        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 class SandboxData {
        public SandboxData(){};

    public static void execute(HashMap<String, String> activeProjectSettings, HashMap<String, Object> apiSettings) throws Exception {

        System.out.println("----- #Start of API Sample Code Steps# -----");
        String httpMethod = apiSettings.get("httpMethod").toString();
        ApiSchema apiSchema = (ApiSchema) apiSettings.get("apiSchema"); //Input "EDGE" for API Version >= 3.0.0. Otherwise, for previous version, input "GTRF".
        System.out.println("-----#1. Start of loading Security Credentials-----");
        // 2. Prepare Request Payload
        System.out.println("-----Start of #2. Preparing the HTTP Request Payload...");
        // 2.1 Prepare Request JSON String
        String requestPayload = null;
        if (httpMethod != HttpGet.METHOD_NAME) {
            requestPayload = getJsonObjectFromFile(apiSettings.get("requestJsonFile").toString()).toString();
        }
        System.out.println("#2.1. Request Content JSON String: \n" + requestPayload);
        System.out.println("-----End of #2. Preparing the HTTP Request Payload...\n");


        // 4. Generate Request
        System.out.println("-----Start of #4. Executing API Request-----");
        //4.1 Generate URI Parameters if required:
        HashMap<String, String> requestUriParameters = (HashMap<String, String>) apiSettings.get("requestUriParameters");
        //4.2 Generate Request Headers
        String clientHostingRegion = activeProjectSettings.get("clientHostingRegion"); // Your Platform or Business Operating Region, In ISO3166-alpha-2
        String clientProfileId = activeProjectSettings.get("clientProfileId");
        String clientSecret = activeProjectSettings.get("clientSecret");
        String organizationId = activeProjectSettings.get("organizationId");
        String oboProfileId = activeProjectSettings.get("oboProfileId");
        HashMap<String, String> additionalRequestHeaders = (HashMap<String, String>) apiSettings.get("additionalRequestHeaders");
        HashMap<String, String> requestHeaderJson = generateRequestHeaders(apiSchema,clientProfileId, clientSecret, organizationId, clientHostingRegion, oboProfileId, additionalRequestHeaders);
        //4.3 Send API Request and save the response as String
        String apiEndpointUrl = apiSettings.get("apiEndpointUrl").toString(); //Input your target URL
        String responseMessage = doRequest(httpMethod, apiEndpointUrl, requestUriParameters, requestHeaderJson, requestPayload);
        System.out.println("-----End of #4. Executing API Request-----\n");

        System.out.println("----- #End of API Sample Code Steps# -----");
    }
        public static HashMap<String, String> generateRequestHeaders(ApiSchema apiSchema, String clientProfileId, String clientSecret, String organizationId, String clientHostingRegion,  String oboProfileId, HashMap<String, String> additionalRequestHeaders) {
            String contentType;
            HashMap<String, String> requestHeaders = new HashMap<String, String>();
            if (clientHostingRegion == "CN") {
                contentType = "text-plain";
            } else {
                contentType = "application/json";
            }
            switch (apiSchema) {
                case EDGE:
                    requestHeaders.put("x-hsbc-client-id", clientProfileId);
                    requestHeaders.put("x-hsbc-client-secret", clientSecret);
                    requestHeaders.put("x-hsbc-profile-id", organizationId);
                    requestHeaders.put("X-HSBC-countryCode", clientHostingRegion);
                    requestHeaders.put("Content-Type", contentType);
                    requestHeaders.put("X-HSBC-Request-Correlation-Id", String.valueOf(UUID.randomUUID()));
                    if (oboProfileId != null) {
                        requestHeaders.put("obo-Profile-ID", oboProfileId);
                    }
                    requestHeaders.putAll(additionalRequestHeaders);
                    break;
                case GTRF:
                    requestHeaders.put("x-hsbc-client-id", clientProfileId);
                    requestHeaders.put("x-hsbc-client-secret", clientSecret);
                    requestHeaders.put("x-hsbc-profile-id", organizationId);
                    requestHeaders.put("CountryCode", clientHostingRegion);
                    requestHeaders.put("Content-Type", contentType);
                    requestHeaders.put("requestId", String.valueOf(UUID.randomUUID()).replace("-", ""));
                    requestHeaders.put("requestTime", ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                    requestHeaders.put("schemaVersion", "1.0.0");
                    requestHeaders.putAll(additionalRequestHeaders);
                    break;
            }
            printHttpRequestHeaders(requestHeaders);
            return requestHeaders;
        }
    }
    public static List<File> getAttachments() {
        List<File> attachedFiles = new ArrayList<>();
        Path startPath = Paths.get("src/main/resources/attachments");
        try {
            Files.walk(startPath)
                .filter(Files::isRegularFile)
                .forEach(filePath -> attachedFiles.add(filePath.toFile()));
            return attachedFiles;
        } catch (Exception e) {
            System.out.println("generate files checksum error:" + e);
        }
        return null;
    }
}
