package com.enactor.training.paymentService.service.actions.gcash;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.X509EncodedKeySpec;

import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.util.encoders.Base64;

import com.enactor.core.application.process.ApplicationProcessDataType;
import com.enactor.core.application.process.IApplicationProcessDataType;
import com.enactor.core.cryptography.CryptographyUtils;
import com.enactor.core.utilities.PropertyRegistry;

/**
 * This class uses Enactor Crypto Utils and BouncyCastle to genarate and validate RSA Signatures,
 * using private and public keys for signature and validation respectively.
 * Designed to be called from Actions to generate/validate signatures in GCash payment service handling.
 */

public class GCashFunctions {
	
	/** Signature data type */
	public static final String SIGNATURE_DATA_NAME = "enactor.gcash.Signature";
	public static final IApplicationProcessDataType SIGNATURE_DATA = new ApplicationProcessDataType(SIGNATURE_DATA_NAME, String.class.getName());
	
	/** Payload data type */
	public static final String PAYLOAD_DATA_NAME = "enactor.gcash.Payload";
	public static final IApplicationProcessDataType PAYLOAD_DATA = new ApplicationProcessDataType(PAYLOAD_DATA_NAME, String.class.getName());
	
	public static final String CREDENTIAL_PROPERTY_PUBLIC_KEY = "publicKey";
	
	public static final String CREDENTIAL_PROPERTY_PRIVATE_KEY = "privateKey";
	
	public static final String CREDENTIAL_PROPERTY_CLIENT_ID = "clientId";
	
	public static final String CREDENTIAL_PROPERTY_CLIENT_SECRET = "clientSecret";
	
	public static final String CREDENTIAL_PROPERTY_MERCHANT_ID = "merchantId";
	
	public static final String CREDENTIAL_PROPERTY_PRODUCT_CODE = "productCode";
	
	public static final String CREDENTIAL_PROPERTY_SHOP_ID = "shopId";

	/**
     * Generates a signature string for the given input string using the provided private key.
     * 
     * @param inputString The input string to be signed.
     * @param privateKeyString The private key string used for signing.
     * @return The signature string generated for the input string.
     */
	public static String generateSignatureString(String inputString, String keyPath) {

		String outputString = "NONE";
		Security.addProvider(new BouncyCastleProvider());
		
		try {
			
		    String privateKeyString = new String(Files.readAllBytes(Paths.get(PropertyRegistry.getApplicationHome() + keyPath)), Charset.forName("UTF-8"));
			PEMParser pemParser = new PEMParser(new StringReader(privateKeyString));
			Object object = pemParser.readObject();

			pemParser.close();

			JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");

			PrivateKey privKey = null;

			if (object instanceof PrivateKeyInfo) {
				privKey = converter.getPrivateKey((PrivateKeyInfo) object);
				outputString = CryptographyUtils.signData(privKey,"SHA256withRSA",new ByteArrayInputStream(inputString.getBytes(Charset.forName("UTF-8"))) );
			}
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			outputString = "FAIL";
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			outputString = "FAIL";
		} catch (SignatureException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			outputString = "FAIL";
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			outputString = "FAIL";
		}

		return outputString;

	}

	 /**
     * Validates a signature string against the given input string and public key string.
     * 
     * @param inputString The input string for which the signature is validated.
     * @param signatureString The signature string to be validated.
     * @param publicKeyString The public key string used for signature validation.
     * @return True if the signature is valid for the input string and public key, otherwise false.
     */
	public static Boolean validateSignatureString(String inputString, String signatureString, String keyPath) {

		Boolean result = false;
		Security.addProvider(new BouncyCastleProvider());

		PublicKey pubKey;

		try {
			String publicKeyString = new String(Files.readAllBytes(Paths.get(PropertyRegistry.getApplicationHome() + keyPath)));
			
			publicKeyString = publicKeyString.replace("\r", "");	
			publicKeyString = publicKeyString.replace("\n", "");	
			publicKeyString = publicKeyString.replace("-----BEGIN PUBLIC KEY-----", "");
			publicKeyString = publicKeyString.replace("-----END PUBLIC KEY----", "");
			byte[] byteKey = publicKeyString.getBytes();
			X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(Base64.decode(byteKey));
			KeyFactory kf = KeyFactory.getInstance("RSA");

			pubKey= kf.generatePublic(X509publicKey);
		}
		catch(Exception e){
			e.printStackTrace();
			return false;
		}

		try {
			Signature publicSignature = Signature.getInstance("SHA256withRSA");
			publicSignature.initVerify(pubKey);
			publicSignature.update(inputString.getBytes(Charset.forName("UTF-8")));

			byte[] signatureBytes = Base64.decode(signatureString);

			result =  publicSignature.verify(signatureBytes);

		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SignatureException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return result;
	}
	
}

