package eu.nets.sis.idrights.demoapp.service;

import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Date;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import eu.nets.sis.idrights.clientapi.config.MerchantContext;
import eu.nets.sis.idrights.clientapi.exception.IDRightsClientException;
import eu.nets.sis.idrights.clientapi.impl.IDRightsFacade;
import eu.nets.sis.idrights.clientapi.impl.IDRightsFactory;
import eu.nets.sis.idrights.clientapi.p12.PKCS12Keystore;
import eu.nets.sis.idrights.demoapp.DemoAppProperties;
import no.bbs.trust.b2b.xmlapi.message.domain.B2BMessage;
import no.bbs.trust.b2b.xmlapi.message.generated.GetSPInfoByOrgTypeDef;
import no.bbs.trust.b2b.xmlapi.message.generated.PersonIDList;
import no.bbs.trust.b2b.xmlapi.message.generated.PersonIDTypeDef;
import no.bbs.trust.b2b.xmlapi.message.generated.TrustB2BMessage;
import no.bbs.trust.b2b.xmlapi.message.generated.TrustB2BStdHeaderType;
import no.bbs.trust.b2b.xmlapi.message.generated.VerifySignatureAndProcurationTypeDef;
import no.bbs.trust.dhc.DHCConfiguration;

/**
 * 
 * Service class that is used to get the Signature data and to verify the signature data.
 *
 */
public class IDRightsService {
	
	private static Logger logger = LogManager.getLogger(IDRightsService.class);
	
	public static void registerMerchantContext(String keyStorePath, String keyStorePwd) {
		logger.info("Initializing ID-Rights client API");

		try {
			MerchantContext merchantContext = new MerchantContext();
			DHCConfiguration dhcConfig = new DHCConfiguration();
			
			ClassLoader classLoader = IDRightsService.class.getClassLoader();
	        InputStream inputStream = classLoader.getResourceAsStream(DemoAppProperties.clientTrustStore);
	        
	        File tempFile = File.createTempFile(DemoAppProperties.clientTrustStore, ".jks");
	        Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
	        
			dhcConfig.setUrl(DemoAppProperties.idRightsUrlBase);
			dhcConfig.setKeyStorePath(keyStorePath);
			dhcConfig.setKeyStorePwd(keyStorePwd);
			dhcConfig.setKeyStoreType(DemoAppProperties.keyStoreType);
			dhcConfig.setTrustStore(tempFile.getAbsolutePath());
			dhcConfig.setTrustStorePwd(DemoAppProperties.clientTrustStorePwd);
			dhcConfig.setTrustStoreType(DemoAppProperties.clientTrustStoreType);
			dhcConfig.setSocketTimeoutOverride(10000);
			
			merchantContext.setDhcConfig(dhcConfig);
			
			PKCS12Keystore pkcs12 = new PKCS12Keystore(keyStorePath, keyStorePwd.toCharArray());
			//pkcs12.setFileName(sslKeyStore);
			merchantContext.setClientSSLkeystore(pkcs12);
			
			merchantContext.setMerchantId(DemoAppProperties.merchantId);
			IDRightsFactory.INSTANCE.registerMerchantContext(merchantContext);
			
			logger.info("Merchant id [" + DemoAppProperties.merchantId + "] registered in MerchantContext");
			
		} catch (Exception e) {
			logger.error("ID-Rights client api setup has failed", e);
			System.out.println("\n\n<<<<<<<<<<ERROR>>>>>>>>>>\n\nID-Rights client api setup has failed.\n\nErrorMessage=" + e.getMessage() + ".\n\n");
			System.exit(0);
		}
	}
	
	/**
	 * Responsible for returning the getSPInfo data by making call to id-rights-clientapi
	 * 
	 * @param countryCode country code which will be used for get the signature data from idrights
	 * @param orgNo organization number which will be used for get the signature data from idrights
	 * @param headerText show header text info in case of SE org numbers
	 * @return result of getSPInfoData
	 * @throws IDRightsClientException Is thrown if an ErrorMessage is returned from IDRights
	 */
	public B2BMessage getSPInfoByOrgXmlRequest(String countryCode, String orgNo, String headerText) throws IDRightsClientException {
		logger.info("Calling GetSPInfoByOrg message !!");
		try {
			IDRightsFacade facade = IDRightsFactory.INSTANCE.getIDRightsFacade(DemoAppProperties.merchantId);
			B2BMessage b2bMessage = createB2BMessage(null, Long.parseLong(DemoAppProperties.merchantId), countryCode);
			
			GetSPInfoByOrgTypeDef getSPInfoByOrg = new GetSPInfoByOrgTypeDef();
			getSPInfoByOrg.setOrganizationNumber(orgNo);
			if ("SE".equals(countryCode)) {
				getSPInfoByOrg.setShowHeaderText(headerText);
			}
			b2bMessage.getTrustB2BMessage().setGetSPInfoByOrg(getSPInfoByOrg);
			
			B2BMessage b2bResponse = facade.getSPInfoByOrg(b2bMessage);
			return b2bResponse;
			
		} catch (IDRightsClientException e) {
			logger.error("GetSPInfoByOrg message failed. ErrorCode [" + e.getErrorCode() + "] ErrorMsg [" + e.getMessage() + "]", e);
			throw e;
		}
	}
	
	/**
	 * Responsible for returning the verifySPInfo data by making call to id-rights-clientapi
	 * 
	 * @param countryCode country code which will be used for verify the signature data from idrights
	 * @param orgNo organization number which will be used for verify the signature data from idrights
	 * @param ssnValues social security numbers which will be used for verify the signature data from idrights
	 * @return result of verifySPInfoData
	 * @throws IDRightsClientException Is thrown if an ErrorMessage is returned from IDRights
	 */
	public B2BMessage verifySPInfoXmlRequest(String countryCode, String orgNo, String ssnValues) throws IDRightsClientException {
		logger.info("Calling VerifySPInfoByOrg message !!");
		try {
			IDRightsFacade facade = IDRightsFactory.INSTANCE.getIDRightsFacade(DemoAppProperties.merchantId);
			B2BMessage b2bMessage = createB2BMessage(null, Long.parseLong(DemoAppProperties.merchantId), countryCode);
			
			VerifySignatureAndProcurationTypeDef verifySignatureAndProcuration = new VerifySignatureAndProcurationTypeDef();
			verifySignatureAndProcuration.setOrganizationNumber(orgNo);
			verifySignatureAndProcuration.setPersonIDList(new PersonIDList());
			
			// Here SSN can be comma-separated multiple values
			for (String ssn : ssnValues.split(",")) {
				if (!ssn.isEmpty() && !ssn.contains(" ")) {
					PersonIDTypeDef personId = new PersonIDTypeDef();
					personId.setSocialSecurityNumber(ssn);
					verifySignatureAndProcuration.getPersonIDList().getPersonID().add(personId);
				}
			}
			
			b2bMessage.getTrustB2BMessage().setVerifySignatureAndProcuration(verifySignatureAndProcuration);
			
			B2BMessage b2bResponse = facade.verifySignatureAndProcuration(b2bMessage);
			return b2bResponse;
			
		} catch (IDRightsClientException e) {
			logger.error("VerifySignatureAndProcuration message failed. ErrorCode [" + e.getErrorCode() + "] ErrorMsg [" + e.getMessage() + "]", e);
			throw e;
		}
	}
	
	/**
	 * @param transactionId transactionId, used for tracking the entire process
	 * @param merchantId merchant identifier provided by Nets
	 * @param countryCode country code which will be used for get and verify the signature data from idrights
	 * @return B2BMessage object, which contains complete header details
	 */
	private B2BMessage createB2BMessage(String transactionId, long merchantId, String countryCode) {
		TrustB2BMessage trustB2bMessage = new TrustB2BMessage();
		trustB2bMessage.setTrustB2BHeader(createHeader(transactionId, merchantId, countryCode));
		B2BMessage b2bMessage = new B2BMessage(trustB2bMessage, transactionId);
		b2bMessage.getTrustB2BMessage().getTrustB2BHeader().setTraceID(transactionId);
		
		return b2bMessage;
	}

	/**
	 * @param transactionId transactionId, used for tracking the entire process
	 * @param merchantId merchant identifier provided by Nets
	 * @param countryCode country code which will be used for get and verify the signature data from idrights
	 * @return TrustB2BStdHeaderType object which contains header details
	 */
	private TrustB2BStdHeaderType createHeader(String transactionId, long merchantId, String countryCode) {
		TrustB2BStdHeaderType trustB2bHeader = new TrustB2BStdHeaderType();
		trustB2bHeader.setCountryCode(countryCode);
		trustB2bHeader.setMerchantID(merchantId);
		// This message id should be unique for IN Groupe ID-Rights service, otherwise an error will be thrown. Customer should generate this message ID
		trustB2bHeader.setMessageID(DemoAppProperties.merchantId + System.currentTimeMillis());  
		trustB2bHeader.setTraceID(transactionId);
		trustB2bHeader.setTime(new Date());
		return trustB2bHeader;
	}
}
