#include <cstdlib>
#include <ext/hash_map>

#include "interswitch.h"
#include "interswitch_sendmoney.h"

#include "cfg.h"
#include "clog.h"
#include "exceptions.h"
#include "input.h"
#include "coregrade_api.h"
#include "pgsql.h"
#include "pgsql_wrapper.h"
#include "safestring.h"

#include <string>
#include <stdexcept>

using namespace std;
using namespace __gnu_cxx;

//*****************************************************************************/
long INTW_configure(CVars in, CVars &out)
{
	logfmt( logINFO, "long INTW_configure(CVars in, CVars &out)");
	long ret = PHP_API_BAD_PARAM;
	try {
		// Configure Interswitch SSL
		interswitch_config_ssl(
		    CfgReadChar("interswitch.keyfile"), 
		    CfgReadChar("interswitch.password"), 
		    CfgReadChar("interswitch.cacert"),
		    CfgReadLong("interswitch.version"));
      
		out["interswitch_ver"]  = CfgReadLong("interswitch.version");
		out["interswitch_sep"]  = CfgReadChar("interswitch.soap_endpoint");
		out["interswitch_tid"]  = CfgReadChar("interswitch.terminal_id");
		out["interswitch_code"] = CfgReadChar("interswitch.entity_code");
		out["interswitch_mkey"] = CfgReadChar("interswitch.master_key");
		out["interswitch_pin"]  = CfgReadChar("interswitch.pin");
		out["interswitch_pfix"] = CfgReadChar("interswitch.prefix");
		out["interswitch_keyf"] = CfgReadChar("interswitch.keyfile");
		out["interswitch_pass"] = CfgReadChar("interswitch.password");
		out["interswitch_cert"] = CfgReadChar("interswitch.cacert");

		ret = PHP_API_OK;

	} catch (bad_parameter) {

		out["status"] = "Bad parameter";

	}  catch ( ... ) {

		out["status"] = "Unhandled exception in INTW_configure";

	}
	logfmt( logINFO, "/long INTW_configure(CVars in, CVars &out)");
	return ret;
}

//*****************************************************************************/
long INTW_getBillersSave(hash_map<const char*, const char*, hash<const char*>, eqstr> saveData) { //long id, char *name, long currencyCode, char *quickTellerSiteUrlName, char *supportEmail) {
	logfmt( logINFO, "long INTW_getBillersSave(...)");
	long gid = -1L, id = 0L;
	CVars x;
	hash_map<const char*, const char*, hash<const char*>, eqstr>::iterator pointer;
	for (pointer = saveData.begin();pointer!=saveData.end();++pointer) {
		x[pointer->first] = pointer->second; x[pointer->first].set_valid(true);
		if (strcmp("Id",pointer->first)==0) {
			id = atol(pointer->second);
			// Check if the record already exists
			const PGresult* res = pgsql_query("SELECT * FROM interswitch_billers WHERE id = %lu", id );
			if (id<1L || (res && pgsql_num_rows(res)>0)) {
				// Incorrect id or alredy exists
				logfmt(FLOG_MAX, "Incorrect id or alredy exists: %ld", id);
				return -1L;
			}
		}
	}
	gid = insert_db_record( DBS_VALID, "interswitch_billers", "interswitch_billers_gid_seq", x );
	pgsql_exec( "UPDATE global_settings SET value=now() WHERE name='LAST_INTSW_GETBILLER_RUN'");
	logfmt( logINFO, "/long INTW_getBillersSave(...)");	

	return gid;
}
        
//*****************************************************************************/
long INTW_getBillers(CVars in, CVars &out)
{
	logfmt( logINFO, "long INTW_getBillers(CVars in, CVars &out)");
	long ret = PHP_API_BAD_PARAM;
	try {

		// Initiate interswitch session
		CVars setup;
		INTW_configure(in, setup);

		// Prepare call
		const int statusSize = 1024;
		char status[statusSize];

		int billerId = 0;
		int categoryId = 0;
		int channelId = 0;

		char xtid[12];
		int channel_code = 3;

		sprintf( xtid, "%s", setup["interswitch_tid"].c_str() );

		ret = interswitch_getBillers(
			setup["interswitch_sep"].c_str(), 
			xtid, 
			billerId, 
			categoryId, 
			channelId, 
			"", 
			status, 
			&INTW_getBillersSave);

	} catch ( ... ) {

		out["status"] = "Unhandled exception in INTW_getBillers";
	}
	logfmt( logINFO, "/long INTW_getBillers(CVars in, CVars &out)");
	return ret;
}
/*
<------>long INTW_getSaveGeneric(const char *table, hash_map<const char*, const char*, hash<const char*>, eqstr> saveData);
<------>long INTW_getBillerPaymentItems(CVars in, CVars &out);
*/
//*****************************************************************************/
long INTW_getSaveGeneric(const char *table, hash_map<const char*, const char*, hash<const char*>, eqstr> saveData) { //long id, char *name, long currencyCode, char *quickTellerSiteUrlName, char *supportEmail) {
	logfmt( logINFO, "long INTW_getSaveGeneric(...)");
	long gid = -1L, id = 0L;
	CVars x;
	hash_map<const char*, const char*, hash<const char*>, eqstr>::iterator pointer;
	for (pointer = saveData.begin();pointer!=saveData.end();++pointer) {
		x[pointer->first] = pointer->second; x[pointer->first].set_valid(true);
	}
	const int n = strlen(table) + 20;
	char table_seq[n];
	snprintf(table_seq, n, "%s_gid_seq", table);
	gid = insert_db_record( DBS_VALID, table, table_seq, x );
	logfmt( logINFO, "/long INTW_getSaveGeneric(...)");	

	return gid;
}
        
//*****************************************************************************/
long INTW_getBillerPaymentItems(CVars in, CVars &out)
{
	logfmt( logINFO, "long INTW_getBillerPaymentItems(CVars in, CVars &out)");
	long ret = PHP_API_BAD_PARAM;
	try {

		// Initiate interswitch session
		CVars setup;
		INTW_configure(in, setup);

		// Prepare call
		const int statusSize = 1024;
		char status[statusSize];

		int billerId = REQ_LONG( in, "id", 1, -1 );;

		char xtid[12];

		sprintf( xtid, "%s", setup["interswitch_tid"].c_str() );

		ret = interswitch_getBillerPaymentItems(
			setup["interswitch_sep"].c_str(), 
			xtid, 
			billerId, 
			status, 
			&INTW_getSaveGeneric);

        } catch (bad_parameter) {
                out["status"] = "Incorrect Biller ID";
	} catch ( ... ) {

		out["status"] = "Unhandled exception in INTW_getBillerPaymentItems";
	}
	logfmt( logINFO, "/long INTW_getBillerPaymentItems(CVars in, CVars &out)");
	return ret;
}

//*****************************************************************************/
long INTW_sendBillPaymentAdvice(CVars in, CVars &out )
{
	logfmt( logINFO, "long INTW_sendBillPaymentAdvice(CVars in, CVars &out)");
	long ret = PHP_API_BAD_PARAM;
	try {
		int customerId = REQ_LONG( in, "customerid", 1, -1 );
		int paymentItemId = REQ_LONG( in, "paymentitemid", 1, -1 );
		int amount = REQ_LONG( in, "amount", 0, -1 );
		
		REQ_STRING( in, "customermobile", 5, 15, "(.*)"); 
		OPTIONAL (in, "customeremail") REQ_STRING( in, "customeremail", 1, 100, "(.*)"); 
		OPTIONAL (in, "order_id") REQ_STRING( in, "order_id", 1, 25, "(.*)"); 
		char CustomerMobile[15];
		char CustomerEmail[100];
		
		
		
		
		
		sprintf( CustomerEmail, "%s", in["customeremail"].c_str() );
		sprintf( CustomerMobile, "%s", in["customermobile"].c_str() );

		
		//topalert = "sales@payquic.com";
		out["topalert"]  = CfgReadChar("transnotif.topalert");
        sprintf( CustomerEmail, "%s", out["topalert"].c_str()  );


		CVars customer; // Load customer
		ret = load_db_record( customer, "SELECT email,firstname,lastname FROM customer WHERE status=1 AND id = %lu", customerId );
		if (ret<1L) {
			throw new bad_parameter(out, "customerid");
		}

		CVars paymentitem; // Load payment item
		ret = load_db_record( paymentitem, "SELECT id,billerid,billername,name,amount,paymentcode FROM interswitch_biller_payment_items WHERE gid = %lu", paymentItemId);
		if (ret<1L) {
		    out["advice"] = "PaymentID not found or not valid !!!";
			throw new bad_parameter(out, "paymentitemid");
		}

		// Check amount
		if (amount<1L) {
			amount = paymentitem["amount"].Long();
		}

/**/	


			

		// Create record
		CVars advice;
		
		advice["customerid"]  = customerId;                 advice["customerid"].set_valid(true);
		advice["billerid"]    = paymentitem["billerid"];    advice["billerid"].set_valid(true);
		advice["billerpaymentitemgid"] = paymentItemId;     advice["billerpaymentitemgid"].set_valid(true);
		advice["amount"]      = amount;                     advice["amount"].set_valid(true);
		advice["paymentcode"] = paymentitem["paymentcode"]; advice["paymentcode"].set_valid(true);
		advice["order_id"]  = in["order_id"];                 advice["order_id"].set_valid(true);
		advice["customeremail"]  = in["customeremail"];                 advice["customeremail"].set_valid(true);
		advice["customermobile"]  = in["customermobile"];                 advice["customermobile"].set_valid(true);
		
		out["bill_payment_advice_id"] = insert_db_record( DBS_VALID, "interswitch_bill_payment_advice", "interswitch_bill_payment_advice_id_seq", advice );
		if (out["bill_payment_advice_id"].Long()<1L) {
			throw new runtime_error("Failed to create interswitch bill payment advice record");
		}

		// Initiate interswitch session
		CVars setup;
		INTW_configure(in, setup);

		
		
		CVars transaction;
		

		
		transaction["detail_id"] = out["bill_payment_advice_id"]; transaction["detail_id"].set_valid(true);
		transaction["type"] = 1L; /* SendBillPaymentAdvice */     transaction["type"].set_valid(true);
		out["transaction_id"] = insert_db_record( DBS_VALID, "interswitch_transaction", "interswitch_transaction_id_seq", transaction );
		if (out["transaction_id"].Long()<1L) {
			throw new runtime_error("Failed to create interswitch transaction record");
		}


		char uniqueRef[20];
		memset(uniqueRef, 0, 20);
		// TransferCode - RequestReference - UniqueRef => request prefix for terminal owner + ID
		sprintf( uniqueRef, "%s%s000000", setup["interswitch_pfix"].c_str(), out["transaction_id"].c_str() );
		//sprintf( uniqueRef, "%s%09s", setup["interswitch_pfix"].c_str(), out["transaction_id"].c_str() );
		transaction["transfer_code"] = uniqueRef; transaction["transfer_code"].set_valid(true);
		
        CVars cxc;
        cxc["transfer_code"] =  transaction["transfer_code"];
		cxc["transfer_code"].set_valid(true);
		update_db_record( DBS_VALID, "interswitch_transaction", cxc, out["transaction_id"].Long());
			logfmt( logINFO, "Here -> 1");
		logfmt( logINFO, "SENDING TRANSACTION - setup[interswitch_tid].c_str() ->%s",setup["interswitch_tid"].c_str());
		// Prepare call
		const int statusSize = 1024;		
		char status[statusSize];
		char transactionRef[statusSize];
		memset(status, 0, statusSize); // Clear?
		memset(transactionRef, 0, statusSize); // Clear?

		char xtid[12];

		sprintf( xtid, "%s", setup["interswitch_tid"].c_str() );

		logfmt( logINFO, "SENDING TRANSACTION->%s",xtid);
		
		
		string result = interswitch_SendBillPaymentAdvice(
			setup["interswitch_sep"].c_str(), 
			xtid, 
			amount, 
			paymentitem["paymentcode"].c_str(),
			customerId,
			uniqueRef,
			status, 
			transactionRef,
			CustomerMobile,
			CustomerEmail);

			
		CVars advicestatus;	
			
		out["status"] = status;
		out["ResponseCode"] = result;
		out["TransactionRef"] = transactionRef;
		// Update status
		
		try
		{
			transaction["responsecode"]   = result;         transaction["responsecode"].set_valid(true);
			transaction["transactionref"] = transactionRef; transaction["transactionref"].set_valid(true);
			update_db_record( DBS_VALID, "interswitch_transaction", transaction, out["transaction_id"].Long());
		}
		catch ( ... ) {  out["status"] = "I see no good response from interswitch";  }
		
		
		// Success?
		if (result=="90000") {		
		
		advicestatus["status"]   = "5";            advicestatus["status"].set_valid(true);
		update_db_record( DBS_VALID, "interswitch_bill_payment_advice", advicestatus, out["bill_payment_advice_id"].Long());
		
			logfmt( logINFO, "Send bill payment advice successful");
		
			ret = PHP_API_OK;
		} else {
			CVars fr1;
			fr1["status"] =  "7"; fr1["status"].set_valid( true );
			
			advicestatus["status"]   = "7";            advicestatus["status"].set_valid(true);
			update_db_record( DBS_VALID, "interswitch_bill_payment_advice", fr1, out["bill_payment_advice_id"].Long());
			//ret = -1;
			throw new runtime_error("Failed to send bill payment advice");
		}

	} catch (runtime_error& ex) {
		out["status"] = ex.what();
		ret = PHP_API_BAD_PARAM;
	} catch (bad_parameter) {
		out["status"] = "Incorrect parameter";
		ret = PHP_API_BAD_PARAM;
	} catch ( ... ) {
		out["status"] = "Unhandled exception in INTW_sendBillPaymentAdvice";
	}
	logfmt( logINFO, "/long INTW_sendBillPaymentAdvice(CVars in, CVars &out)");
	return ret;
}
//*****************************************************************************/
long INTW_getBillerCategories(CVars in, CVars &out)
{
	logfmt( logINFO, "long INTW_getBillerCategories(CVars in, CVars &out)");
	long ret = PHP_API_BAD_PARAM;
	try {

		// Initiate interswitch session
		CVars setup;
		INTW_configure(in, setup);

                // Prepare call
                const int statusSize = 1024;
                char status[statusSize];

		ret = interswitch_getBillerCategories(
			setup["interswitch_sep"].c_str(),
			status, 
			NULL);

	} catch ( ... ) {

		out["status"] = "Unhandled exception in INTW_getBillerCategories";
	}
	logfmt( logINFO, "/long INTW_getBillerCategories(CVars in, CVars &out)");
	return ret;
}

//*****************************************************************************/
long INTW_doTransfer(CVars in, CVars &out )
{
	logfmt( logINFO, "long INTW_doTransfer(CVars in, CVars &out)");
	long ret = PHP_API_BAD_PARAM;
	try {

		// Initiate interswitch session
		CVars setup;
		INTW_configure(in, setup);


                in["action"].set_valid(false);
                REQ_STRING( in, "InitiatingEntityCode",         3,  3, "(.*)" );
                REQ_LONG( in, "InitiatingAmount",  1, -1 );
                REQ_LONG( in, "Fee",               1, -1 );
                REQ_LONG( in, "InitiatingChannel", 1, -1 );
                REQ_STRING( in, "InitiatingPaymentMethodCode",  2,  2, "(.*)" );
                REQ_STRING( in, "InitiatingCurrencyCode",       3,  3, "(.*)" );
                REQ_STRING( in, "TerminatingPaymentMethodCode", 2,  2, "(.*)" );
                REQ_LONG( in, "TerminatingAmount", 1, -1 );
                REQ_STRING( in, "TerminatingCurrencyCode",      3,  3, "(.*)" );
                REQ_STRING( in, "TerminatingCountryCode",       2,  2, "(.*)" );
				
                //REQ_STRING( in, "TerminatingEntityCode",        1,  5, "(.*)" );
                //REQ_STRING( in, "TerminatingAccountNumber",     1, 50, "(.*)" );
                //REQ_STRING( in, "TerminatingAccountType",       2,  2, "(.*)" );

                long senderid = REQ_LONG( in, "senderid", 1, -1 );
                long recipientid = REQ_LONG( in, "recipientid", 1, -1 );
 in["InitiatingEntityCode"]="MBA";
				
		CVars recipient; // Load recipient
		//ret = load_db_record( recipient, "SELECT firstname,lastname FROM customer WHERE id = %lu AND status = 1", recipientid);
		ret = load_db_record( recipient, "SELECT firstname,lastname,account_no,bank_code,account_type FROM sendmoney_recipient WHERE id = %lu ", recipientid);
		if (ret<1L) {
			throw new bad_parameter(out, "recipientid");
		}
		in["TerminatingEntityCode"] = recipient["bank_code"];  in["TerminatingEntityCode"].set_valid( true );
		in["TerminatingAccountNumber"]  = recipient["account_no"];   in["TerminatingAccountNumber"].set_valid( true );
				
		out["money_transfer_id"] = insert_db_record( DBS_VALID, "interswitch_money_transfer", "interswitch_money_transfer_id_seq", in );
		if (out["money_transfer_id"].Long()<1L) {
			throw new runtime_error("Failed to create interswitch bill money transfer record");
		}

		CVars transaction;
		transaction["detail_id"] = out["money_transfer_id"]; transaction["detail_id"].set_valid(true);
		transaction["type"] = 2L; /* DoTransfer */           transaction["type"].set_valid(true);
		out["transaction_id"] = insert_db_record( DBS_VALID, "interswitch_transaction", "interswitch_transaction_id_seq", transaction );
		if (out["transaction_id"].Long()<1L) {
			throw new runtime_error("Failed to create interswitch transaction record");
		}

		char xtid[12], uniqueRef[20];
		memset(uniqueRef, 0, 20);
		// TransferCode - RequestReference - UniqueRef => request prefix for terminal owner + ID
		sprintf( uniqueRef, "%s%s000000", setup["interswitch_pfix"].c_str(), out["transaction_id"].c_str() );
		transaction["transfer_code"] = uniqueRef; transaction["transfer_code"].set_valid(true);

		//sprintf( xtid, "%s", setup["interswitch_tid"].c_str() );
		sprintf( xtid, "%s", "MBA" );

		CVars sender; // Load sender
		ret = load_db_record( sender, "SELECT firstname,lastname FROM customer WHERE id = %lu AND status = 1", senderid);
		if (ret<1L) {
			throw new bad_parameter(out, "senderid");
		}
		//in["SenderEmail"] = sender["email"]; in["SenderEmail"].set_valid(true);

	
		//in["BeneficiaryEmail"] = recipient["email"]; in["BeneficiaryEmail"].set_valid(true);

		CVars trans_info;

		trans_info["Fee"] = in["Fee"];
		trans_info["InitiatingAmount"] = in["InitiatingAmount"];
		trans_info["InitiatingChannel"] = in["InitiatingChannel"];
		trans_info["InitiatingCurrencyCode"] = in["InitiatingCurrencyCode"];
		trans_info["InitiatingEntityCode"] = in["InitiatingEntityCode"];		
		trans_info["InitiatingPaymentMethodCode"] = in["InitiatingPaymentMethodCode"];
		trans_info["TerminatingAccountNumber"] = recipient["account_no"]; //in["TerminatingAccountNumber"];
		trans_info["TerminatingAccountType"] = recipient["account_type"]; // in["TerminatingAccountType"];
		trans_info["TerminatingAmount"] = in["TerminatingAmount"];
		trans_info["TerminatingCountryCode"] = in["TerminatingCountryCode"];
		trans_info["TerminatingCurrencyCode"] = in["TerminatingCurrencyCode"];
		trans_info["TerminatingEntityCode"] =  recipient["bank_code"]; //in["TerminatingEntityCode"];
		trans_info["TerminatingPaymentMethodCode"] = in["TerminatingPaymentMethodCode"];
		trans_info["TransferCode"] = uniqueRef;
		trans_info["SenderLastname"] = sender["lastname"];
		trans_info["SenderOthernames"] = sender["firstname"];
		trans_info["BeneficiaryLastname"] = recipient["lastname"];
		trans_info["BeneficiaryOthernames"] = recipient["firstname"];

		char status[SAFESTRING_COMMON_BUFFER_SIZE];
		memset(status,32,SAFESTRING_COMMON_BUFFER_SIZE);
		status[SAFESTRING_COMMON_BUFFER_SIZE] = '\0';

		char transCode[SAFESTRING_COMMON_BUFFER_SIZE];
		memset(transCode, 0, SAFESTRING_COMMON_BUFFER_SIZE);
		sprintf(transCode,""); // ???

		string result = interswitch_doTransfer(
			setup["interswitch_sep"].c_str(), 
			xtid, 
			trans_info, 
			status);

		logfmt(FLOG_MAX, "Return: %s", result.c_str());
		logfmt(FLOG_MAX, "Status: %s", status);

		out["ResponseCode"] = result;
		out["status"] = status;
		out["TransferCode"] = in["TransferCode"];
		if (result=="90000") {
			ret = PHP_API_OK;
			out["TransactionReferenceCode"] = status;
			out["status"] = "Transfer successful";
		} else {
			ret = PHP_API_BAD_PARAM;
			out["TransactionReferenceCode"] = "";
		}

		transaction["responsecode"] = out["ResponseCode"]; transaction["responsecode"].set_valid(true);
		transaction["transactionref"] = out["TransactionReferenceCode"]; transaction["transactionref"].set_valid(true);

		update_db_record( DBS_VALID, "interswitch_transaction", transaction, out["transaction_id"].Long());

	} catch (runtime_error& ex) {

		out["status"] = ex.what();
		ret = PHP_API_BAD_PARAM;

	} catch (bad_parameter) {

		out["status"] = "Invalid input";
		ret = PHP_API_BAD_PARAM;

	} catch ( ... ) {

		out["status"] = "Unhandled exception in INTW_doTransfer";
		ret = PHP_API_BAD_PARAM;

	}
	logfmt( logINFO, "/long INTW_doTransfer(CVars in, CVars &out)");
	return ret;
}

//*****************************************************************************/
long INTW_queryTransaction(CVars in, CVars &out )
{
	logfmt( logINFO, "long INTW_queryTransaction(CVars in, CVars &out)");
	long ret = PHP_API_BAD_PARAM;
	try {

		// Initiate interswitch session
		CVars setup;
		INTW_configure(in, setup);

                // Prepare call
                const int statusSize = SAFESTRING_COMMON_BUFFER_SIZE;
                char status[statusSize];
                char transactionResponseCode[SAFESTRING_COMMON_BUFFER_SIZE];

                long id = REQ_LONG( in, "id", 1, -1 );

		char xtid[12];

		sprintf( xtid, "%s", setup["interswitch_tid"].c_str() );

		CVars advice; // Load advice
		ret = load_db_record( advice, "SELECT transfer_code,transactionref FROM interswitch_transaction WHERE id = %lu", id);
		if (ret<1L) {
			throw new bad_parameter(out, "id");
		}

		string result = interswitch_queryTransaction(
			setup["interswitch_sep"].c_str(),
			xtid,
			advice["transfer_code"].c_str(),
			advice["transactionref"].c_str(),
			status,
			transactionResponseCode);
		out["ResponseCode"] = result;
		out["TransactionResponseCode"] = transactionResponseCode;
		out["status"] = status;
		ret = PHP_API_OK;

		advice["responsecode"] = transactionResponseCode; advice["responsecode"].set_valid(true);
		update_db_record( DBS_VALID, "interswitch_transaction", advice, id);

	} catch (bad_parameter) {

		out["status"] = "Invalid ID";

	} catch ( ... ) {

		out["status"] = "Unhandled exception in INTW_queryTransaction";
	}
	logfmt( logINFO, "/long INTW_queryTransaction(CVars in, CVars &out)");
	return ret;
}

//*****************************************************************************/
