#include "SceneSerializer.h"
#include <yaml-cpp/yaml.h>
#include "../../Entitys/Object.h"
#include "../../Renderer.h"
#include "../../utils/GameObjectsList.h"
#include "../../core/utils/LoadingWindow.h"
#include "../../core/utils/Popup.h"




#include <filesystem>
#include <openssl/sha.h>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <vector>
#include <memory>


bool VerifySceneHash(const YAML::Node &root)
{
    if (!root["scene_hash"] || !root["objects"])
        return false;

    YAML::Emitter sceneOnly;
    sceneOnly << root["objects"];

    std::string sceneString = sceneOnly.c_str();
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256(reinterpret_cast<const unsigned char *>(sceneString.c_str()), sceneString.size(), hash);

    std::ostringstream hashHex;
    for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i)
        hashHex << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);

    return hashHex.str() == root["scene_hash"].as<std::string>();
}

void SceneLoader::SaveScene(const std::string &path)
{
    YAML::Emitter out;
    YAML::Emitter sceneData;

    // Serialize object list only
    sceneData << YAML::BeginSeq;
    for (const auto &obj : objects)
        obj->Save(sceneData);
    sceneData << YAML::EndSeq;

    std::string objectData = sceneData.c_str();
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256(reinterpret_cast<const unsigned char *>(objectData.c_str()), objectData.size(), hash);

    std::ostringstream hashHex;
    for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i)
        hashHex << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);

    out << YAML::BeginMap;
    out << YAML::Key << "engine_version" << YAML::Value << g_engineConfig.version;
    out << YAML::Key << "scene_name" << YAML::Value << std::filesystem::path(path).stem().string();
    out << YAML::Key << "scene_hash" << YAML::Value << hashHex.str();
    out << YAML::Key << "format_version" << YAML::Value << 1;
    out << YAML::Key << "objects" << YAML::Value << YAML::Load(objectData);

    Logger::LogVerbose("[SaveScene] Saving Color Correction Attributes");

    out << YAML::Key << "color_correction" << YAML::BeginMap;
    out << YAML::Key << "brightness" << YAML::Value << Renderer::GetColorCorrection()->brightness;
    out << YAML::Key << "saturation" << YAML::Value << Renderer::GetColorCorrection()->saturation;
    out << YAML::Key << "gamma" << YAML::Value << Renderer::GetColorCorrection()->gamma;
    out << YAML::Key << "bloom" << YAML::Value << Renderer::GetColorCorrection()->bloom;
    out << YAML::Key << "intensity" << YAML::Value << Renderer::GetColorCorrection()->intensity;
    out << YAML::Key << "threshold" << YAML::Value << Renderer::GetColorCorrection()->threshold;
    out << YAML::EndMap;

    AssetManager::Save(out);

    out << YAML::EndMap;

    std::ofstream file(path);
    file << out.c_str();

    Logger::LogOk("Scene Saved");
}

void SceneLoader::LoadScene(const std::string &path)
{

    bool legacyLoad = false;
    Logger::LogDebug("Reading Scene File.");
    YAML::Node root = YAML::LoadFile(path);

    LoadingWindow loadingUI;
    loadingUI.Create("Loading Scene");

    std::string currentStep = "Verifying Scene";
    std::string currentDetail = "";
    loadingUI.Update(currentStep, currentDetail, 0.0f);

    if (!root["engine_version"] || !root["format_version"] || !root["scene_name"])
    {
        Logger::LogError("Missing required metadata!");
        loadingUI.Destroy();
        return;
    }

    std::string loadedVersion = root["engine_version"].as<std::string>();
    std::string expectedVersion = g_engineConfig.version;

    if (loadedVersion != expectedVersion)
    {
        Logger::LogWarning("Version mismatch! Expected %s, got %s",
                           expectedVersion.c_str(), loadedVersion.c_str());

        if (!g_engineConfig.settings.ignore_invalid_versions_popups)
        {
            loadingUI.Destroy(); // Close visual loading before blocking popup

            int result = Popup::Show("Version Mismatch",
                                     "Scene was created with engine version: " + loadedVersion +
                                         "\nExpected: " + expectedVersion +
                                         "\n\nLoad anyway?",
                                     {Popup::Option("Yes", 1),
                                      Popup::Option("Yes (Don't Ask Again)", 2),
                                      Popup::Option("No", 0)});

            if (result == 0)
            {
                Logger::LogInfo("User cancelled scene load.");
                return;
            }
            if (result == 2)
            {
                g_engineConfig.settings.ignore_invalid_versions_popups = true;
            }

            loadingUI.Create("Loading Scene (Legacy Recovery)");
            loadingUI.Update("Attempting Recovery", "Loading scene with fallback...", 0.02f);
        }
        else
        {
            Logger::LogInfo("Version mismatch ignored due to user setting.");
        }
    }

    if (!VerifySceneHash(root))
    {
        Logger::LogWarning("Scene hash does not match! File may be corrupted or tampered.");
    }

    if (root["Assets"])
    {
        currentStep = "Loading Assets";
        currentDetail = "Parsing asset data...";
        loadingUI.Update(currentStep, currentDetail, 0.05f);

        Logger::LogDebug("Loading Assets");
        AssetManager::Load(root["Assets"]);
    }

    Logger::LogDebug("Reseting Scene.");
    currentStep = "Clearing Previous Scene";
    currentDetail = "Removing existing objects";
    loadingUI.Update(currentStep, currentDetail, 0.1f);

    objects.clear();
    Object::usedIDs.clear();

    Logger::LogDebug("Recreating Objects");
    currentStep = "Creating Scene Objects";

    const auto &objectArray = root["objects"];
    int objectIndex = 0;
    int totalObjects = static_cast<int>(objectArray.size());

    for (const auto &node : objectArray)
    {
        std::string objName = node["name"] ? node["name"].as<std::string>() : ("Unnamed_" + std::to_string(objectIndex));
        currentDetail = "Loading: " + objName;
        float progress = 0.1f + (0.75f * (objectIndex / static_cast<float>(std::max(1, totalObjects))));
        loadingUI.Update(currentStep, currentDetail, progress);

        try
        {
            auto obj = std::make_shared<Object>(objName);
            obj->Load(node);
            objects.push_back(obj);
        }
        catch (const std::exception &e)
        {
            Logger::LogWarning("Failed to load object '%s': %s", objName.c_str(), e.what());
        }

        objectIndex++;
    }

    // Color correction settings
    if (root["color_correction"])
    {
        Logger::LogVerbose("Loading Color Correction Attributes");
        currentStep = "Color Correction";
        currentDetail = "Applying settings...";
        loadingUI.Update(currentStep, currentDetail, 0.9f);

        ColorCorrection *cc = Renderer::GetColorCorrection();
        const auto &data = root["color_correction"];
        if (cc)
        {
            if (data["brightness"])
                cc->brightness = data["brightness"].as<float>();
            if (data["saturation"])
                cc->saturation = data["saturation"].as<float>();
            if (data["gamma"])
                cc->gamma = data["gamma"].as<float>();
            if (data["bloom"])
                cc->bloom = data["bloom"].as<bool>();
            if (data["intensity"])
                cc->intensity = data["intensity"].as<float>();
            if (data["threshold"])
                cc->threshold = data["threshold"].as<float>();
        }
    }

    // Finalize
    currentStep = "Done";
    currentDetail = "Finalizing load...";
    loadingUI.Update(currentStep, currentDetail, 1.0f);

    Logger::LogOk("Scene Loaded: %s", root["scene_name"].as<std::string>().c_str());

    loadingUI.Destroy();
}