// ProjectManager.cpp

#include "ProjectManager.h"
#include "../../core/utils/EngineConfig.h"
#include "../../core/utils/Logging.h"
#include "../../Engine.h"
#include "../../core/utils/FileDialog.h"
#include "SceneSerializer.h"

#include <yaml-cpp/yaml.h>
#include <filesystem>
#include <fstream>
#include <chrono>
#include <imgui.h>
#include <string>

namespace fs = std::filesystem;

std::string ProjectManager::s_currentProjectPath;
std::string ProjectManager::s_currentProjectName;
std::string ProjectManager::s_defaultScene;




// Call this to open the popup:
//   ImGui::OpenPopup("Create New Project");
//   ShowCreateProjectPopup(&open);
void ProjectManager::ShowCreateProjectPopup(bool* pOpen)
{
    // Keep static buffers across frames
    static char nameBuf[128] = "";
    static char pathBuf[512] = "";

    // Center-popup and set a decent default size
    ImGui::SetNextWindowSize({500, 0}, ImGuiCond_Appearing);
    if (!ImGui::BeginPopupModal("Create New Project", pOpen, ImGuiWindowFlags_NoResize))
        return;

    // Title
    ImGui::TextUnformatted("Enter details for your new project:");
    ImGui::Separator();
    ImGui::Spacing();

    // Project Name field
    ImGui::InputText("##proj_name", nameBuf, sizeof(nameBuf));
    ImGui::SameLine();
    ImGui::Text("Project Name");

    // Project Path field with Browse button
    ImGui::PushItemWidth(-100); // make path input use remaining width
    ImGui::InputText("##proj_path", pathBuf, sizeof(pathBuf));
    ImGui::PopItemWidth();
    ImGui::SameLine();
    if (ImGui::Button("Browse...", {80, 0}))
    {
        std::string chosen = CreateFileDialog(FileDialogType::Project);
        if (!chosen.empty())
            strncpy(pathBuf, chosen.c_str(), sizeof(pathBuf));
    }
    ImGui::SameLine();
    ImGui::Text("Project Folder");

    ImGui::Spacing();

    // Compute and show the final .cproj path preview
    std::filesystem::path preview = std::filesystem::path(pathBuf) / (std::string(nameBuf) + ".cproj");
    ImGui::Text("Project file will be created at:");
    ImGui::TextColored(ImVec4(0.4f, 0.7f, 1.0f, 1.0f), "%s", preview.string().c_str());

    ImGui::Spacing();
    ImGui::Separator();

    // Buttons: Cancel and Create
    float width = ImGui::GetContentRegionAvail().x;
    ImGui::Dummy({0,0}); // for a little vertical padding
    if (ImGui::Button("Cancel", {width * 0.45f, 0}))
    {
        ImGui::CloseCurrentPopup();
    }
    ImGui::SameLine();
    if (ImGui::Button("Create Project", {width * 0.45f, 0}))
    {
        CreateProject(nameBuf, pathBuf);
        ImGui::CloseCurrentPopup();

        // Clear buffers for next time
        nameBuf[0] = '\0';
        pathBuf[0] = '\0';
    }

    ImGui::EndPopup();
}




bool CreateDirectories(const fs::path &baseDir)
{
    static const std::vector<std::string> subdirs = {
        "Prefabs",
        "Scenes",
        "Images",
        "Sounds",
        "Fomts",
        "Shaders",
        "Scripts",
        "Videos",

    };

    std::error_code ec;
    bool allOk = true;
    fs::path assetsRoot = baseDir / "Assets";

    for (const auto &name : subdirs)
    {
        Logger::LogVerbose("Creating Folder: '%s'", name.c_str());
        fs::create_directories(assetsRoot / name, ec);
        if (ec)
        {
            Logger::LogError("Could not ensure %s dir: %s",
                             name.c_str(),
                             ec.message().c_str());
            allOk = false;
        }
    }

    return allOk;
}

std::string ProjectManager::ResolveResPath(const std::string &resPath)
{
    constexpr const char *prefix = "res://";
    if (resPath.rfind(prefix, 0) != 0)
        return resPath;

    if (s_currentProjectPath.empty() || s_currentProjectName.empty())
    {
        Logger::LogError("Cannot resolve res path: 'project not loaded!'");
        return resPath;
    }

    fs::path baseDir = fs::path(s_currentProjectPath) / s_currentProjectName;
    std::string relativePart = resPath.substr(strlen(prefix));
    fs::path fullPath = baseDir / relativePart;

    return fullPath.lexically_normal().string();
}

bool ProjectManager::Init()
{
    s_currentProjectPath.clear();
    s_currentProjectName.clear();
    s_defaultScene.clear();
    Logger::LogOk("Project Core");
    return true;
}

bool ProjectManager::LoadProject(const std::string &projectPath,
                                 const std::string &projectName)
{
    fs::path baseDir = fs::path(projectPath) / projectName;
    fs::path projFile = baseDir / (projectName + ".cproj");

    if (!fs::exists(projFile))
    {
        Logger::LogInfo("Project '%s' not found at '%s' creating new one.",
                        projectName.c_str(), projectPath.c_str());
        return SaveProject(projectPath, projectName);
    }

    YAML::Node config;
    try
    {
        config = YAML::LoadFile(projFile.string());
    }
    catch (const YAML::Exception &e)
    {
        Logger::LogError("Failed to parse project file '%s': %s",
                         projFile.string().c_str(), e.what());
        return false;
    }

    std::string savedName = config["Name"].as<std::string>(projectName);
    std::string createdDate = config["CreatedDate"].as<std::string>("unknown");
    std::string s_defaultScene = config["s_defaultScene"].as<std::string>("s_defaultScene");

    Logger::LogInfo("Loading Project: '%s", savedName.c_str());

    s_currentProjectPath = projectPath;
    s_currentProjectName = savedName;

    std::string resScenePath = config["s_defaultScene"].as<std::string>("");

    if (!resScenePath.empty())
    {
        s_defaultScene = ResolveResPath(resScenePath);
        Logger::LogDebug("Loading Scene: %s", s_defaultScene.c_str());

        if (!std::filesystem::exists(s_defaultScene))
        {
            Logger::LogError("Scene file does not exist: %s", s_defaultScene.c_str());
            return false;
        }

        SceneLoader::LoadScene(s_defaultScene);
    }

    Logger::LogInfo("Loaded project '%s'", savedName.c_str());

    CreateDirectories(baseDir);

    return true;
}

bool ProjectManager::CreateProject(const std::string &projectPath,
                                   const std::string &projectName)
{
    fs::path baseDir = fs::path(projectPath) / projectName;
    fs::path projFile = baseDir / (projectName + ".cproj");

    std::error_code ec;
    if (!fs::create_directories(baseDir, ec) && ec)
    {
        Logger::LogError("Failed to create project directory '%s': %s",
                         projectPath.c_str(), ec.message().c_str());
        return false;
    }

    // Set current project path before scene creation
    s_currentProjectPath = baseDir.string();
    s_currentProjectName = projectName;

    // Create initial scene
    fs::path scenePath = baseDir / "Assets" / "Scenes" / "main.cene";

    SceneLoader::SaveScene(scenePath.string());

    s_defaultScene = scenePath.string();

    // Write .cproj YAML
    YAML::Emitter out;
    out << YAML::BeginMap;
    out << YAML::Key << "Name" << YAML::Value << projectName;
    out << YAML::Key << "EngineVersion" << YAML::Value << g_engineConfig.version;
    fs::path relPath = fs::relative(scenePath, baseDir);
    out << YAML::Key << "s_defaultScene" << YAML::Value << ("res://" + relPath.generic_string());

    auto now = std::chrono::system_clock::now();
    auto t = std::chrono::system_clock::to_time_t(now);
    std::tm tm{};
#if defined(_WIN32)
    gmtime_s(&tm, &t);
#else
    gmtime_r(&t, &tm);
#endif
    char buf[32];
    std::strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &tm);
    out << YAML::Key << "CreatedDate" << YAML::Value << buf;
    out << YAML::EndMap;

    std::ofstream fout(projFile);
    if (!fout.is_open())
    {
        Logger::LogError("Could not open project file for writing: %s",
                         projFile.string().c_str());
        return false;
    }
    fout << out.c_str();
    fout.close();

    Logger::LogOk("Created project '%s' at '%s'",
                  projectName.c_str(), projectPath.c_str());

    CreateDirectories(baseDir);

    SceneLoader::LoadScene(scenePath.string());

    return true;
}


bool ProjectManager::SaveProject(const std::string &projectPath,
                                 const std::string &projectName)
{
    fs::path baseDir = fs::path(projectPath) / projectName;
    fs::path projFile = baseDir / (projectName + ".cproj");

    std::error_code ec;
    if (!fs::create_directories(baseDir, ec) && ec)
    {
        Logger::LogError("Failed to create project directory '%s': %s",
                         projectPath.c_str(), ec.message().c_str());
        return false;
    }



    YAML::Emitter out;
    out << YAML::BeginMap;
    out << YAML::Key << "Name" << YAML::Value << projectName;
    out << YAML::Key << "EngineVersion" << YAML::Value << g_engineConfig.version;
    fs::path relPath = fs::relative(s_defaultScene,
                                    fs::path(s_currentProjectPath) / s_currentProjectName);
    out << YAML::Key << "s_defaultScene" << YAML::Value << ("res://" + relPath.generic_string());

    auto now = std::chrono::system_clock::now();
    auto t = std::chrono::system_clock::to_time_t(now);
    std::tm tm{};
#if defined(_WIN32)
    gmtime_s(&tm, &t);
#else
    gmtime_r(&t, &tm);
#endif
    char buf[32];
    std::strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", &tm);
    out << YAML::Key << "CreatedDate" << YAML::Value << buf;
    out << YAML::EndMap;

    std::ofstream fout(projFile);
    if (!fout.is_open())
    {
        Logger::LogError("Could not open project file for writing: %s",
                         projFile.string().c_str());
        return false;
    }
    fout << out.c_str();
    fout.close();

    Logger::LogOk("Saved project '%s' at '%s'",
                  projectName.c_str(), projectPath.c_str());

    CreateDirectories(baseDir);

    s_currentProjectPath = projectPath;
    s_currentProjectName = projectName;
    return true;
}

const std::string &ProjectManager::GetCurrentProjectPath()
{
    return s_currentProjectPath;
}

const std::string &ProjectManager::GetCurrentProjectName()
{
    return s_currentProjectName;
}

const std::string &ProjectManager::GetCurrentAssetsPath()
{
    static std::string assetsPath;
    fs::path p = fs::path(s_currentProjectPath) / s_currentProjectName / "Assets";
    assetsPath = p.string();
    return assetsPath;

}
