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

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.UUID;

import com.enactor.commonUI.CommonUIDataTypesBase;
import com.enactor.core.application.process.ApplicationProcessDataType;
import com.enactor.core.application.process.ApplicationProcessException;
import com.enactor.core.application.process.IApplicationProcessData;
import com.enactor.core.application.process.IApplicationProcessDataType;
import com.enactor.core.application.process.IApplicationProcessOutcome;
import com.enactor.core.json.serializer.IJsonSerializerUtil;
import com.enactor.core.json.serializer.JsonParseException;
import com.enactor.core.json.serializer.JsonSerializerUtil;
import com.enactor.core.signOn.IUserPrincipal;
import com.enactor.core.transaction.ITransactionHandler;
import com.enactor.core.utilities.DateUtils;
import com.enactor.core.utilities.Logger;
import com.enactor.coreUI.actions.IUIAction;
import com.enactor.coreUI.actions.UIActionFunctions;
import com.enactor.coreUI.annotations.Input;
import com.enactor.coreUI.annotations.Inputs;
import com.enactor.coreUI.annotations.Outcomes;
import com.enactor.coreUI.annotations.Output;
import com.enactor.coreUI.annotations.Outputs;
import com.enactor.coreUI.processes.CoreUIOutcomes;
import com.enactor.coreUI.processes.UIProcessException;
import com.enactor.mfc.basket.BasketDataTypes;
import com.enactor.mfc.location.ILocation;
import com.enactor.mfc.location.LocationDataTypes;
import com.enactor.mfc.posTerminal.IPOSTerminal;
import com.enactor.mfc.posTerminal.POSTerminalDataTypes;
import com.enactor.mfc.retail.transaction.IRetailSaleTransaction;
import com.enactor.mfc.retail.transaction.IRetailTransactionHandler;
import com.enactor.training.paymentService.service.messages.gcash.EnvInfo;
import com.enactor.training.paymentService.service.messages.gcash.IRequestMessage;
import com.enactor.training.paymentService.service.messages.gcash.MessageBody;
import com.enactor.training.paymentService.service.messages.gcash.MessageContent;
import com.enactor.training.paymentService.service.messages.gcash.MessageHead;
import com.enactor.training.paymentService.service.messages.gcash.RequestMessage;
import com.enactor.training.paymentService.service.messages.gcash.ScannerInfo;
import com.enactor.training.paymentService.service.messages.gcash.ShopInfo;

@Inputs({
	@Input(name = CommonUIDataTypesBase.USER_PRINCIPAL_NAME, type = IUserPrincipal.class, required = true),
	@Input(name = BasketDataTypes.TRANSACTION_HANDLER_DATA_NAME, type = IRetailTransactionHandler.class, required = true),
	@Input(name = LocationDataTypes.LOCATION_DATA_NAME, type = ILocation.class, required = true),
	@Input(name = POSTerminalDataTypes.POS_TERMINAL_DATA_NAME, type = IPOSTerminal.class, required = true),
	@Input(name = CreateAndSignRequestAction.CUSTOMER_CODE_DATA_NAME, type = String.class, required = true)	
})
@Outputs({
	@Output(name = RequestMessage.ENTITY_DATA_TYPE_NAME, type = IRequestMessage.class)
})
@Outcomes({
	CoreUIOutcomes.SUCCESS_OUTCOME_NAME
})
public class CreateAndSignRequestAction implements IUIAction {

	private static final long serialVersionUID = 1L;

	private static Logger logger = Logger.getLogger(CreateAndSignRequestAction.class.getName());

	/** Signature data type */
	public static final String CUSTOMER_CODE_DATA_NAME = "enactor.gcash.CustomerCode";
	public static final IApplicationProcessDataType CUSTOMER_CODE_DATA = new ApplicationProcessDataType(CUSTOMER_CODE_DATA_NAME, String.class.getName());

	public static final String FUNCTION = "gcash.acquiring.retail.pay";

	public static final String VERSION = "2.0";
	
	public static final String AUTH_CODE_TYPE = "BAR_CODE";
	
	public static final String ORDER_TERMINAL_TYPE = "SYSTEM";
	
	public static final String TERMINAL_TYPE = "SYSTEM";
	
	public static final String ORDER_TITLE = "Enactor GcashTest";

	@Override
	public IApplicationProcessOutcome execute(IApplicationProcessData inputData, IApplicationProcessData outputData)
			throws UIProcessException, ApplicationProcessException {

		IUserPrincipal userPrincipal = UIActionFunctions.getRequiredDataItem(inputData, CommonUIDataTypesBase.USER_PRINCIPAL);

		ITransactionHandler transactionHandler = UIActionFunctions.getRequiredDataItem(inputData, BasketDataTypes.TRANSACTION_HANDLER_DATA);

		ILocation location = UIActionFunctions.getRequiredDataItem(inputData, LocationDataTypes.LOCATION_DATA);

		IPOSTerminal posTerminal = UIActionFunctions.getRequiredDataItem(inputData, POSTerminalDataTypes.POS_TERMINAL_DATA);

		String customerCode = UIActionFunctions.getRequiredDataItem(inputData, CreateAndSignRequestAction.CUSTOMER_CODE_DATA);
		
		IRequestMessage request = createRequest(userPrincipal, transactionHandler, location, posTerminal, customerCode);

		try {
			request = signRequest(request, userPrincipal);
		} catch (JsonParseException e) {
			logger.log(Logger.LOG_ERROR, "Failed to sign request", e);
			return CoreUIOutcomes.FAIL_OUTCOME;
		}
		
		outputData.setData(RequestMessage.ENTITY_DATA_TYPE, request);

		return CoreUIOutcomes.SUCCESS_OUTCOME;
	}
	
	protected IRequestMessage createRequest(IUserPrincipal userPrincipal, ITransactionHandler transactionHandler, ILocation location, IPOSTerminal posTerminal, String customerCode) {
		
		String clientId = userPrincipal.getCredentialProperties().get(GCashFunctions.CREDENTIAL_PROPERTY_CLIENT_ID);
		String clientSecret = userPrincipal.getCredentialProperties().get(GCashFunctions.CREDENTIAL_PROPERTY_CLIENT_SECRET);
		String merchantId = userPrincipal.getCredentialProperties().get(GCashFunctions.CREDENTIAL_PROPERTY_MERCHANT_ID);
		String productCode = userPrincipal.getCredentialProperties().get(GCashFunctions.CREDENTIAL_PROPERTY_PRODUCT_CODE);
		String shopId = userPrincipal.getCredentialProperties().get(GCashFunctions.CREDENTIAL_PROPERTY_SHOP_ID);
		
		IRetailSaleTransaction retailTransaction = (IRetailSaleTransaction) transactionHandler.getTransaction();
		
		IRequestMessage request = new RequestMessage();

		MessageContent messageContent = new MessageContent();
		
		MessageHead head = new MessageHead();
		head.setClientId(clientId);
		head.setClientSecret(clientSecret);
		head.setFunction(FUNCTION);
		head.setReqMsgId(UUID.randomUUID().toString());
		head.setReqTime(DateUtils.formatJAXBISO8601DateNoMillis(DateUtils.getLocalCalendar(new Date())));
		head.setVersion(VERSION);
		messageContent.setHead(head);

		MessageBody body = new MessageBody();
		body.setAuthCode(customerCode);
		body.setAuthCodeType(AUTH_CODE_TYPE);
		body.setMerchantId(merchantId);
		body.setProductCode(productCode);
		
		EnvInfo envInfo = new EnvInfo();
		envInfo.setClientIp("192.168.101.146");
		try {
			InetAddress IP = InetAddress.getLocalHost();
			envInfo.setMerchantIp(IP.getHostAddress());
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		envInfo.setMerchantTerminalId(location.getLocationId()+posTerminal.getTerminalNumber());
		envInfo.setOrderTerminalType(ORDER_TERMINAL_TYPE);
		envInfo.setTerminalType(TERMINAL_TYPE);
		body.setEnvInfo(envInfo);
		
		com.enactor.training.paymentService.service.messages.gcash.Order order = new com.enactor.training.paymentService.service.messages.gcash.Order();
		order.setMerchantTransId(retailTransaction.getTransactionId());
		order.setOrderTitle(ORDER_TITLE);
		com.enactor.training.paymentService.service.messages.gcash.CurrencyAmount orderAmount = new com.enactor.training.paymentService.service.messages.gcash.CurrencyAmount();
//		orderAmount.setCurrency(retailTransaction.getBasket().getCurrencyId());
		orderAmount.setCurrency("PHP");
		orderAmount.setValue(retailTransaction.getBasket().getBalance());
		order.setOrderAmount(orderAmount);
		
		body.setOrder(order);
		
		ScannerInfo scannerInfo = new ScannerInfo();
		scannerInfo.setDeviceId("9022181100111118");
		scannerInfo.setDeviceIp("112.143.220.101");
		body.setScannerInfo(scannerInfo);
		
		ShopInfo shopInfo = new ShopInfo();
		shopInfo.setShopId(shopId);
		shopInfo.setShopName(location.getDescription());
		body.setShopInfo(shopInfo);
		
		messageContent.setBody(body);
		
		request.setRequest(messageContent);
		
		return request;
	}

	protected IRequestMessage signRequest(IRequestMessage request, IUserPrincipal userPrincipal) throws JsonParseException {
		String keyPath = userPrincipal.getCredentialProperties().get(GCashFunctions.CREDENTIAL_PROPERTY_PRIVATE_KEY);	
		IJsonSerializerUtil jsonSerializerUtil = JsonSerializerUtil.getInstance(com.enactor.core.json.serializer.JsonSerializerUtil.Format.ENACTOR);
		String payloadJson = jsonSerializerUtil.getUnformattedJSON(request.getRequest());
		String signatureString = GCashFunctions.generateSignatureString(payloadJson, keyPath);
		request.setSignature(signatureString);
		
		if (logger.isDebugEnabled()) {
			logger.log(Logger.LOG_DEBUG, "Request:\n\r" + payloadJson + "\n\r");
			logger.log(Logger.LOG_DEBUG, "Signature:\n\r" + signatureString + "\n\r");
		}
		
		return request;
	}

}
