#include "core/jail/jfile.hpp"

#include <sys/stat.h>
#include <unistd.h>

#include <algorithm>
#include <filesystem>
#include <fstream>
#include <string>
#include <vector>

#ifndef _WIN32
#include <pwd.h>
#endif

namespace {

    struct Keyvalue {
        std::string key;
        std::string value;
    };

    std::vector<Keyvalue> config;
    std::string resource_folder;
    std::string config_folder;

    void loadConfigValues() {
        config.clear();
        const std::string CONFIG_FILE = config_folder + "/config.txt";
        std::ifstream fi(CONFIG_FILE);
        if (!fi.is_open()) {
            return;
        }

        std::string line;
        while (std::getline(fi, line)) {
            const auto EQ = line.find('=');
            if (EQ == std::string::npos) {
                continue;
            }
            config.push_back({line.substr(0, EQ), line.substr(EQ + 1)});
        }
    }

    void saveConfigValues() {
        const std::string CONFIG_FILE = config_folder + "/config.txt";
        std::ofstream fo(CONFIG_FILE);
        if (!fo.is_open()) {
            return;
        }
        for (const auto& pair : config) {
            fo << pair.key << '=' << pair.value << '\n';
        }
    }

}  // namespace

void Jf::setResourceFolder(const char* str) {
    resource_folder = str;
}

auto Jf::getResourceFolder() -> const char* {
    return resource_folder.c_str();
}

// Crea la carpeta del sistema on guardar les dades.
// Accepta rutes amb subdirectoris (ex: "jailgames/aee") i crea tota la jerarquia.
void Jf::setConfigFolder(const char* foldername) {
#ifdef _WIN32
    const char* base = getenv("APPDATA");
    if (!base) base = "C:/";
    config_folder = std::string(base) + "/" + foldername;
#elif __APPLE__
    struct passwd* pw = getpwuid(getuid());
    const char* homedir = (pw && pw->pw_dir) ? pw->pw_dir : nullptr;
    if (!homedir) homedir = getenv("HOME");
    if (!homedir) homedir = "/tmp";
    config_folder = std::string(homedir) + "/Library/Application Support/" + foldername;
#elif __linux__
    // Nota emscripten: `__linux__` també està definit, però `getpwuid` pot
    // retornar nullptr (sense /etc/passwd al MEMFS) o un passwd amb pw_dir
    // buit. Amb els fallbacks HOME → /tmp evitem crashejar al primer
    // arranque dins del navegador. La config no persistirà entre recàrregues
    // (MEMFS és volàtil); caldria IDBFS si volguéssem persistència a web.
    struct passwd* pw = getpwuid(getuid());
    const char* homedir = ((pw != nullptr) && (pw->pw_dir != nullptr) && (pw->pw_dir[0] != 0)) ? pw->pw_dir : nullptr;
    if ((homedir == nullptr) || (homedir[0] == 0)) {
        homedir = getenv("HOME");
    }
    if ((homedir == nullptr) || (homedir[0] == 0)) {
        homedir = "/tmp";
    }
    config_folder = std::string(homedir) + "/.config/" + foldername;
#else
    config_folder = std::string("/tmp/jailgames_config/") + foldername;
#endif

    std::error_code ec;
    std::filesystem::create_directories(config_folder, ec);
    // A emscripten/MEMFS create_directories pot fallar (p.ex. parent
    // read-only o libc++ amb path empty-check estricte). La config és
    // volàtil al navegador de totes formes: ignorem l'error i continuem.
}

auto Jf::getConfigFolder() -> const char* {
    thread_local std::string folder_;
    folder_ = config_folder + "/";
    return folder_.c_str();
}

auto Jf::getConfigValue(const char* key) -> const char* {
    if (config.empty()) {
        loadConfigValues();
    }
    const auto IT = std::ranges::find_if(config, [key](const Keyvalue& pair) { return pair.key == key; });
    if (IT != config.end()) {
        thread_local std::string value_cache_;
        value_cache_ = IT->value;
        return value_cache_.c_str();
    }
    return nullptr;
}

void Jf::setConfigValue(const char* key, const char* value) {
    if (config.empty()) {
        loadConfigValues();
    }
    const auto IT = std::ranges::find_if(config, [key](const Keyvalue& pair) { return pair.key == key; });
    if (IT != config.end()) {
        IT->value = value;
        saveConfigValues();
        return;
    }
    config.push_back({std::string(key), std::string(value)});
    saveConfigValues();
}
