#include "engine_vault.h"

#include <array>
#include <memory>
#include <fstream>
#include <iostream>
#include <dirent.h>
#include <unistd.h>
#include <cstring>
#include <openssl/crypto.h>

namespace node669427 {

Vault::Vault(const std::string& vault_path, const std::string& key_path)
    : vault_path_(vault_path), key_path_(key_path) {}

Vault::~Vault() {
    std::lock_guard<std::mutex> lock(cache_mutex_);
    for (auto& [k, v] : cache_) {
        OPENSSL_cleanse(&v[0], v.size());
    }
    cache_.clear();
}

bool Vault::initialize() {
    if (access(vault_path_.c_str(), R_OK) != 0) {
        std::cerr << "[Vault] Vault path not accessible: " << vault_path_ << std::endl;
        return false;
    }
    if (access(key_path_.c_str(), R_OK) != 0) {
        std::cerr << "[Vault] Key file not accessible: " << key_path_ << std::endl;
        return false;
    }
    scanAndDecrypt();
    return !cache_.empty();
}

std::string Vault::decryptFile(const std::string& file_path) const {
    std::string cmd = "openssl enc -aes-256-cbc -d -salt -pbkdf2 -iter 100000"
                      " -in \"" + file_path + "\""
                      " -pass file:" + key_path_ + " 2>/dev/null";
    std::array<char, 4096> buffer{};
    std::string result;
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
    if (!pipe) return "";
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        result += buffer.data();
    }
    if (!result.empty() && result.back() == '\n') result.pop_back();
    return result;
}

void Vault::scanAndDecrypt() {
    std::lock_guard<std::mutex> lock(cache_mutex_);
    DIR* dir = opendir(vault_path_.c_str());
    if (!dir) return;
    struct dirent* entry;
    while ((entry = readdir(dir)) != nullptr) {
        std::string name = entry->d_name;
        if (name == "." || name == "..") continue;
        if (name.size() < 6 || name.substr(name.size() - 5) != ".vault") continue;
        
        // Command Injection Protection: Ensure filename is strictly alphanumeric + underscores/hyphens
        bool safe = true;
        for (char c : name) {
            if (!std::isalnum(c) && c != '_' && c != '-' && c != '.') {
                safe = false;
                break;
            }
        }
        if (!safe) {
            std::cerr << "[Vault] Skipping unsafe vault filename: " << name << std::endl;
            continue;
        }

        std::string file_path = vault_path_ + "/" + name;
        std::string decrypted = decryptFile(file_path);
        if (!decrypted.empty()) {
            std::string key = name.substr(0, name.size() - 5);
            cache_[key] = decrypted;
        }
    }
    closedir(dir);
}

std::string Vault::get(const std::string& key) const {
    std::lock_guard<std::mutex> lock(cache_mutex_);
    auto it = cache_.find(key);
    return (it != cache_.end()) ? it->second : "";
}

std::string Vault::getOrDefault(const std::string& key, const std::string& default_val) const {
    std::string val = get(key);
    return val.empty() ? default_val : val;
}

bool Vault::has(const std::string& key) const {
    std::lock_guard<std::mutex> lock(cache_mutex_);
    return cache_.find(key) != cache_.end();
}

void Vault::refresh() {
    scanAndDecrypt();
}

size_t Vault::size() const {
    std::lock_guard<std::mutex> lock(cache_mutex_);
    return cache_.size();
}

} // namespace node669427
