#include "Config.h"

#include <filesystem>
#include <fstream>

#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h>

using json = nlohmann::json;

static constexpr const char* kPath = "config/overlay.json";

// -------------------------------------------------------------------------
// Load
// -------------------------------------------------------------------------

OverlayConfig OverlayConfig::Load(const std::string& path) {
    OverlayConfig cfg;

    std::ifstream f(path);
    if (!f.is_open()) {
        spdlog::info("Config: {} not found, using defaults", path);
        return cfg;
    }

    try {
        json j = json::parse(f);

        // ESP toggles
        if (j.contains("showPlayers"))  cfg.showPlayers  = j["showPlayers"].get<bool>();
        if (j.contains("showAnimals"))  cfg.showAnimals  = j["showAnimals"].get<bool>();
        if (j.contains("showZombies"))  cfg.showZombies  = j["showZombies"].get<bool>();
        if (j.contains("showItems"))    cfg.showItems    = j["showItems"].get<bool>();
        if (j.contains("showBox"))      cfg.showBox      = j["showBox"].get<bool>();
        if (j.contains("showSkeleton")) cfg.showSkeleton = j["showSkeleton"].get<bool>();
        if (j.contains("showHeadDot"))      cfg.showHeadDot      = j["showHeadDot"].get<bool>();
        if (j.contains("showCorpses"))      cfg.showCorpses      = j["showCorpses"].get<bool>();
        if (j.contains("showWeapon"))       cfg.showWeapon       = j["showWeapon"].get<bool>();
        if (j.contains("showHealthBar"))    cfg.showHealthBar    = j["showHealthBar"].get<bool>();
        if (j.contains("showHealthNumber")) cfg.showHealthNumber = j["showHealthNumber"].get<bool>();

        // Distances
        if (j.contains("playerMaxDist")) cfg.playerMaxDist = j["playerMaxDist"].get<float>();
        if (j.contains("animalMaxDist")) cfg.animalMaxDist = j["animalMaxDist"].get<float>();
        if (j.contains("zombieMaxDist")) cfg.zombieMaxDist = j["zombieMaxDist"].get<float>();
        if (j.contains("itemMaxDist"))   cfg.itemMaxDist   = j["itemMaxDist"].get<float>();

        // Per-category item toggles
        if (j.contains("itemCategories") && j["itemCategories"].is_object()) {
            for (auto& [key, val] : j["itemCategories"].items()) {
                if (val.is_boolean()) cfg.itemCategories[key] = val.get<bool>();
            }
        }

        // Resolution override
        if (j.contains("overlayWidth"))  cfg.overlayWidth  = j["overlayWidth"].get<int>();
        if (j.contains("overlayHeight")) cfg.overlayHeight = j["overlayHeight"].get<int>();

        // Stretched resolution
        if (j.contains("renderWidth"))   cfg.renderWidth   = j["renderWidth"].get<int>();
        if (j.contains("renderHeight"))  cfg.renderHeight  = j["renderHeight"].get<int>();
        if (j.contains("stretchToFill")) cfg.stretchToFill = j["stretchToFill"].get<bool>();

        // Web radar
        if (j.contains("webBindAddress")) cfg.webBindAddress = j["webBindAddress"].get<std::string>();
        if (j.contains("webPort"))        cfg.webPort        = j["webPort"].get<int>();
        if (j.contains("webPassword"))    cfg.webPassword    = j["webPassword"].get<std::string>();

        spdlog::info("Config: loaded from {}", path);
    } catch (const std::exception& ex) {
        spdlog::warn("Config: parse error in {} — {} — using defaults", path, ex.what());
    }

    return cfg;
}

// -------------------------------------------------------------------------
// Save
// -------------------------------------------------------------------------

void OverlayConfig::Save(const std::string& path) const {
    try {
        std::filesystem::create_directories(
            std::filesystem::path(path).parent_path());

        json j;

        // ESP toggles
        j["showPlayers"]  = showPlayers;
        j["showAnimals"]  = showAnimals;
        j["showZombies"]  = showZombies;
        j["showItems"]    = showItems;
        j["showBox"]      = showBox;
        j["showSkeleton"] = showSkeleton;
        j["showHeadDot"]      = showHeadDot;
        j["showCorpses"]      = showCorpses;
        j["showWeapon"]       = showWeapon;
        j["showHealthBar"]    = showHealthBar;
        j["showHealthNumber"] = showHealthNumber;

        // Distances
        j["playerMaxDist"] = playerMaxDist;
        j["animalMaxDist"] = animalMaxDist;
        j["zombieMaxDist"] = zombieMaxDist;
        j["itemMaxDist"]   = itemMaxDist;

        // Per-category item toggles
        json cats = json::object();
        for (const auto& [key, enabled] : itemCategories)
            cats[key] = enabled;
        j["itemCategories"] = cats;

        // Resolution override
        j["overlayWidth"]  = overlayWidth;
        j["overlayHeight"] = overlayHeight;

        // Stretched resolution
        j["renderWidth"]   = renderWidth;
        j["renderHeight"]  = renderHeight;
        j["stretchToFill"] = stretchToFill;

        // Web radar
        j["webBindAddress"] = webBindAddress;
        j["webPort"]        = webPort;
        j["webPassword"]    = webPassword;

        std::ofstream f(path);
        f << j.dump(2);
        spdlog::debug("Config: saved to {}", path);
    } catch (const std::exception& ex) {
        spdlog::warn("Config: failed to save {} — {}", path, ex.what());
    }
}
