#include <kore/kore.h>
#include <kore/http.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sqlite3.h>
#include <unistd.h>
#include "assets.h"
#include "sessions.h"
#include "sqlite_utils.h"
#include <pwd.h>

#define SESSION_LEN 30
#define DB_NAME "portal_user.db"

#if defined(__linux__)

#include <kore/seccomp.h>
#include <crypt.h>

KORE_SECCOMP_FILTER("sqlite3",
                    KORE_SYSCALL_ALLOW(newfstatat),
                    KORE_SYSCALL_ALLOW(pread64),
                    KORE_SYSCALL_ALLOW(pwrite64),
                    KORE_SYSCALL_ALLOW(fdatasync),
                    KORE_SYSCALL_ALLOW_ARG(write, 0, STDOUT_FILENO)
                    );
#endif


int init(int state);
int portal_user_load(struct http_request *);
int v_password_func(struct http_request *, char *);
int create_user(struct http_request *);
int v_session_validate(struct http_request *, char *);
int v_session_remove(struct http_request *, char *);
int private_portal(struct http_request *);

hashtable_t *hashtable = NULL;

int init(int state){
    hashtable = ht_create( 65536 );
    int err=0;
    if( hashtable == NULL )
    {
        kore_log(LOG_ERR, "Can't create hastable sessions");
        err = 1;
    }

    if ( access( DB_NAME, F_OK ) == 0 && err == 0 )
    { kore_log(LOG_NOTICE,  "Execute check_db()");
        err = check_db(DB_NAME);}
    else
    {
        kore_log(LOG_NOTICE, "Create Sqlite Base");
        err = init_db(DB_NAME);
    }
    if (err)
        return (KORE_RESULT_ERROR);
    else
        return (KORE_RESULT_OK);
}

int create_user(struct http_request *req)
{
    struct kore_buf *b = NULL;
    u_int8_t *d = NULL;
    size_t len = 0;
    char *salt = NULL, *cryptpwd = NULL;
    char *zErrMsg = NULL;
    char *user = NULL, *pwd = NULL, name[50];
    sqlite3 *db = NULL;
    char sql[512];

    if (req->method == HTTP_METHOD_GET)
        http_populate_get(req);
    else if (req->method == HTTP_METHOD_POST)
        http_populate_post(req);

    b = kore_buf_alloc(asset_len_signup_html);
    kore_buf_append(b, asset_signup_html, asset_len_signup_html);


    if (req->method == HTTP_METHOD_GET) {
        kore_buf_replace_string(b, "$msg$", "GO", 4);

        http_response_header(req, "content-type", "text/html");
        d = kore_buf_release(b, &len);
        http_response(req, 200, d, len);
        kore_free(d);

        return (KORE_RESULT_OK);
    }

    if (req->method == HTTP_METHOD_POST)
    {
        int rc = sqlite3_open(DB_NAME, &db);

        if (rc != SQLITE_OK) {

            kore_log(LOG_ERR, "Cannot open database: %s\n", sqlite3_errmsg(db));
            sqlite3_close(db);

            return (KORE_RESULT_ERROR);
        }
        //salt = crypt_gensalt("$2b$", 15, NULL, 0); //-> linux
        salt = bcrypt_gensalt(15); //-> openbsd

        if (salt == NULL) {
            kore_log(LOG_ERR, "crypt_gensalt");
            exit(EXIT_FAILURE);
        }
        kore_log(LOG_NOTICE, "bcrypt_salt réussi");

        (void)snprintf(name, sizeof(name), "login");
        http_argument_get_string(req, name, &user);

        (void)snprintf(name, sizeof(name), "password");
        http_argument_get_string(req, name, &pwd);

        cryptpwd = crypt(pwd, salt);
        if (cryptpwd == NULL) {
            kore_log(LOG_PERROR, "Can't encrypt password");
            exit(EXIT_FAILURE);
        }


       kore_log(LOG_NOTICE, "Encrypted called %s", cryptpwd );

       (void)sprintf(sql,
               "INSERT INTO Users (Email, Password, Active) VALUES(\"%s\",\"%s\",\"yes\");",
               user, cryptpwd);


       printf ( "%s",sql);
       rc = sqlite3_exec(db, sql, 0, 0, &zErrMsg);

       if( rc != SQLITE_OK ){
           kore_log(LOG_ERR, "SQL error: %s", zErrMsg);
           sqlite3_free(zErrMsg);
       } else {
           kore_log(LOG_NOTICE, "Database successfully created !");
       }

       sqlite3_close(db);
       http_response_header(req, "location", "/");
       http_response_header(req, "content-type", "text/html");
       d = kore_buf_release(b, &len);
       http_response(req, HTTP_STATUS_FOUND, NULL, 0);
       kore_free(d);

       return (KORE_RESULT_OK);
    }

    return (KORE_RESULT_ERROR);

}

int portal_user_load(struct http_request *req)
{
    struct kore_buf *b = NULL;
    u_int8_t *d = NULL;
    size_t len = 0;
    char *login = NULL, *pwd = NULL, name[70];
    char *err_msg = 0;
    char salt[29], *cryptpwd = NULL;
    int rc = 0;
    sqlite3_stmt *res= NULL;
    sqlite3 *db = NULL;
    char *cookie = NULL;
    char *session_id = NULL;

    if (req->method == HTTP_METHOD_GET)
        http_populate_get(req);
    else if (req->method == HTTP_METHOD_POST)
        http_populate_post(req);

    b = kore_buf_alloc(asset_len_index_html);
    kore_buf_append(b, asset_index_html, asset_len_index_html);

    if (req->method == HTTP_METHOD_GET) {
        kore_buf_replace_string(b, "$login$", NULL, 0);
        kore_buf_replace_string(b, "$password$", NULL, 0);
        kore_buf_replace_string(b, "$msg$", "Toto", 4);

        http_response_header(req, "content-type", "text/html");
        d = kore_buf_release(b, &len);
        http_response(req, 200, d, len);
        kore_free(d);

        return (KORE_RESULT_OK);
    }

    if (req->method == HTTP_METHOD_POST)
    {
        rc = sqlite3_open(DB_NAME, &db);

        if (rc != SQLITE_OK)
        {
            kore_log(LOG_ERR, "Cannot open database: %s\n", sqlite3_errmsg(db));
            sqlite3_close(db);

            return (KORE_RESULT_ERROR);
        }

        char *sql = "SELECT Email, Password FROM Users WHERE Email = ?";

        rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);

        if (rc == SQLITE_OK)
        {
            (void)snprintf(name, sizeof(name), "login");
            http_argument_get_string(req, name, &login);
            sqlite3_bind_text(res, 1, login , -1, NULL);
        }
        else
        {
            kore_log(LOG_ERR, "Failed to execute statement: %s\n", sqlite3_errmsg(db));
        }

        sqlite3_step(res);
        if (rc != SQLITE_OK ) {
            kore_log(LOG_ERR, "SQL error: %s\n", err_msg);
            sqlite3_finalize(res);
            sqlite3_free(err_msg);
            sqlite3_close(db);
            return (KORE_RESULT_ERROR);
        }

        (void)snprintf(name, sizeof(name), "password");
        http_argument_get_string(req, name, &pwd);


        strncpy (salt,(const char *)sqlite3_column_text(res, 1),29);


        cryptpwd = crypt(pwd, salt);
        if (cryptpwd == NULL) {
            kore_log(LOG_PERROR, "Can't encrypt password");
            exit(EXIT_FAILURE);
        }


        if ( strcmp( (const char *)sqlite3_column_text(res, 1), cryptpwd) )
        {
            printf("mauvais mot de passe\n");
            kore_buf_replace_string(b, "$msg$", "Erreur de login ou mot de passe ", 17);
        }
        else
        {
            kore_log(LOG_NOTICE, "on va faire le set cookie");
            session_id = gen_session_id(SESSION_LEN);
            ht_set(hashtable, session_id, login);

            kore_log(LOG_NOTICE, "on a ajouté le sessions dans la hastable");
            cookie = set_cookie_header("session_id", '=', session_id);
            kore_log(LOG_NOTICE, "on a set le cookie dans les headers");
            kore_buf_replace_string(b, "$msg$", "BRAVO !!!", 13);
            free(session_id);
            kore_log(LOG_NOTICE, "set cookie OK");
        }

        sqlite3_finalize(res);
        sqlite3_close(db);


        http_response_header(req, "content-type", "text/html");
        if (cookie != NULL)
            http_response_header(req, "set-cookie", cookie);
        d = kore_buf_release(b, &len);

       http_response_header(req, "location", "/portal/bienvenue");
       http_response(req, HTTP_STATUS_FOUND, NULL, 0);
       kore_free(d);
        return (KORE_RESULT_OK);
    }

    return (KORE_RESULT_ERROR);

}

int v_password_func(struct http_request *req, char *data)
{
    kore_log(LOG_NOTICE, "v_password_func called %s", data);

    if ( strlen(data) < 128 )
        return (KORE_RESULT_OK);

    return (KORE_RESULT_ERROR);

}

int v_session_remove (struct http_request *req, char *data)
{
    char buffer[SESSION_LEN];
    kore_log(LOG_NOTICE, "v_session_remove: %s", data);
    (void)snprintf(buffer, SESSION_LEN, "%s", data);

    if (ht_get(hashtable, buffer) != NULL)
        ht_delete(hashtable, buffer);
    http_response_header(req, "location", "/");
    http_response_header(req, "content-type", "text/html");
    http_response_header(req, "set-cookie", "session_id=""");
    http_response(req, HTTP_STATUS_FOUND, NULL, 0);

    return (KORE_RESULT_OK);
}

int v_session_validate(struct http_request *req, char *data)
{
    kore_log(LOG_NOTICE, "v_session_validate: %s", data);

    if ( ht_get(hashtable, data) != NULL )
        return (KORE_RESULT_OK);

    return (KORE_RESULT_ERROR);
}

int private_portal(struct http_request *req)
{
    struct kore_buf *b = NULL;
    u_int8_t *d = NULL;
    size_t len = 0;

    if (req->method == HTTP_METHOD_GET)
        http_populate_get(req);

    b = kore_buf_alloc(asset_len_signup_html);
    kore_buf_append(b, asset_private_html, asset_len_private_html);


    if (req->method == HTTP_METHOD_GET) {
        kore_buf_replace_string(b, "$msg$", "GO", 4);

        http_response_header(req, "content-type", "text/html");
        d = kore_buf_release(b, &len);
        http_response(req, 200, d, len);
        kore_free(d);
        return (KORE_RESULT_OK);
    }
}
