// Account management toosl
#include "clog.h"
#include "cgi.h"
#include "input.h"
#include "coregrade_api.h"
#include "account.h"
#include "email.h"
#include "safestring.h"
#include <string>
#include "pgsql.h"
#include "pgsql_wrapper.h"
#include "cfg.h"
#include <curl/curl.h>

#define CREATE_BY_EMAIL 1000
#define CREATE_BY_PHONE 2000

long CreateDefaultPage(CVars in, CVars &out);
long CreateUserPage(CVars in, CVars &out);
long AddPageCard(CVars in, CVars &out);

struct MemoryStruct {
    char *memory;
    size_t size;
};

static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
    size_t realsize = size * nmemb;
    struct MemoryStruct *mem = (struct MemoryStruct *) userp;

    mem->memory = (char*) realloc(mem->memory, mem->size + realsize + 1);
    if (mem->memory == NULL) {
        /* out of memory! */
        logfmt(logINFO, "not enough memory (realloc returned NULL)");
        return 0;
    }

    memcpy(&(mem->memory[mem->size]), contents, realsize);
    mem->size += realsize;
    mem->memory[mem->size] = 0;

    return realsize;
}

static const string base64_chars =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz"
        "0123456789+/";

static inline bool is_base64(unsigned char c) {
    return (isalnum(c) || (c == '+') || (c == '/'));
}

string base64_decode(string const& encoded_string) {
    int in_len = encoded_string.size();
    int i = 0;
    int j = 0;
    int in_ = 0;
    unsigned char char_array_4[4], char_array_3[3];
    std::string ret;

    while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
        char_array_4[i++] = encoded_string[in_];
        in_++;
        if (i == 4) {
            for (i = 0; i < 4; i++)
                char_array_4[i] = base64_chars.find(char_array_4[i]);

            char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
            char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
            char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

            for (i = 0; (i < 3); i++)
                ret += char_array_3[i];
            i = 0;
        }
    }

    if (i) {
        for (j = i; j < 4; j++)
            char_array_4[j] = 0;

        for (j = 0; j < 4; j++)
            char_array_4[j] = base64_chars.find(char_array_4[j]);

        char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
        char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
        char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

        for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
    }

    return ret;
}

long account_calls(CVars in, CVars &out) {
    logfmt(logINFO, "account_calls()");
    out["result"] = "YES I GET TO BACK END";
    long action = REQ_LONG(in, "action", 0, -1);
    switch (action) {
        case COREGRADE_ACCOUNT_TESTEMAIL:

            break;

        case COREGRADE_ACCOUNT_LOGIN:
            return LoginCoreGradeAccount(in, out);
            break;

        case COREGRADE_ACCOUNT_PENDING:
            return CreateCoreGradeAccountPending(in, out);
            break;

        case COREGRADE_ACCOUNT_CREATEACC:
            return CreateCoreGradeAccount(in, out);
            break;

        case COREGRADE_ACCOUNT_ADDCARD:
            return CreateUserPage(in, out);
            break;

        case COREGRADE_ACCOUNT_ADDPAGECARD:
            return AddPageCard(in, out);
            break;
    }
    logfmt(logINFO, "/account_calls()");
    return 0;
}

/*************************************************************************************************************************/
long AddPageCard(CVars in, CVars &out) {
    long ret = PHP_API_BAD_PARAM;


    try {

        logfmt(logINFO, "AddPageCard()");
        REQ_STRING(in, "title", 1, 99, "(.*)");
        REQ_STRING(in, "item_type", 5, 12, "(.*)");
        REQ_STRING(in, "sessionid", 4, 40, "(.*)");
        REQ_STRING(in, "detail", 3, 549, "(.*)");
        REQ_LONG(in, "member_id", 1, -1);
        REQ_LONG(in, "page_id", 1, -1);

        //    ret = load_db_record(out, "SELECT UPPER( md5( now()::text ) ) AS sessionid, *,id AS member_id FROM members WHERE status=1 AND LOWER(username)=LOWER('%s') AND password= md5('%s')", in["username"].c_str(), in["password"].c_str());
        //   if (ret) {

        CVars x;
        x["title"] = in["title"];
        x["title"].set_valid(true);
        x["page_id"] = in["page_id"];
        x["page_id"].set_valid(true);
        x["detail"] = in["detail"];
        x["detail"].set_valid(true);
        x["member_id"] = in["member_id"];
        x["member_id"].set_valid(true);
        x["item_type"] = in["item_type"];
        x["item_type"].set_valid( true );

        ret = insert_db_record(DBS_VALID, "members_page_item", "members_page_item_id_seq", x);




        //   } else {
        //         out["status"] = "Error";
        //    }

    } catch (bad_parameter) {
        out["status"] = "Invalid session ID";
    }



    logfmt(logINFO, "/AddPageCard()");
    return ret;
}

/*
        member_id INT REFERENCES members(id),
        item_type VARCHAR(15) NOT NULL REFERENCES page_item_types(item_type),
        page_id INT REFERENCES members_pages(id),
        title VARCHAR(100),
        detail VARCHAR(550)
 */

/**************************************************************************************************************************/
long account_session_check(CVars in, CVars out) {
    logfmt(logINFO, "long account_session_check(CVars in, CVars out)");
    long ret = PHP_API_BAD_PARAM;
    try {
        REQ_STRING(in, "sessionid", 4, 40, "(.*)");
        if (load_db_record(out, "SELECT uid FROM members_session WHERE sessionid='%s'", in["sessionid"].c_str()) > 0) {
            if (SessionCheck(out["uid"].Long(), in["sessionid"].c_str(), 0) > 0) {
                out["status"] = "Session valid";
                ret = PHP_API_OK;
            }
        }
    } catch (bad_parameter) {
        out["status"] = "Invalid session ID";
    }
    logfmt(logINFO, "/long account_session_check(CVars in, CVars out)");
    return ret;
}

/**************************************************************************************************************************/
long SessionCheck(long uid, const char *sessionid, int create) {
    logfmt(logINFO, "long SessionCheck(long uid, const char *sessionid, int create )");
    // Sanity check
    if (uid < 1 || sessionid == NULL || strlen(sessionid) < 4) {
        return -1L; // Invalif parameters
    }
    // Clean old sessions
    pgsql_exec("DELETE FROM members_session WHERE member_id=%ld AND updated < (now() - interval '35 minutes')", uid);
    // Update/check existing session
    if (create == 0 && pgsql_exec("UPDATE members_session SET updated=NOW() WHERE member_id=%ld AND session='%s'", uid, sessionid) > 0) {
        return 1L; // Session updated
    }
    if (create > 0) {

        pgsql_exec("DELETE FROM members_session WHERE member_id=%ld", uid);
        // Check session i?
        const PGresult *res = pgsql_query("SELECT * FROM members_session WHERE member_id=%ld AND session<>'%s'", uid, sessionid);
        if (res != NULL && pgsql_num_rows(res) > 0) {
            return -2L; // Active sessions found
        }
        CVars sess; // Do we have the same session already?
        if (load_db_record(sess, "SELECT * FROM members_session WHERE member_id=%lu AND session='%s'", uid, sessionid) > 0) {
            pgsql_exec("UPDATE members_session SET updated=NOW() WHERE member_id=%ld AND session='%s'", uid, sessionid);
            return sess["id"].Long();
        }
        // Create a new session
        sess["member_id"] = uid;
        sess["member_id"].set_valid(true);
        sess["session"] = sessionid;
        sess["session"].set_valid(true);
        long sid = insert_db_record(DBS_VALID, "members_session", "members_session_id_seq", sess);
        if (sid > 0) {
            return sid; // New session created
        }
        return -3L; // Failed to create new session
    }
    logfmt(logINFO, "/long SessionCheck(long uid, const char *sessionid, int create )");
    return 0L; // No route
}

long LoginCoreGradeAccount(CVars in, CVars &out) {
    long ret = PHP_API_BAD_PARAM;
    logfmt(logINFO, "LoginCoreGradeAccount()");
    REQ_STRING(in, "username", 5, 49, "(.*)");
    REQ_STRING(in, "password", 5, 49, "(.*)");
    REQ_STRING(in, "sessionid", 4, 40, "(.*)");
    OPTIONAL(in, "loc") REQ_STRING(in, "loc", 3, 15, "(.*)");



    ret = load_db_record(out, "SELECT UPPER( md5( now()::text ) ) AS sessionid, *,id AS member_id FROM members WHERE status=1 AND LOWER(username)=LOWER('%s') AND password= md5('%s')", in["username"].c_str(), in["password"].c_str());
    if (ret) {
        if (SessionCheck(out["member_id"].Long(), out["sessionid"].c_str(), 1) > 0) {
            out["stauts"] = "OK";
            CVars y, z;
            y["member_id"] = out["member_id"];
            y["member_id"].set_valid(true);

            CreateDefaultPage(y, z);
            ret = PHP_API_OK;
        } else {
            out["status"] = "Session check failed";
        }
    } else {
        out["status"] = "Invalid username and/or password";
    }


    logfmt(logINFO, "/LoginCoreGradeAccount()");
    return ret;
}

long CreateCoreGradeAccountPending(CVars in, CVars &out) {
    long ret = PHP_API_BAD_PARAM;
    out = in;

    /*
        CREATE TABLE members_pending (
            id SERIAL,
            username VARCHAR(15) UNIQUE NOT NULL,
            firstname VARCHAR(50),
            lastname VARCHAR(50),
            email    VARCHAR(50),
            phone    VARCHAR(25),  
            status   integer DEFAULT 1,   
            added timestamp without time zone DEFAULT now(),
            verify_link VARCHAR(100),
            expire timestamp without time zone DEFAULT now() +'30 days'
        );
        ALTER TABLE ONLY members_pending
        ADD CONSTRAINT members_pending_id_key UNIQUE (id);
     */



    REQ_STRING(in, "username", 5, 49, "(.*)");
    REQ_STRING(in, "firstname", 2, 49, "(.*)");
    REQ_STRING(in, "lastname", 2, 49, "(.*)");
    REQ_STRING(in, "email", 5, 49, "(.*)");
    OPTIONAL(in, "phone") REQ_STRING(in, "phone", 3, 15, "(.*)");
    OPTIONAL(in, "loc") REQ_STRING(in, "loc", 3, 15, "(.*)");

    out["pending_id"] = "0";
    out["status_message"] = "Pending";

    int r1, r2, r3;
    char verify_link[100];
    srand(time(NULL));
    r1 = abs(rand()*100000000);
    srand(time(NULL));
    r2 = abs(rand() * r1);
    srand(time(NULL));
    r3 = abs(rand() * r2);

    sprintf(verify_link, "RS-%09lu-%08lu-%08lu", r1, r2, r3);

    const char *loc = getenv("REMOTE_ADDR");


    const PGresult *res = pgsql_query("SELECT * FROM members WHERE LOWER(username)  = LOWER('%s') ", in["username"].c_str());

    if (res != NULL && pgsql_num_rows(res) > 0) {
        out["status_message"] = "Unable to create account, try another username or reset password";
    } else {
        // dulpicate condition satisfied


        CVars x;
        x["username"] = in["username"];
        x["username"].set_valid(true);
        x["firstname"] = in["firstname"];
        x["firstname"].set_valid(true);
        x["lastname"] = in["lastname"];
        x["lastname"].set_valid(true);
        x["email"] = in["email"];
        x["email"].set_valid(true);
        x["phone"] = in["phone"];
        x["phone"].set_valid(true);
        x["loc"] = loc;
        x["loc"].set_valid(true);
        x["verify_link"] = verify_link;
        x["verify_link"].set_valid(true);
        ret = insert_db_record(DBS_VALID, "members_pending", "members_pending_id_seq", x);

        if (ret > 0) {
            pgsql_query("UPDATE members_pending SET password = md5('%s') WHERE id = %lu", in["password"].c_str(), ret);
            out["pending_id"] = ret;
            out["pending_id"].set_valid(true);
            x["pending_id"] = ret;
            x["pending_id"].set_valid(true);
            AccountPendingMail(x);
        }
    }




    return ret;
}
//******************************************************************************

long CreateUserPage(CVars in, CVars &out) {

    logfmt(logINFO, "CreateUserPage()");
    long ret = PHP_API_BAD_PARAM;


    const char *loc = getenv("REMOTE_ADDR");
    try {
        long member_id = REQ_LONG(in, "member_id", 1, -1);
        REQ_STRING(in, "description", 1, 300, "(.*)");
        REQ_STRING(in, "title", 2, 49, "(.*)");


        load_db_record(out, "SELECT 'PG'|| UPPER( md5( now()::text ) ) AS page_key");

        // long lonkF = load_db_record(out, "SELECT *,id AS pending_id FROM members_pages WHERE member_id = %lu AND page_mode='DEFAULT' ", in["member_id"].Long());

        //  const PGresult* res = pgsql_query("SELECT id FROM members_pages WHERE member_id = %lu AND page_mode='DEFAULT' ", in["member_id"].Long());
        //  int rcc = pgsql_num_rows(res);
        //  if (rcc == 0) {


        CVars x;
        x["member_id"] = member_id;
        x["member_id"].set_valid(true);
        x["page_key"] = out["page_key"];
        x["page_key"].set_valid(true);
        x["page_mode"] = "OTHERPG";
        x["page_mode"].set_valid(true);
        x["title"] = in["title"];
        x["title"].set_valid(true);

        x["description"] = in["description"];
        x["description"].set_valid(true);


        ret = insert_db_record(DBS_VALID, "members_pages", "members_pages_id_seq", x);

        if (ret > 0) {

            //   AccountPendingMail(x); welcome email
        }

        //  }



    } catch (bad_parameter) {
        out["status"] = "ERROR";
    }

    logfmt(logINFO, "/CreateUserPage()");
    return ret;

}

long CreateDefaultPage(CVars in, CVars &out) {

    logfmt(logINFO, "CreateDefaultPage()");
    long ret = PHP_API_BAD_PARAM;
    long member_id = REQ_LONG(in, "member_id", 1, -1);

    const char *loc = getenv("REMOTE_ADDR");
    try {

        load_db_record(out, "SELECT 'PG'|| UPPER( md5( now()::text ) ) AS page_key");

        // long lonkF = load_db_record(out, "SELECT *,id AS pending_id FROM members_pages WHERE member_id = %lu AND page_mode='DEFAULT' ", in["member_id"].Long());

        const PGresult* res = pgsql_query("SELECT id FROM members_pages WHERE member_id = %lu AND page_mode='DEFAULT' ", in["member_id"].Long());
        int rcc = pgsql_num_rows(res);
        if (rcc == 0) {


            CVars x;
            x["member_id"] = member_id;
            x["member_id"].set_valid(true);
            x["page_key"] = out["page_key"];
            x["page_key"].set_valid(true);
            x["page_mode"] = "DEFAULT";
            x["page_mode"].set_valid(true);
            x["title"] = "My default page.";
            x["title"].set_valid(true);

            ret = insert_db_record(DBS_VALID, "members_pages", "members_pages_id_seq", x);

            if (ret > 0) {

                //   AccountPendingMail(x); welcome email
            }

        }



    } catch (bad_parameter) {
        out["status"] = "ERROR";
    }

    logfmt(logINFO, "/CreateDefaultPage()");
    return ret;

}
/*
 
        CREATE TABLE members_pages (
    id SERIAL,
        member_id INT REFERENCES members(id),
        page_mode VARCHAR(15),
        page_key VARCHAR(150) UNIQUE NOT NULL,
        title VARCHAR(50),
    status   integer DEFAULT 1,   
    added timestamp without time zone DEFAULT now()
);
ALTER TABLE ONLY members_pages
    ADD CONSTRAINT members_pages_id_key UNIQUE (id);
 */
//*****************************************************************************

long CreateCoreGradeAccount(CVars in, CVars &out) {
    logfmt(logINFO, "CreateCoreGradeAccount()");
    long ret = PHP_API_BAD_PARAM;
    REQ_STRING(in, "verify_link", 2, 120, "(.*)");
    // REQ_STRING(in, "pass1", 3, 15, "(.*)");
    OPTIONAL(in, "loc") REQ_STRING(in, "loc", 3, 15, "(.*)");
    const char *loc = getenv("REMOTE_ADDR");
    try {
        long lonkF = load_db_record(out, "SELECT *,id AS pending_id FROM members_pending WHERE verify_link ='%s' AND expire > now() LIMIT 1", in["verify_link"].c_str());
        if (lonkF) {
            out["status"] = "prepare to create account";
            CVars x;
            x["username"] = out["username"];
            x["username"].set_valid(true);
            x["firstname"] = out["firstname"];
            x["firstname"].set_valid(true);
            x["lastname"] = out["lastname"];
            x["lastname"].set_valid(true);
            x["email"] = out["email"];
            x["email"].set_valid(true);
            x["phone"] = out["phone"];
            x["phone"].set_valid(true);
            x["loc"] = loc;
            x["loc"].set_valid(true);
            x["password"] = out["password"];
            x["password"].set_valid(true);

            ret = insert_db_record(DBS_VALID, "members", "members_id_seq", x);

            if (ret > 0) {
                out["member_id"] = ret;
                out["member_id"].set_valid(true);
                x["member_id"] = ret;
                x["member_id"].set_valid(true);
                // pgsql_query("UPDATE members SET password = md5('%s') WHERE id = %lu", in["pass1"].c_str(), ret);
                pgsql_query("UPDATE members_pending SET status = 5,verified=now() WHERE id = %lu", out["pending_id"].Long());

                //   AccountPendingMail(x); welcome email
            }


        }








    } catch (bad_parameter) {
        out["status"] = "ERROR";
    }

    logfmt(logINFO, "/CreateCoreGradeAccount()");
    return ret;
}

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

long AddMobileTopuNumber(CVars in, CVars &out) {
    long ret = -1;
    long pin = REQ_LONG(in, "pin", 1, -1);
    long customer_id = REQ_LONG(in, "customer_id", 1, -1);
    OPTIONAL(in, "loc") REQ_STRING(in, "loc", 3, 15, "(.*)");
    REQ_STRING(in, "seller_number", 10, 11, "(.*)");

    CVars x;
    x["pin"] = in["pin"];
    x["pin"].set_valid(true);
    x["customer_id"] = in["customer_id"];
    x["customer_id"].set_valid(true);
    x["loc"] = in["loc"];
    x["loc"].set_valid(true);
    x["seller_number"] = in["seller_number"];
    x["seller_number"].set_valid(true);

    ret = insert_db_record(DBS_VALID, "mobile_sales_auth", "mobile_sales_auth_id_seq", x);

    out["status"] = ret;
    out["auth_id"] = ret;



    return ret;
}


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

long InitiateAddCustomerBalance(CVars in, CVars &out) {
    logfmt(logINFO, "AddCustomerBalance()");
    long ret = PHP_API_BAD_PARAM;
    try {
        // REQ_LONG( in, "whattodo", 1, -1 );



    } catch (bad_parameter) {
        out["status"] = "ERROR";
    }

    logfmt(logINFO, "/AddCustomerBalance()");
    return ret;
}

long CompleteAddCustomerBalance(CVars in, CVars &out) {
    logfmt(logINFO, "AddCustomerBalance()");
    long ret = PHP_API_BAD_PARAM;
    try {
        // REQ_LONG( in, "whattodo", 1, -1 );



    } catch (bad_parameter) {
        out["status"] = "ERROR";
    }

    logfmt(logINFO, "/AddCustomerBalance()");
    return ret;
}

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




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

long VerifyEmailCoreGradeAccount(CVars in, CVars &out) {
    logfmt(logINFO, "VerifyEmailCoreGradeAccount()");
    long ret = PHP_API_BAD_PARAM;
    try {
        long account_id = REQ_LONG(in, "account_id", 1, -1);
        //  ret = load_db_record( out, "SELECT * FROM members WHERE id = %lu", account_id );


    } catch (bad_parameter) {
        out["status"] = "ERROR";
    }

    logfmt(logINFO, "/VerifyEmailCoreGradeAccount()");
    return ret;
}
//******************************************************************************

long VerifyPhoneCoreGradeAccount(CVars in, CVars &out) {
    logfmt(logINFO, "VerifyPhoneCoreGradeAccount()");
    long ret = PHP_API_BAD_PARAM;
    try {
        // REQ_LONG( in, "whattodo", 1, -1 );



    } catch (bad_parameter) {
        out["status"] = "ERROR";
    }

    logfmt(logINFO, "/VerifyPhoneCoreGradeAccount()");
    return ret;
}
//******************************************************************************

long EditProfileCoreGradeAccount(CVars in, CVars &out) {
    logfmt(logINFO, "EditProfileCoreGradeAccount()");
    long ret = PHP_API_BAD_PARAM;
    try {
        long account_id = REQ_LONG(in, "account_id", 1, -1);
        REQ_STRING(in, "State", 2, 20, "(.*)");
        REQ_STRING(in, "Zipcode", 3, 15, "(.*)");
        REQ_STRING(in, "Country", 2, 5, "(.*)");



    } catch (bad_parameter) {
        out["status"] = "ERROR";
    }

    logfmt(logINFO, "/EditProfileCoreGradeAccount()");
    return ret;
}
//******************************************************************************

long LoadProfileCoreGradeAccount(CVars in, CVars &out) {
    logfmt(logINFO, "LoadProfileCoreGradeAccount()");
    long ret = PHP_API_BAD_PARAM;
    try {
        long account_id = REQ_LONG(in, "account_id", 1, -1);
        const PGresult* res = pgsql_query("SELECT * FROM members WHERE id = %lu", account_id);
        if (res && pgsql_num_rows(res) > 0) {
        }

    } catch (bad_parameter) {
        out["status"] = "ERROR";
    }

    logfmt(logINFO, "/LoadProfileCoreGradeAccount()");
    return ret;
}
//******************************************************************************
//*****************************************************************************/

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

        out["etisalat_endpoint"] = "http://etisalat.service.chiefsoft.com/etisalat_payquic.php";


        ret = PHP_API_OK;

    } catch (bad_parameter) {

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

    } catch (...) {

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

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

long DeliverMobilseEtisalat(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");
            return PHP_API_BAD_PARAM;
        }

        // Initiate interswitch session
        CVars setup;
        ETSL_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");
            return PHP_API_BAD_PARAM;
        }


        char uniqueRef[20];
        memset(uniqueRef, 0, 20);
        // TransferCode - RequestReference - UniqueRef => request prefix for terminal owner + ID
        sprintf(uniqueRef, "ETSL%08lu", out["transaction_id"].Long());
        //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 PAQUIC ETISALAT TRANSACTION->%s", xtid);
        out["ResponseCode"] = "99999";
        long ret1 = GateDeliverMobilseEtisalat(in, out);

        if (out["statusCode"].Long() == 0 && out["errorCode"].Long() == 0) {
            ret = 90000L;
            //'message' => '<?xml version="1.0" encoding="utf-8"?><result><statusCode>0</statusCode><errorCode>0</errorCode><errorDescription>Successful Transaction 0305055755074473</errorDescription><instanceId>0305055755074473</instanceId></result>'

            try {
                CVars txt;
                txt["responsecode"] = "90000";
                txt["responsecode"].set_valid(true);
                txt["transactionref"] = out["errorDescription"];
                txt["transactionref"].set_valid(true);
                update_db_record(DBS_VALID, "interswitch_transaction", txt, out["transaction_id"].Long());
            } catch (...) {
                out["status"] = "I see no good response from interswitch";
            }

            CVars fr0;
            fr0["status"] = "5";
            fr0["status"].set_valid(true);
            update_db_record(DBS_VALID, "interswitch_bill_payment_advice", fr0, out["bill_payment_advice_id"].Long());

        } else {

            try {
                CVars txt0;
                txt0["responsecode"] = "99999";
                txt0["responsecode"].set_valid(true);
                txt0["transactionref"] = out["errorDescription"];
                txt0["transactionref"].set_valid(true);
                update_db_record(DBS_VALID, "interswitch_transaction", txt0, out["transaction_id"].Long());
            } catch (...) {
                out["status"] = "I see no good response from interswitch";
            }

            CVars fr1;
            fr1["status"] = "7";
            fr1["status"].set_valid(true);
            update_db_record(DBS_VALID, "interswitch_bill_payment_advice", fr1, out["bill_payment_advice_id"].Long());
        }

        out["statusCode"].set_valid(true);


    } 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 GateDeliverMobilseEtisalat(CVars in, CVars &out) {
    logfmt(logINFO, "long DeliverMobilseEtisalat(CVars in, CVars &out)");
    long ret = PHP_API_BAD_PARAM;

    CURL *curl;
    CURLcode res;

    struct MemoryStruct chunk;

    chunk.memory = (char*) malloc(1); /* will be grown as needed by the realloc above */
    chunk.size = 0; /* no data at this point */

    curl_global_init(CURL_GLOBAL_ALL);

    /* get a curl handle */
    curl = curl_easy_init();

    if (curl) {
        // Prepare data
        string data = "";
        CVars::iterator i;
        int j = 0; // Will limit to 200 variables
        for (i = in.begin(); i != in.end() && j < 200; i++) {
            logfmt(logINFO, "IN: %s=%s", i->first.c_str(), i->second.c_str());
            if (j > 0) {
                data += "&";
            }
            data += i->first.c_str();
            data += "=";
            data += i->second.c_str();
            j++;
        }
        logfmt(logINFO, "DATA: %s", data.c_str());
        curl_easy_setopt(curl, CURLOPT_URL, "http://etisalat.service.chiefsoft.com/etisalat_payquic.php");
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &chunk);
        res = curl_easy_perform(curl);
        if (res != CURLE_OK) {
            // Error
            out["status"] = curl_easy_strerror(res);
            logfmt(logINFO, "ERROR: %s", out["status"].c_str());
            ret = PHP_API_BAD_PARAM;
        } else {
            logfmt(logINFO, "%lu bytes retrieved\n", (long) chunk.size);
            data = (const char *) chunk.memory;
            istringstream buffer(data);
            string temp, key, val;
            size_t found;
            while (std::getline(buffer, temp)) {
                logfmt(logINFO, "line: %s", temp.c_str());
                found = temp.find_first_of("=");
                key = temp.substr(0, found);
                val = temp.substr(found + 1);
                out[key] = base64_decode(val);
                logfmt(logINFO, "'%s' => '%s'", key.c_str(), out[key].c_str());
            }
            ret = PHP_API_OK;
        }
        curl_easy_cleanup(curl);

        if (chunk.memory) {
            free(chunk.memory);
        }
    }
    curl_global_cleanup();
    logfmt(logINFO, "/long DeliverMobilseEtisalat(CVars in, CVars &out)");
    return ret;
}

/*
void TeleSignAddJob(const char *CustomerId, const char *AutheticationId, const char *CountryCode, const char *PhoneNumber, const char *PIN) { 
  // I make sure that headerlist is erased before rebuilding it with the soap function 
  curl_slist_free_all(headerlist); 
  headerlist = curl_slist_append(headerlist, "Host: www.telesign.com");
  headerlist = curl_slist_append(headerlist, "Content-Type: text/xml"); 
  headerlist = curl_slist_append(headerlist, "SOAPAction: \"https://www.telesign.com/api/RequestCALL\""); 
  sprintf(TeleSignSoapReq, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\
<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \
xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\
  <soap:Body>\
    <RequestCALL xmlns=\"https://www.telesign.com/api/\">\
      <CustomerID>%s</CustomerID>\
      <AuthenticationID>%s</AuthenticationID>\
      <CountryCode>%s</CountryCode>\
      <PhoneNumber>%s</PhoneNumber>\
      <VerificationCode>%s</VerificationCode>\
      <RedialCount>1</RedialCount>\
    </RequestCALL>\
</soap:Body>\
</soap:Envelope>", CustomerId, AutheticationId, CountryCode, PhoneNumber, PIN); 
        //FILE_LOG(logINFO) << TeleSignSoapReq;
  curl_easy_setopt(ch, CURLOPT_POSTFIELDS, TeleSignSoapReq); 
  curl_easy_setopt(ch, CURLOPT_HTTPHEADER, headerlist); 
  curl_easy_perform(ch); 
} 
 */
