#include "core/system/system_utils.hpp"

#include <sys/stat.h>  // Para stat, mkdir, S_ISDIR

#include <cerrno>   // Para EACCES, EEXIST, ENAMETOOLONG, errno
#include <cstdlib>  // Para getenv, size_t

#ifdef _WIN32
#include <direct.h>
#include <shlobj.h>
#include <windows.h>
// Evitar conflictos con macros de Windows
#ifdef ERROR_ALREADY_EXISTS
#undef ERROR_ALREADY_EXISTS
#endif
#else
#include <pwd.h>     // Para getpwuid, passwd
#include <unistd.h>  // Para getuid
#endif

namespace SystemUtils {

    // Función auxiliar para crear una carpeta individual
    auto createSingleFolder(const std::string& path, int permissions) -> Result {
        struct stat st = {.st_dev = 0};

        // Verificar si ya existe
        if (stat(path.c_str(), &st) == 0) {
            return Result::SUCCESS;  // Ya existe, no es error por defecto
        }

        // Intentar crear la carpeta
        int result;
#ifdef _WIN32
        result = _mkdir(path.c_str());
#else
        result = mkdir(path.c_str(), permissions);
#endif

        if (result == -1) {
            switch (errno) {
                case EACCES:
                    return Result::PERMISSION_DENIED;
                case EEXIST:
                    return Result::ALREADY_EXISTS;
                case ENAMETOOLONG:
                    return Result::PATH_TOO_LONG;
                default:
                    return Result::UNKNOWN_ERROR;
            }
        }

        return Result::SUCCESS;
    }

    // Función auxiliar para crear carpetas padre recursivamente
    auto createParentFolders(const std::string& path, int permissions) -> Result {
        size_t pos = 0;

        while ((pos = path.find('/', pos + 1)) != std::string::npos) {
            std::string parent = path.substr(0, pos);
            if (!parent.empty() && !folderExists(parent)) {
                Result result = createSingleFolder(parent, permissions);
                if (result != Result::SUCCESS && result != Result::ALREADY_EXISTS) {
                    return result;
                }
            }
        }

        return Result::SUCCESS;
    }

    auto createApplicationFolder(const std::string& app_name, std::string& out_path) -> Result {
        FolderConfig config;
        return createApplicationFolder(app_name, out_path, config);
    }

    auto createApplicationFolder(const std::string& app_name, std::string& out_path, const FolderConfig& config) -> Result {
        out_path = getApplicationDataPath(app_name);
        return createFolder(out_path, config);
    }

    auto createFolder(const std::string& path) -> Result {
        FolderConfig config;
        return createFolder(path, config);
    }

    auto createFolder(const std::string& path, const FolderConfig& config) -> Result {
        if (path.empty()) {
            return Result::INVALID_PATH;
        }

        // Verificar si ya existe y si eso es un error
        if (folderExists(path) && config.fail_if_exists) {
            return Result::ALREADY_EXISTS;
        }

        // Crear carpetas padre si es necesario
        if (config.create_parents) {
            Result parent_result = createParentFolders(path, config.permissions);
            if (parent_result != Result::SUCCESS) {
                return parent_result;
            }
        }

        // Crear la carpeta final
        return createSingleFolder(path, config.permissions);
    }

    auto getApplicationDataPath(const std::string& app_name) -> std::string {
#ifdef _WIN32
        char* appdata = getenv("APPDATA");
        if (appdata) {
            return std::string(appdata) + "/" + app_name;
        }
        return "C:/Users/Default/AppData/Roaming/" + app_name;

#elif __APPLE__
        std::string home = getHomeDirectory();
        return home + "/Library/Application Support/" + app_name;

#elif __linux__
        std::string home = getHomeDirectory();
        return home + "/.config/" + app_name;

#else
        // Fallback genérico
        std::string home = getHomeDirectory();
        return home + "/." + app_name;
#endif
    }

    auto folderExists(const std::string& path) -> bool {
        struct stat st = {.st_dev = 0};
        return (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode));
    }

    auto resultToString(Result result) -> const char* {
        switch (result) {
            case Result::SUCCESS:
                return "Operación exitosa";
            case Result::PERMISSION_DENIED:
                return "Error: Permisos insuficientes";
            case Result::PATH_TOO_LONG:
                return "Error: Ruta demasiado larga";
            case Result::ALREADY_EXISTS:
                return "Error: La carpeta ya existe";
            case Result::INVALID_PATH:
                return "Error: Ruta inválida";
            case Result::UNKNOWN_ERROR:
                return "Error desconocido";
            default:
                return "Error no identificado";
        }
    }

    auto getHomeDirectory() -> std::string {
#ifdef _WIN32
        char* userprofile = getenv("USERPROFILE");
        if (userprofile) {
            return std::string(userprofile);
        }
        return "C:/Users/Default";
#else
        struct passwd* pw = getpwuid(getuid());
        if ((pw != nullptr) && (pw->pw_dir != nullptr)) {
            return {pw->pw_dir};
        }

        // Fallback
        char* home = getenv("HOME");
        if (home != nullptr) {
            return {home};
        }
        return "/tmp";
#endif
    }

    auto getTempDirectory() -> std::string {
#ifdef _WIN32
        char* temp = getenv("TEMP");
        if (temp) {
            return std::string(temp);
        }
        return "C:/Windows/Temp";
#else
        return "/tmp";
#endif
    }

}  // namespace SystemUtils