// Topup management toosl
#include "clog.h"
#include "cgi.h"
#include "input.h"
#include "mermsemr_api.h"
#include "safestring.h"
#include <string>
#include "pgsql.h"
#include "pgsql_wrapper.h"
#include <curl/curl.h>
#include "medTEmails.h"

/* -- */
#include "function_members.h"
#include "providers.h"
#include "function_members.h"
#include "function_providers_charts.h"
#include "medTEmails.h"

long provider_login(CVars in, CVars &out);
long PracticeLogin(CVars in, CVars &out);
long PracticeSessionCheck(long practice_id, long practice_users_id, const char *sessionid, int create);
long provider_createmember(CVars in, CVars &out);
long provider_acceptmember(CVars in, CVars &out);
long provider_linkIdemember(CVars in, CVars &out);
void GenerateLongID(char * number, int sz, int seed);
long provider_startNewPractice(CVars in, CVars &out);

long providers_call(CVars in, CVars &out) {
    long ret = PHP_API_BAD_PARAM;
    char vname[20];
    logfmt(logINFO, "ERROR CALL long providers_call(CVars in, CVars &out)");
    try {
        REQ_LONG(in, "action", 0, -1);

        switch (in["action"].Long()) {

            case MERMS_PROVIDERS_LOGIN:
                return provider_login(in, out);
                break;

            case MERMS_PROVIDERS_CREATEMEMBER:
                return provider_createmember(in, out);
                break;

            case MERMS_PROVIDERS_STARTPRACTICE:
                return provider_startNewPractice(in, out);
                break;

            case MERMS_PROVIDERS_CREATECHART:
                // create chart from provider side now
                return provider_chart(in, out);
                break;

            case MERMS_PROVIDERS_LINKMEMBER:
                return provider_linkIdemember(in, out);
                break;

            case EXISTING_CARD:
                REQ_LONG(in, "paymentid", 0, -1); // now we have to make sure the payment id is valid for this customer

                break;



            case INVOICED:
                const PGresult *res = pgsql_query("SELECT * FROM members WHERE invoiced =1 AND id=%lu", in["member_id"].Long());
                if (res != NULL && pgsql_num_rows(res) > 0) {
                    ret = PHP_API_OK;
                } else {
                    out["error_status"] = "user not authorized to use invoice";
                }
                break;

        }

    } catch (bad_parameter) {
        logfmt(logINFO, "ERROR CALL long providers_call(CVars in, CVars &out)");
    }
    return ret;
}

long provider_login(CVars in, CVars &out) {

    long ret = -1;
    try {
        REQ_STRING(in, "username", 2, 49, "(.*)");
        REQ_STRING(in, "pass", 2, 49, "(.*)");


        if (load_db_record(out, "SELECT p.name AS practice_name,u.*,u.id AS user_id FROM practice_users u LEFT JOIN practice p ON p.id = u.practice_id WHERE u.id = 1 ") > 0) {
            ret = PracticeLogin(in, out);
        }


    } catch (bad_parameter) {
        logfmt(logINFO, "ERROR CALL long provider_login(CVars in, CVars &out)");
    }
    return ret;

}

//000

long provider_linkIdemember(CVars in, CVars &out) {

    long ret = PHP_API_BAD_PARAM;
    logfmt(logINFO, "Provider provider_linkIdemember()");
    long qret = 0;
    try {

        REQ_LONG(in, "practice_id", 0, -1);
        REQ_LONG(in, "practice_user_id", 0, -1);
        REQ_STRING(in, "patient_link_id", 5, 15, "(.*)");
        out["member_id"] = "0";
        qret = (load_db_record(out, "SELECT *, id AS member_id FROM members WHERE acc_link = '%s'", in["patient_link_id"].c_str()) == true);
        if (qret) {
            logfmt(logINFO, "Provider provider_linkIdemember() QERT=>%lu", qret);


            if (load_db_record(out, "SELECT * from patients WHERE practice_id =%lu AND member_id=%lu ", in["practice_id"].Long(), out["member_id"].Long()) == true) {

                out["message"] = "Member already linked";
            } else {
                CVars x;
                char vname[15];
                GenerateLongID(vname, sizeof (vname), 10);
                //snprintf(vname, sizeof (vname), "ME%05d", i);
                out["long_id"] = vname;


                x["practice_id"] = in["practice_id"];
                x["practice_id"].set_valid(true);
                x["practice_user_id"] = in["practice_user_id"];
                x["practice_user_id"].set_valid(true);
                x["member_id"] = out["member_id"];
                x["member_id"].set_valid(true);

                x["long_id"] = vname;
                x["long_id"].set_valid(true);
                out["patient_id"] = insert_db_record(DBS_VALID, "patients", "patients_id_seq", x);
                out["message"] = "Member add completed";
            }

        } else {
            out["message"] = "Link Id not found";
        }

    } catch (bad_parameter) {
        logfmt(logINFO, "ERROR CALL Provider provider_linkIdemember");
    }
    logfmt(logINFO, "/Provider provider_linkIdemember()");
    return ret;

}


long provider_startNewPractice(CVars in, CVars &out) {

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

    try {

     //   REQ_LONG(in, "practice_id", 0, -1);
     //   REQ_LONG(in, "practice_user_id", 0, -1);
        REQ_STRING(in, "practice_name", 5, 49, "(.*)");
        REQ_STRING(in, "username", 5, 49, "(.*)");
        REQ_STRING(in, "firstname", 2, 49, "(.*)");
        REQ_STRING(in, "lastname", 2, 49, "(.*)");
        OPTIONAL(in, "phone") REQ_STRING(in, "phone", 5, 23, "(.*)");
        REQ_STRING(in, "password", 5, 49, "(.*)");

        long member_id = 0;

        //REQ_STRING (in, "sessionid",  4, 40, "(.*)");
        const char * loc = getenv("REMOTE_ADDR");
        out["member_id"] = "0";

        ret = load_db_record(out, "SELECT *,id AS practice_pending_id FROM practice_pending WHERE LOWER(username)=LOWER('%s') ", in["username"].c_str());
        logfmt(logINFO, "provider_startNewPractice()practice_pending_id=%lu", out["practice_pending_id"].Long());
        if (out["practice_pending_id"].Long() == 0) {

            // LETS CREATE THE ACOUNT NOW
            CVars x;
            const char * loc = getenv("REMOTE_ADDR");
            x["loc"] = loc;
            x["loc"].set_valid(true);
            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["practice_name"] = in["practice_name"];
            x["practice_name"].set_valid(true);
            
            out["practice_pending_id"] = insert_db_record(DBS_VALID, "practice_pending", "practice_pending_id_seq", x);

            if (out["practice_pending_id"].Long() > 0) {
             
                // Now Send Email			
                ret = PHP_API_OK;
                //make your patient
                provider_email_calls(MERMS_PROVIDERS_STARTPRACTICE, out, out);
                // ==============
            }
        } // if username
    } catch (bad_parameter) {
        logfmt(logINFO, "ERROR CALL provider_startNewPractice");
    }
    logfmt(logINFO, "/provider_startNewPractice()");
    return ret;
}


long provider_createmember(CVars in, CVars &out) {

    long ret = PHP_API_BAD_PARAM;
    logfmt(logINFO, "Provider CreateMember()");

    try {

        REQ_LONG(in, "practice_id", 0, -1);
        REQ_LONG(in, "practice_user_id", 0, -1);
        REQ_STRING(in, "username", 5, 49, "(.*)");
        REQ_STRING(in, "firstname", 2, 49, "(.*)");
        REQ_STRING(in, "lastname", 2, 49, "(.*)");
        OPTIONAL(in, "phone") REQ_STRING(in, "phone", 5, 23, "(.*)");
        REQ_STRING(in, "password", 5, 49, "(.*)");
        OPTIONAL(in, "phone") REQ_STRING(in, "loc", 5, 16, "(.*)");
        OPTIONAL(in, "login") REQ_LONG(in, "login", 0, -1);

        long member_id = 0;

        //REQ_STRING (in, "sessionid",  4, 40, "(.*)");
        const char * loc = getenv("REMOTE_ADDR");
        out["member_id"] = "0";

        ret = load_db_record(out, "SELECT *,id AS member_id FROM members WHERE LOWER(username)=LOWER('%s') ", in["username"].c_str());
        logfmt(logINFO, "Provider CreateMember()member_id=%lu", out["member_id"].Long());
        if (out["member_id"].Long() == 0) {

            // LETS CREATE THE ACOUNT NOW
            CVars x;
            const char * loc = getenv("REMOTE_ADDR");
            x["loc"] = loc;
            x["loc"].set_valid(true);
            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["phone"] = in["phone"];
            x["phone"].set_valid(true);
            // x["loc"] = in["loc"];
            // x["loc"].set_valid(true);
            x["email"] = in["email"];
            x["email"].set_valid(true);
            //  x["password"] = in["email"];
            // x["password"].set_valid(true);
            out["member_id"] = insert_db_record(DBS_VALID, "members", "members_id_seq", x);

            if (out["member_id"].Long() > 0) {
                pgsql_query("UPDATE members SET password = md5('%s') WHERE id  = %lu", in["password"].c_str(), out["member_id"].Long()); // setting the password MD5 now
                // Now Send Email			
                ret = PHP_API_OK;
                //make your patient
                CVars z;
                z["practice_id"] = in["practice_id"];
                z["practice_id"].set_valid(true);
                z["practice_user_id"] = in["practice_user_id"];
                z["practice_user_id"].set_valid(true);
                z["member_id"] = out["member_id"];
                z["member_id"].set_valid(true);
                provider_acceptmember(z, out);

                out["status"] = "OK";
                member_id = out["member_id"].Long();
                in["member_id"] = out["member_id"];
                in["member_id"].set_valid(true);
                medTrUpdateProfile(in, out);

                // ==============
            }
        } // if username
    } catch (bad_parameter) {
        logfmt(logINFO, "ERROR CALL Provider CreateMember");
    }
    logfmt(logINFO, "/Provider CreateMember()");
    return ret;
}

long provider_acceptmember(CVars in, CVars &out) {
    long ret = PHP_API_BAD_PARAM;
    logfmt(logINFO, "provider_acceptmember()");
    char vname[15];
    try {

        REQ_LONG(in, "practice_id", 0, -1);
        REQ_LONG(in, "practice_user_id", 0, -1);
        REQ_LONG(in, "member_id", 0, -1);

        GenerateLongID(vname, sizeof (vname), 10);
        //snprintf(vname, sizeof (vname), "ME%05d", i);
        out["long_id"] = vname;



        CVars x;
        x["practice_id"] = in["practice_id"];
        x["practice_id"].set_valid(true);
        x["practice_user_id"] = in["practice_user_id"];
        x["practice_user_id"].set_valid(true);
        x["member_id"] = in["member_id"];
        x["member_id"].set_valid(true);

        x["long_id"] = vname;
        x["long_id"].set_valid(true);
        out["patient_id"] = insert_db_record(DBS_VALID, "patients", "patients_id_seq", x);



    } catch (bad_parameter) {
        logfmt(logINFO, "ERROR CALL Provider CreateMember");
    }
    logfmt(logINFO, "/provider_acceptmember()");
    return ret;
}

void GenerateLongID(char * number, int sz, int seed) {
    char chars[] = "WRB01234WRB56789WRB";
    int n = sizeof (chars);
    int i;
    /*
            time_t t;
            time( &t );
            pid_t pid = getpid();

            srand(t+pid);
     */
    srand((unsigned int) time((time_t *) NULL));

    for (i = 0; i < 10 && i < sz - 1; i++) {
        int k = seed % n;
        if (seed > 1 && k == i) {
            number[i] = chars[n - k];
        } else {
            number[i] = chars[ (int) ((n - 1.0) * rand() / (RAND_MAX + 1.0)) ];
        }
    }
    number[i] = 0;
}


//XXX

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

    try {
        REQ_STRING(in, "username", 2, 49, "(.*)");
        REQ_STRING(in, "pass", 2, 49, "(.*)");
        const char * loc = getenv("REMOTE_ADDR");
        ret = load_db_record(out, "SELECT UPPER( md5( now()::text) ) AS sessionid,m.*,m.*,m.id AS practice_users_id FROM practice_users m \
			            WHERE m.status=1 AND LOWER(m.username)=LOWER('%s') AND m.password= md5('%s')", in["username"].c_str(), in["pass"].c_str());
        if (ret && out["practice_users_id"].Long() > 0) {
            // remove all existing session
            pgsql_exec("DELETE FROM practice_users_session WHERE practice_users_id=%ld ", out["practice_users_id"].Long());
            // Create New Session Now
            if (PracticeSessionCheck(out["practice_id"].Long(), out["practice_users_id"].Long(), out["sessionid"].c_str(), 1) > 0) {

                out["stauts"] = "OK";
                /*LOAD THE SESSION INTO OUT now */
                load_db_record(out, "SELECT session FROM practice_users_session WHERE practice_users_id=%lu ORDER BY id DESC LIMIT 1", out["practice_users_id"].Long());
                provider_email_calls(in["action"].Long(), in, out);
                //===============================================================================================================================
                pgsql_query("UPDATE practice_users SET last_login = now() WHERE id  = %lu", out["practice_users_id"].Long());
                // account_email(ACCOUNT_LOGIN_ALERT,out,out); // ALERT CUSTOMER OF LOGIN
                ret = PHP_LOGIN_OK;
            } else {
                out["status"] = "Practice Session Check Failed";
            }

        } else {
            out["status_message"] = "Invalid Username/Password";
        }

    } catch (bad_parameter) {
        logfmt(logINFO, "ERROR CALL long PracticeLogin(CVars in, CVars &out)");
    }
    return ret;
}

long PracticeSessionCheck(long practice_id, long practice_users_id, const char *sessionid, int create) {
    logfmt(logINFO, "long PracticeSessionCheck(long uid, const char *sessionid, int create )");
    // Sanity check

    long session_expired_minutes = 15; // load in the global 


    if (practice_users_id < 1 || sessionid == NULL || strlen(sessionid) < 4) {
        return -1L; // Invalif parameters
    }
    logfmt(logINFO, "#######-#########-A");
    // Clean old sessions
    if (create == 1) // Clean Previous session by force
    {
        pgsql_exec("DELETE FROM practice_users_session WHERE practice_user_id=%ld", practice_users_id);
    }
    logfmt(logINFO, "#######-#########-B");

    pgsql_exec("DELETE FROM practice_users_session WHERE practice_user_id=%ld AND updated < (now() - interval '%lu minutes')", practice_users_id, session_expired_minutes);
    // Update/check existing session
    if (create == 0) {

        pgsql_exec("UPDATE practice_users_session SET updated=NOW() WHERE practice_users_id=%ld AND session='%s'", practice_users_id, sessionid);

        const PGresult *res = pgsql_query("SELECT * FROM practice_users_session WHERE practice_users_id=%ld AND session='%s'", practice_users_id, sessionid);
        if (res != NULL && pgsql_num_rows(res) > 0) {
            logfmt(logINFO, "VALID SESSION *****");
            return 1L; // Session updated
        } else {
            logfmt(logINFO, "INVALID SESSION *****");
            //INVALID SESSION DETECTED
            return -1L; // Invalid parameters
        }

    }


    if (create > 0) {
        // Check session i?
        const PGresult *res = pgsql_query("SELECT * FROM practice_users_session WHERE practice_user_id=%ld AND session<>'%s'", practice_users_id, 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 practice_users_session WHERE practice_user_id=%lu AND session='%s'", practice_users_id, sessionid) > 0) {
            pgsql_exec("UPDATE practice_users_session SET updated=NOW() WHERE practice_user_id=%ld AND session='%s'", practice_users_id, sessionid);
            return sess["id"].Long();
        }
        // Create a new session
        const char * loc = getenv("REMOTE_ADDR");
        sess["loc"] = loc;
        sess["loc"].set_valid(true);
        sess["practice_id"] = practice_id;
        sess["practice_id"].set_valid(true);
        sess["practice_user_id"] = practice_users_id;
        sess["practice_user_id"].set_valid(true);
        sess["session"] = sessionid;
        sess["session"].set_valid(true);
        long sid = insert_db_record(DBS_VALID, "practice_users_session", "practice_users_session_id_seq", sess); //members_session_id_seq
        if (sid > 0) {
            return sid; // New session created
        }
        return -3L; // Failed to create new session
    }
    logfmt(logINFO, "/long PracticeSessionCheck(long uid, const char *sessionid, int create )");
    return 0L; // No route
}
