#include "database.h"
#include <iostream>
#include <ctime>

namespace scar {

Database::Database(const std::string& db_path)
    : db_(nullptr), db_path_(db_path) {}

Database::~Database() {
    if (db_) {
        sqlite3_close(db_);
    }
}

bool Database::initialize() {
    int rc = sqlite3_open(db_path_.c_str(), &db_);
    if (rc != SQLITE_OK) {
        std::cerr << "Cannot open database: " << sqlite3_errmsg(db_) << std::endl;
        return false;
    }
    
    const char* create_table_sql = R"(
        CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            username TEXT UNIQUE NOT NULL,
            password TEXT NOT NULL,
            salt TEXT NOT NULL,
            token TEXT,
            status TEXT DEFAULT 'offline',
            role TEXT,
            email TEXT,
            last_login INTEGER,
            avatar_pic BLOB
        );
        
        CREATE INDEX IF NOT EXISTS idx_username ON users(username);
        CREATE INDEX IF NOT EXISTS idx_token ON users(token);
    )";
    
    return execute(create_table_sql);
}

bool Database::execute(const std::string& sql) {
    char* err_msg = nullptr;
    int rc = sqlite3_exec(db_, sql.c_str(), nullptr, nullptr, &err_msg);
    
    if (rc != SQLITE_OK) {
        std::cerr << "SQL error: " << err_msg << std::endl;
        sqlite3_free(err_msg);
        return false;
    }
    
    return true;
}

bool Database::createUser(const std::string& username, const std::string& password_hash,
                         const std::string& salt) {
    const char* sql = "INSERT INTO users (username, password, salt) VALUES (?, ?, ?)";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        return false;
    }
    
    sqlite3_bind_text(stmt, 1, username.c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 2, password_hash.c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 3, salt.c_str(), -1, SQLITE_TRANSIENT);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return rc == SQLITE_DONE;
}

std::unique_ptr<UserRecord> Database::getUserByUsername(const std::string& username) {
    const char* sql = "SELECT id, username, password, salt, token, status, role, email, last_login FROM users WHERE username = ?";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        return nullptr;
    }
    
    sqlite3_bind_text(stmt, 1, username.c_str(), -1, SQLITE_TRANSIENT);
    
    std::unique_ptr<UserRecord> user;
    if (sqlite3_step(stmt) == SQLITE_ROW) {
        user = std::make_unique<UserRecord>();
        user->id = sqlite3_column_int(stmt, 0);
        user->username = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
        user->password_hash = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
        user->salt = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
        
        const char* token = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 4));
        user->token = token ? token : "";
        
        const char* status_str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 5));
        user->status = (status_str && std::string(status_str) == "online") 
                       ? UserStatus::ONLINE : UserStatus::OFFLINE;
        
        const char* role = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 6));
        user->role = role ? role : "";
        
        const char* email = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 7));
        user->email = email ? email : "";
        
        user->last_login = sqlite3_column_int64(stmt, 8);
    }
    
    sqlite3_finalize(stmt);
    return user;
}

bool Database::updateUserToken(const std::string& username, const std::string& token) {
    const char* sql = "UPDATE users SET token = ? WHERE username = ?";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        return false;
    }
    
    sqlite3_bind_text(stmt, 1, token.c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 2, username.c_str(), -1, SQLITE_TRANSIENT);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return rc == SQLITE_DONE;
}

bool Database::updateUserStatus(const std::string& username, UserStatus status) {
    const char* status_str = (status == UserStatus::ONLINE) ? "online" : "offline";
    const char* sql = "UPDATE users SET status = ? WHERE username = ?";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        return false;
    }
    
    sqlite3_bind_text(stmt, 1, status_str, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, username.c_str(), -1, SQLITE_TRANSIENT);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return rc == SQLITE_DONE;
}

bool Database::updateLastLogin(const std::string& username) {
    int64_t now = std::time(nullptr);
    const char* sql = "UPDATE users SET last_login = ? WHERE username = ?";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        return false;
    }
    
    sqlite3_bind_int64(stmt, 1, now);
    sqlite3_bind_text(stmt, 2, username.c_str(), -1, SQLITE_TRANSIENT);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return rc == SQLITE_DONE;
}

bool Database::verifyCredentials(const std::string& username, const std::string& password_hash) {
    auto user = getUserByUsername(username);
    return user && user->password_hash == password_hash;
}

std::string Database::getUsernameByToken(const std::string& token) {
    const char* sql = "SELECT username FROM users WHERE token = ?";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        return "";
    }
    
    sqlite3_bind_text(stmt, 1, token.c_str(), -1, SQLITE_TRANSIENT);
    
    std::string username;
    if (sqlite3_step(stmt) == SQLITE_ROW) {
        username = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
    }
    
    sqlite3_finalize(stmt);
    return username;
}

bool Database::deleteUser(const std::string& username) {
    const char* sql = "DELETE FROM users WHERE username = ?";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        return false;
    }
    
    sqlite3_bind_text(stmt, 1, username.c_str(), -1, SQLITE_TRANSIENT);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return rc == SQLITE_DONE;
}

bool Database::updateUserPassword(const std::string& username, const std::string& password_hash,
                                 const std::string& salt) {
    const char* sql = "UPDATE users SET password = ?, salt = ? WHERE username = ?";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        return false;
    }
    
    sqlite3_bind_text(stmt, 1, password_hash.c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 2, salt.c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 3, username.c_str(), -1, SQLITE_TRANSIENT);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return rc == SQLITE_DONE;
}

bool Database::updateUserAvatar(const std::string& username, const std::vector<uint8_t>& avatar_data) {
    const char* sql = "UPDATE users SET avatar_pic = ? WHERE username = ?";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        return false;
    }
    
    sqlite3_bind_blob(stmt, 1, avatar_data.data(), avatar_data.size(), SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 2, username.c_str(), -1, SQLITE_TRANSIENT);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return rc == SQLITE_DONE;
}

bool Database::updateUserEmail(const std::string& username, const std::string& email) {
    const char* sql = "UPDATE users SET email = ? WHERE username = ?";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        return false;
    }
    
    sqlite3_bind_text(stmt, 1, email.c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 2, username.c_str(), -1, SQLITE_TRANSIENT);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return rc == SQLITE_DONE;
}

bool Database::updateUserRole(const std::string& username, const std::string& role) {
    const char* sql = "UPDATE users SET role = ? WHERE username = ?";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        return false;
    }
    
    sqlite3_bind_text(stmt, 1, role.c_str(), -1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 2, username.c_str(), -1, SQLITE_TRANSIENT);
    
    rc = sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    
    return rc == SQLITE_DONE;
}

std::vector<UserRecord> Database::searchUsers(const std::string& field, const std::string& value) {
    std::vector<UserRecord> results;
    
    std::string sql = "SELECT id, username, password, salt, token, status, role, email, last_login, avatar_pic FROM users WHERE " + field + " LIKE ?";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        return results;
    }
    
    std::string search_pattern = "%" + value + "%";
    sqlite3_bind_text(stmt, 1, search_pattern.c_str(), -1, SQLITE_TRANSIENT);
    
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        UserRecord user;
        user.id = sqlite3_column_int(stmt, 0);
        user.username = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
        user.password_hash = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
        user.salt = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
        
        const char* token = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 4));
        user.token = token ? token : "";
        
        const char* status_str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 5));
        user.status = (status_str && std::string(status_str) == "online") 
                      ? UserStatus::ONLINE : UserStatus::OFFLINE;
        
        const char* role = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 6));
        user.role = role ? role : "";
        
        const char* email = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 7));
        user.email = email ? email : "";
        
        user.last_login = sqlite3_column_int64(stmt, 8);
        
        const void* blob = sqlite3_column_blob(stmt, 9);
        int blob_size = sqlite3_column_bytes(stmt, 9);
        if (blob && blob_size > 0) {
            const uint8_t* data = static_cast<const uint8_t*>(blob);
            user.avatar_pic.assign(data, data + blob_size);
        }
        
        results.push_back(user);
    }
    
    sqlite3_finalize(stmt);
    return results;
}

std::vector<UserRecord> Database::getAllUsers() {
    std::vector<UserRecord> results;
    
    const char* sql = "SELECT id, username, password, salt, token, status, role, email, last_login, avatar_pic FROM users ORDER BY username";
    
    sqlite3_stmt* stmt;
    int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr);
    if (rc != SQLITE_OK) {
        return results;
    }
    
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        UserRecord user;
        user.id = sqlite3_column_int(stmt, 0);
        user.username = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
        user.password_hash = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2));
        user.salt = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3));
        
        const char* token = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 4));
        user.token = token ? token : "";
        
        const char* status_str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 5));
        user.status = (status_str && std::string(status_str) == "online") 
                      ? UserStatus::ONLINE : UserStatus::OFFLINE;
        
        const char* role = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 6));
        user.role = role ? role : "";
        
        const char* email = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 7));
        user.email = email ? email : "";
        
        user.last_login = sqlite3_column_int64(stmt, 8);
        
        const void* blob = sqlite3_column_blob(stmt, 9);
        int blob_size = sqlite3_column_bytes(stmt, 9);
        if (blob && blob_size > 0) {
            const uint8_t* data = static_cast<const uint8_t*>(blob);
            user.avatar_pic.assign(data, data + blob_size);
        }
        
        results.push_back(user);
    }
    
    sqlite3_finalize(stmt);
    return results;
}

} // namespace scar

