#include "db_manager.h"
#include "../shared/crypto/argon2_wrapper.h"
#include <iostream>
#include <fstream>
#include <filesystem>
#include <iomanip>
#include <ctime>

namespace scar {

DBManager::DBManager(const std::string& db_path) {
    db_ = std::make_unique<Database>(db_path);
    if (!db_->initialize()) {
        throw std::runtime_error("Failed to initialize database: " + db_path);
    }
}

bool DBManager::addUser(const std::string& username, const std::string& password,
                       const std::string& avatar) {
    // Check if user already exists
    auto existing_user = db_->getUserByUsername(username);
    if (existing_user) {
        std::cerr << "Error: User '" << username << "' already exists" << std::endl;
        return false;
    }
    
    // Generate random salt
    std::string salt = Argon2Wrapper::generateSalt();
    
    // Hash password with salt
    std::string password_hash = Argon2Wrapper::hashPassword(password, salt);
    
    // Create user
    if (!db_->createUser(username, password_hash, salt)) {
        std::cerr << "Error: Failed to create user" << std::endl;
        return false;
    }
    
    // Set avatar if provided
    if (!avatar.empty()) {
        modifyAvatar(username, avatar);
    }
    
    std::cout << "User '" << username << "' created successfully" << std::endl;
    std::cout << "  Salt: " << salt << std::endl;
    std::cout << "  Hash: " << password_hash.substr(0, 32) << "..." << std::endl;
    
    return true;
}

bool DBManager::deleteUser(const std::string& username) {
    auto user = db_->getUserByUsername(username);
    if (!user) {
        std::cerr << "Error: User '" << username << "' not found" << std::endl;
        return false;
    }
    
    // Execute delete
    if (!db_->deleteUser(username)) {
        std::cerr << "Error: Failed to delete user" << std::endl;
        return false;
    }
    
    std::cout << "User '" << username << "' deleted successfully" << std::endl;
    return true;
}

bool DBManager::modifyPassword(const std::string& username, const std::string& new_password) {
    auto user = db_->getUserByUsername(username);
    if (!user) {
        std::cerr << "Error: User '" << username << "' not found" << std::endl;
        return false;
    }
    
    // Generate new salt
    std::string salt = Argon2Wrapper::generateSalt();
    
    // Hash new password
    std::string password_hash = Argon2Wrapper::hashPassword(new_password, salt);
    
    // Update database
    if (!db_->updateUserPassword(username, password_hash, salt)) {
        std::cerr << "Error: Failed to update password" << std::endl;
        return false;
    }
    
    std::cout << "Password updated for user '" << username << "'" << std::endl;
    return true;
}

bool DBManager::modifyAvatar(const std::string& username, const std::string& avatar_path) {
    auto user = db_->getUserByUsername(username);
    if (!user) {
        std::cerr << "Error: User '" << username << "' not found" << std::endl;
        return false;
    }
    
    // Read avatar file
    auto avatar_data = readAvatarFile(avatar_path);
    if (avatar_data.empty()) {
        std::cerr << "Error: Failed to read avatar file" << std::endl;
        return false;
    }
    
    // Update database
    if (!db_->updateUserAvatar(username, avatar_data)) {
        std::cerr << "Error: Failed to update avatar" << std::endl;
        return false;
    }
    
    std::cout << "Avatar updated for user '" << username << "' (" 
              << avatar_data.size() << " bytes)" << std::endl;
    return true;
}

bool DBManager::modifyEmail(const std::string& username, const std::string& email) {
    auto user = db_->getUserByUsername(username);
    if (!user) {
        std::cerr << "Error: User '" << username << "' not found" << std::endl;
        return false;
    }
    
    if (!db_->updateUserEmail(username, email)) {
        std::cerr << "Error: Failed to update email" << std::endl;
        return false;
    }
    
    std::cout << "Email updated for user '" << username << "'" << std::endl;
    return true;
}

bool DBManager::modifyRole(const std::string& username, const std::string& role) {
    auto user = db_->getUserByUsername(username);
    if (!user) {
        std::cerr << "Error: User '" << username << "' not found" << std::endl;
        return false;
    }
    
    if (!db_->updateUserRole(username, role)) {
        std::cerr << "Error: Failed to update role" << std::endl;
        return false;
    }
    
    std::cout << "Role updated for user '" << username << "'" << std::endl;
    return true;
}

void DBManager::fetchUser(const std::string& username) {
    auto user = db_->getUserByUsername(username);
    if (!user) {
        std::cerr << "User '" << username << "' not found" << std::endl;
        return;
    }
    
    printUserDetails(*user);
}

void DBManager::searchUsers(const std::string& field, const std::string& value) {
    auto users = db_->searchUsers(field, value);
    
    if (users.empty()) {
        std::cout << "No users found matching " << field << "='" << value << "'" << std::endl;
        return;
    }
    
    std::cout << "Found " << users.size() << " user(s):" << std::endl;
    std::cout << std::string(80, '-') << std::endl;
    
    for (const auto& user : users) {
        printUserDetails(user);
        std::cout << std::string(80, '-') << std::endl;
    }
}

void DBManager::listAllUsers() {
    auto users = db_->getAllUsers();
    
    if (users.empty()) {
        std::cout << "No users in database" << std::endl;
        return;
    }
    
    std::cout << "Total users: " << users.size() << std::endl;
    std::cout << std::string(100, '=') << std::endl;
    
    // Table header
    std::cout << std::left
              << std::setw(20) << "Username"
              << std::setw(10) << "Status"
              << std::setw(25) << "Email"
              << std::setw(15) << "Role"
              << std::setw(20) << "Last Login"
              << std::endl;
    std::cout << std::string(100, '-') << std::endl;
    
    for (const auto& user : users) {
        std::string status = (user.status == UserStatus::ONLINE) ? "Online" : "Offline";
        
        std::string last_login = "Never";
        if (user.last_login > 0) {
            std::time_t t = static_cast<std::time_t>(user.last_login);
            std::tm* tm = std::localtime(&t);
            char buffer[32];
            std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm);
            last_login = buffer;
        }
        
        std::cout << std::left
                  << std::setw(20) << user.username
                  << std::setw(10) << status
                  << std::setw(25) << user.email
                  << std::setw(15) << user.role
                  << std::setw(20) << last_login
                  << std::endl;
    }
}

std::string DBManager::findDatabase(const std::string& override_path) {
    // If path provided, use it
    if (!override_path.empty()) {
        if (std::filesystem::exists(override_path)) {
            return override_path;
        }
        std::cerr << "Warning: Specified database not found: " << override_path << std::endl;
    }
    
    // Check current directory
    if (std::filesystem::exists("scarchat.db")) {
        return "scarchat.db";
    }
    
    // Check install path (would be set by CMake install)
    std::string install_path = "/usr/local/share/scarchat/scarchat.db";
    if (std::filesystem::exists(install_path)) {
        return install_path;
    }
    
    // Check user's home directory
    const char* home = std::getenv("HOME");
    if (home) {
        std::string home_path = std::string(home) + "/.local/share/scarchat/scarchat.db";
        if (std::filesystem::exists(home_path)) {
            return home_path;
        }
    }
    
    // Default to current directory (will be created if doesn't exist)
    return "scarchat.db";
}

void DBManager::printUserDetails(const UserRecord& user) {
    std::cout << "User ID: " << user.id << std::endl;
    std::cout << "Username: " << user.username << std::endl;
    std::cout << "Status: " << (user.status == UserStatus::ONLINE ? "Online" : "Offline") << std::endl;
    std::cout << "Email: " << (user.email.empty() ? "(not set)" : user.email) << std::endl;
    std::cout << "Role: " << (user.role.empty() ? "(not set)" : user.role) << std::endl;
    
    if (user.last_login > 0) {
        std::time_t t = static_cast<std::time_t>(user.last_login);
        std::cout << "Last Login: " << std::ctime(&t);
    } else {
        std::cout << "Last Login: Never" << std::endl;
    }
    
    std::cout << "Has Avatar: " << (user.avatar_pic.empty() ? "No" : "Yes") << std::endl;
    std::cout << "Token: " << (user.token.empty() ? "(none)" : user.token.substr(0, 20) + "...") << std::endl;
    std::cout << "Salt: " << user.salt << std::endl;
    std::cout << "Password Hash: " << user.password_hash.substr(0, 32) << "..." << std::endl;
}

std::vector<uint8_t> DBManager::readAvatarFile(const std::string& path) {
    std::ifstream file(path, std::ios::binary | std::ios::ate);
    if (!file.is_open()) {
        return {};
    }
    
    std::streamsize size = file.tellg();
    file.seekg(0, std::ios::beg);
    
    std::vector<uint8_t> buffer(size);
    if (!file.read(reinterpret_cast<char*>(buffer.data()), size)) {
        return {};
    }
    
    return buffer;
}

} // namespace scar
