#include <sqlite3.h>
#include <iostream>
#include <algorithm>
#include "Logger.h"
#include <cctype>

bool db_exec(sqlite3* targetDB, const char* command) {
	char* errMsg = nullptr;
	int rc = sqlite3_exec(targetDB, command, 0, 0, &errMsg);
	if (rc != SQLITE_OK) {
		postLog("[SQLite] SQL error: " + std::string(errMsg), 4);
		sqlite3_free(errMsg);
		return false;
	}
	return true;
}

bool db_addItem(sqlite3* targetDB, std::string targetTable, int id, std::string sector, std::string value) {
	if (!targetDB) {
		postLog("[SQLite] targetDB is null.", 3);
		return false;
	}
	char* sql = nullptr;
	if (targetTable == "users") {
		sql = sqlite3_mprintf(
			"INSERT INTO users (userId, %Q) VALUES (%d, %Q);",
			sector.c_str(),
			id,
			value.c_str()
		);
	}
	else if (targetTable == "salaryForm") {
		sql = sqlite3_mprintf(
			"INSERT INTO salaryForm (teacherId, %Q) VALUES (%d, %Q);",
			sector.c_str(),
			id,
			value.c_str()
		);
	}
	else if (targetTable == "teacherInfo") {
		sql = sqlite3_mprintf(
			"INSERT INTO teacherInfo (teacherId, %Q) VALUES (%d, %Q);",
			sector.c_str(),
			id,
			value.c_str()
		);
	}
	else {
		postLog("[SQLite] Unknown target table.", 3);
		return false;
	}


	if (!sql) {
		postLog("[SQLite] Failed to allocate SQL string.", 3);
		return false;
	}

	char* errMsg = nullptr;
	int rc = sqlite3_exec(targetDB, sql, 0, 0, &errMsg);

	sqlite3_free(sql);

	if (rc != SQLITE_OK) {
		postLog(std::string("[SQLite] Failed to add item: ") + (errMsg ? errMsg : "unknown error"), 3);
		if (errMsg) sqlite3_free(errMsg);
		return false;
	}

	return true;
}

bool db_updateItem(sqlite3* targetDB, std::string targetTable, int id, std::string sector, std::string value) {
	if (!targetDB) {
		postLog("[SQLite] targetDB is null.", 3);
		return false;
	}
	char* sql = nullptr;
	if (targetTable == "users"){
		sql = sqlite3_mprintf(
			"UPDATE users SET %s=\'%s\' WHERE userId=%d;",
			sector.c_str(),
			value.c_str(),
			id
		);
	}
	else if (targetTable == "salaryForm") {
		sql = sqlite3_mprintf(
			"UPDATE salaryForm SET %s=\'%s\' WHERE teacherId=%d;",
			sector.c_str(),
			value.c_str(),
			id
		);
	}
	else if (targetTable == "teacherInfo") {
		sql = sqlite3_mprintf(
			"UPDATE teacherInfo SET %s=\'%s\' WHERE teacherId=%d;",
			sector.c_str(),
			value.c_str(),
			id
		);
	}
	else {
		postLog("[SQLite] Unknown target table.", 3);
		return false;
	}
	

	if (!sql) {
		postLog("[SQLite] Failed to allocate SQL string.", 3);
		return false;
	}

	char* errMsg = nullptr;
	int rc = sqlite3_exec(targetDB, sql, 0, 0, &errMsg);	

	sqlite3_free(sql);

	if (rc != SQLITE_OK) {
		postLog(std::string("[SQLite] Failed to add item: ") + (errMsg ? errMsg : "unknown error"), 3);
		if (errMsg) sqlite3_free(errMsg);
		return false;
	}

	return true;
}

bool db_rmItem(sqlite3* targetDB, std::string targetTable, int id) {
	if (!targetDB) {
		postLog("[SQLite] targetDB is null.", 3);
		return false;
	}
	char* sql = sqlite3_mprintf(
		"DELETE FROM %s WHERE teacherId = %d;",
		targetTable.c_str(),
		id
	);
	if (!sql) {
		postLog("[SQLite] Failed to allocate SQL string.", 3);
		return false;
	}
	char* errMsg = nullptr;
	int rc = sqlite3_exec(targetDB, sql, 0, 0, &errMsg);

	sqlite3_free(sql);
	if (rc != SQLITE_OK) {
		postLog(std::string("[SQLite] Failed to remove item: ") + (errMsg ? errMsg : "unknown error"), 3);
		if (errMsg) sqlite3_free(errMsg);
		return false;
	}
	return true;
}

std::string db_getItem(sqlite3* sourceDB, std::string sourceTable, std::string itemType, int itemID) {
    if (!sourceDB) {
        postLog("[SQLite] sourceDB is null.", 3);
        return "";
    }

    std::string command;
    if (sourceTable == "users") {
        command = "SELECT " + itemType + " FROM users WHERE userId = " + std::to_string(itemID) + ";";
    } else if (sourceTable == "salaryForm") {
        command = "SELECT " + itemType + " FROM salaryForm WHERE teacherId = " + std::to_string(itemID) + ";";
	}
	else if (sourceTable == "teacherInfo") {
		command = "SELECT " + itemType + " FROM teacherInfo WHERE teacherId = " + std::to_string(itemID) + ";";
	}
	else {
        postLog("[SQLite] Unknown table in db_getItem.", 3);
        return "";
    }

    sqlite3_stmt* stmt = nullptr;
    int rc = sqlite3_prepare_v2(sourceDB, command.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        postLog(std::string("[SQLite] Precompiling failed: ") + sqlite3_errmsg(sourceDB), 3);
        if (stmt) sqlite3_finalize(stmt);
        return "";
    }

    std::string result;
    rc = sqlite3_step(stmt);
    if (rc == SQLITE_ROW) {
        const unsigned char* txt = sqlite3_column_text(stmt, 0);
        if (txt) result = reinterpret_cast<const char*>(txt);
    } else if (rc == SQLITE_DONE) {
        postLog("[SQLite] Failed to find specific ID [" + std::to_string(itemID) + "]", 3);
        result = "err: no specific id";
    } else {
        postLog(std::string("[SQLite] Step error: ") + sqlite3_errmsg(sourceDB), 4);
    }

    sqlite3_finalize(stmt);
    return result;
}

std::string db_findIdBy(sqlite3* sourceDB, std::string sourceTable, std::string itemType, std::string value) {
    if (!sourceDB) {
        postLog("[SQLite] sourceDB is null.", 3);
        return "0";
    }

    const std::string idCol = (sourceTable == "users") ? "userId" : "teacherId";
    std::string sql = "SELECT " + idCol + " FROM " + sourceTable + " WHERE " + itemType + " = ?;";

    sqlite3_stmt* stmt = nullptr;
    if (sqlite3_prepare_v2(sourceDB, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
        postLog(std::string("[SQLite] Precompiling failed: ") + sqlite3_errmsg(sourceDB), 3);
        return "0";
    }

    // Bind the value: numeric vs text
    bool isNumber = !value.empty() && std::all_of(value.begin(), value.end(), [](unsigned char ch){
        return std::isdigit(ch) != 0;
    });
    if (isNumber) {
        sqlite3_bind_int(stmt, 1, std::stoi(value));
    } else {
        sqlite3_bind_text(stmt, 1, value.c_str(), -1, SQLITE_TRANSIENT);
    }

    std::string result;
    int rc = sqlite3_step(stmt);
    if (rc == SQLITE_ROW) {
        const unsigned char* txt = sqlite3_column_text(stmt, 0);
        if (txt) result = reinterpret_cast<const char*>(txt);
    } else if (rc == SQLITE_DONE) {
        postLog("[SQLite] Failed to find specific ID by content [" + itemType + ":" + value + "]", 3);
        result = "0";
    } else {
        postLog(std::string("[SQLite] Step error: ") + sqlite3_errmsg(sourceDB), 4);
    }

    sqlite3_finalize(stmt);
    return result;
}