#ifndef DB_UTILS_H
#define DB_UTILS_H

#include "r.h"
#include "utils.h"
#include <json-c/json.h>
#include <sqlite3.h>

json_object *db_execute(const char *query);

char *db_file_expanded() {
  char *expanded = expand_home_directory(DB_FILE);
  static char result[4096];
  result[0] = 0;
  strcpy(result, expanded);
  free(expanded);
  return result;
}

void db_initialize();
json_object *db_set(const char *key, const char *value);
json_object *db_get(const char *key);
json_object *db_query(const char *query);

void db_initialize() {
  sqlite3 *db;
  int rc = sqlite3_open(db_file_expanded(), &db);

  if (rc != SQLITE_OK) {
    return;
  }

  db_execute("CREATE TABLE IF NOT EXISTS kv_store (key TEXT PRIMARY KEY, value "
             "TEXT);");
  db_execute("CREATE TABLE IF NOT EXISTS file_version_history ( id INTEGER "
             "PRIMARY KEY AUTOINCREMENT,"
             "path TEXT NOT NULL,"
             "content TEXT,"
             "date DATETIME DEFAULT CURRENT_TIMESTAMP"
             ");");

  sqlite3_close(db);
}

json_object *db_set(const char *key, const char *value) {
  sqlite3 *db;
  char *err_msg = 0;
  int rc = sqlite3_open(db_file_expanded(), &db);

  if (rc != SQLITE_OK) {
    return NULL;
  }

  char *sql =
      sqlite3_mprintf("INSERT INTO kv_store (key, value) VALUES (%Q, %Q) ON "
                      "CONFLICT(key) DO UPDATE SET value = %Q WHERE key = %Q",
                      key, value, value, key);
  rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
  sqlite3_free(sql);

  if (rc != SQLITE_OK) {
    sqlite3_free(err_msg);
    sqlite3_close(db);
    return NULL;
  }

  sqlite3_close(db);
  return json_object_new_string("Success");
}

json_object *db_get(const char *key) {
  sqlite3 *db;
  sqlite3_stmt *stmt;
  json_object *result = json_object_new_object();
  const char *value = NULL;

  int rc = sqlite3_open(db_file_expanded(), &db);
  if (rc != SQLITE_OK) {
    return NULL;
  }

  const char *sql = "SELECT value FROM kv_store WHERE key = ?";
  sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
  sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);

  if (sqlite3_step(stmt) == SQLITE_ROW) {
    value = (const char *)sqlite3_column_text(stmt, 0);
  }

  if (value) {
    json_object_object_add(result, "value", json_object_new_string(value));
  } else {
    json_object_object_add(result, "error",
                           json_object_new_string("Key not found"));
  }

  sqlite3_finalize(stmt);
  sqlite3_close(db);
  return result;
}
json_object *db_query(const char *query) {
  sqlite3 *db;
  sqlite3_stmt *stmt;

  if (strncmp(query, "SELECT", 6)) {
    return db_execute(query);
  }

  json_object *result = json_object_new_array();

  int rc = sqlite3_open(db_file_expanded(), &db);
  if (rc != SQLITE_OK) {
    return NULL;
  }

  sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
  while (sqlite3_step(stmt) == SQLITE_ROW) {
    json_object *row = json_object_new_object();
    for (int i = 0; i < sqlite3_column_count(stmt); i++) {
      const char *col_name = sqlite3_column_name(stmt, i);
      switch (sqlite3_column_type(stmt, i)) {
      case SQLITE_INTEGER:
        json_object_object_add(
            row, col_name,
            json_object_new_int64(sqlite3_column_int64(stmt, i)));
        break;
      case SQLITE_FLOAT:
        json_object_object_add(
            row, col_name,
            json_object_new_double(sqlite3_column_double(stmt, i)));
        break;
      case SQLITE_TEXT:
        json_object_object_add(
            row, col_name,
            json_object_new_string((const char *)sqlite3_column_text(stmt, i)));
        break;
      case SQLITE_BLOB:
        json_object_object_add(row, col_name,
                               json_object_new_string_len(
                                   (const char *)sqlite3_column_blob(stmt, i),
                                   sqlite3_column_bytes(stmt, i)));
        break;
      case SQLITE_NULL:
      default:
        json_object_object_add(row, col_name, json_object_new_string("NULL"));
        break;
      }
    }
    json_object_array_add(result, row);
  }

  sqlite3_finalize(stmt);
  sqlite3_close(db);

  return result;
}

json_object *db_execute(const char *query) {
  sqlite3 *db;
  char *err_msg = 0;
  int rc = sqlite3_open(db_file_expanded(), &db);
  json_object *result = json_object_new_object();

  if (rc != SQLITE_OK) {
    json_object_object_add(result, "error",
                           json_object_new_string("Cannot open database"));
    return result;
  }

  rc = sqlite3_exec(db, query, 0, 0, &err_msg);
  if (rc != SQLITE_OK) {
    json_object_object_add(result, "error", json_object_new_string(err_msg));
    sqlite3_free(err_msg);
  } else {
    json_object_object_add(
        result, "success",
        json_object_new_string("Query executed successfully"));
  }

  sqlite3_close(db);
  return result;
}
void db_store_file_version(const char *path) {
  char *expanded = expand_home_directory(path);

  char *content = read_file(expanded);
  if (!content) {
    return;
  }
  fprintf(stderr, "Creating backup:: %s\n", expanded);
  char *formatted = sqlite3_mprintf(
      "INSERT INTO file_version_history (path, content) VALUES (%Q, %Q)",
      expanded, content);
  db_execute(formatted);
  sqlite3_free(formatted);
  free(content);
}
char *db_get_schema() {
  json_object *tables =
      db_query("SELECT * FROM sqlite_master WHERE type='table'");
  char *result = strdup(json_object_get_string(tables));
  json_object_put(tables);
  return result;
}

#endif
