// Desktop kernel implementation
#include "kernel_impl.h"
#include "platform.h"
#include "file_interface.h"
#include "log.h"
#include <RmlUi/Core.h>
#include <RmlUi/Lua.h>
#include <RmlUi/Lua/Interpreter.h>
#include <mutex>
#include <vector>
#include <functional>
#include <atomic>
#include <iostream>
#include <chrono>
#include <algorithm>

extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}

namespace mosis {

// Global kernel instance for Lua access
static DesktopKernel* g_kernel_instance = nullptr;

// Implementation class
class DesktopKernel::Impl {
public:
    KernelConfig config;
    Rml::Context* context = nullptr;
    Rml::ElementDocument* document = nullptr;
    std::vector<std::shared_ptr<IServiceListener>> listeners;
    std::vector<std::function<void()>> tasks;
    std::mutex mutex;
    std::atomic<bool> running{false};
    std::atomic<bool> reload_requested{false};
    std::string current_document_path;

    // Status bar time
    std::string status_time;
    Rml::DataModelHandle status_time_handle;

    explicit Impl(const KernelConfig& cfg) : config(cfg) {}
};

DesktopKernel::DesktopKernel(const KernelConfig& config)
    : m_impl(std::make_unique<Impl>(config))
{
    g_kernel_instance = this;
}

DesktopKernel::~DesktopKernel() {
    Stop();
    g_kernel_instance = nullptr;
}

void DesktopKernel::AddListener(std::shared_ptr<IServiceListener> listener) {
    std::lock_guard lock(m_impl->mutex);
    m_impl->listeners.push_back(listener);
    if (m_impl->running) {
        listener->OnServiceInitialized(true);
    }
}

void DesktopKernel::RemoveListener(std::shared_ptr<IServiceListener> listener) {
    std::lock_guard lock(m_impl->mutex);
    m_impl->listeners.erase(
        std::remove(m_impl->listeners.begin(), m_impl->listeners.end(), listener),
        m_impl->listeners.end()
    );
}

void DesktopKernel::OnTouchDown(float x, float y) {
    std::lock_guard lock(m_impl->mutex);
    auto px = static_cast<int>(x * m_impl->config.width);
    auto py = static_cast<int>(y * m_impl->config.height);
    m_impl->tasks.emplace_back([this, px, py]() {
        if (m_impl->context) {
            m_impl->context->ProcessMouseMove(px, py, 0);
            m_impl->context->ProcessMouseButtonDown(0, 0);
        }
    });
}

void DesktopKernel::OnTouchMove(float x, float y) {
    std::lock_guard lock(m_impl->mutex);
    auto px = static_cast<int>(x * m_impl->config.width);
    auto py = static_cast<int>(y * m_impl->config.height);
    m_impl->tasks.emplace_back([this, px, py]() {
        if (m_impl->context) {
            m_impl->context->ProcessMouseMove(px, py, 0);
        }
    });
}

void DesktopKernel::OnTouchUp(float x, float y) {
    std::lock_guard lock(m_impl->mutex);
    auto px = static_cast<int>(x * m_impl->config.width);
    auto py = static_cast<int>(y * m_impl->config.height);
    m_impl->tasks.emplace_back([this, px, py]() {
        if (m_impl->context) {
            m_impl->context->ProcessMouseMove(px, py, 0);
            m_impl->context->ProcessMouseButtonUp(0, 0);
        }
    });
}

void DesktopKernel::OnBackButton() {
    std::lock_guard lock(m_impl->mutex);
    m_impl->tasks.emplace_back([this]() {
        lua_State* L = Rml::Lua::Interpreter::GetLuaState();
        lua_getglobal(L, "goBack");
        if (lua_isfunction(L, -1)) {
            lua_pcall(L, 0, 0, 0);
        } else {
            lua_pop(L, 1);
        }
    });
}

void DesktopKernel::OnHomeButton() {
    std::lock_guard lock(m_impl->mutex);
    m_impl->tasks.emplace_back([this]() {
        lua_State* L = Rml::Lua::Interpreter::GetLuaState();
        lua_getglobal(L, "goHome");
        if (lua_isfunction(L, -1)) {
            lua_pcall(L, 0, 0, 0);
        } else {
            lua_pop(L, 1);
        }
    });
}

void DesktopKernel::OnRecentsButton() {
    std::cout << "Recents button pressed (not implemented)" << std::endl;
}

void DesktopKernel::RequestReload() {
    m_impl->reload_requested = true;
}

void DesktopKernel::LoadDocument(const std::string& path) {
    std::lock_guard lock(m_impl->mutex);
    m_impl->tasks.emplace_back([this, path]() {
        auto& file_interface = GetPlatform().GetFileInterface();
        std::string full_path = file_interface.ResolvePath(path);

        if (m_impl->document) {
            m_impl->context->UnloadDocument(m_impl->document);
            m_impl->document = nullptr;
        }

        m_impl->document = m_impl->context->LoadDocument(full_path);
        if (m_impl->document) {
            m_impl->document->Show();
            m_impl->current_document_path = full_path;
            std::cout << "Loaded document: " << path << std::endl;
        } else {
            std::cerr << "Failed to load document: " << path << std::endl;
        }
    });
}

void DesktopKernel::Start() {
    if (m_impl->running) return;
    m_impl->running = true;

    std::lock_guard lock(m_impl->mutex);
    for (auto& listener : m_impl->listeners) {
        listener->OnServiceInitialized(true);
    }
}

void DesktopKernel::Stop() {
    m_impl->running = false;
}

bool DesktopKernel::IsRunning() const {
    return m_impl->running;
}

void DesktopKernel::Update() {
    if (!m_impl->running || !m_impl->context) return;

    // Process pending tasks
    {
        std::lock_guard lock(m_impl->mutex);
        for (const auto& task : m_impl->tasks) {
            task();
        }
        m_impl->tasks.clear();
    }

    // Handle reload request
    if (m_impl->reload_requested) {
        m_impl->reload_requested = false;

        if (!m_impl->current_document_path.empty()) {
            std::cout << "Reloading document..." << std::endl;

            if (m_impl->document) {
                m_impl->context->UnloadDocument(m_impl->document);
                m_impl->document = nullptr;
            }

            m_impl->context->Update();

            m_impl->document = m_impl->context->LoadDocument(m_impl->current_document_path);
            if (m_impl->document) {
                m_impl->document->ReloadStyleSheet();
                m_impl->document->Show();
                std::cout << "Document reloaded" << std::endl;
            }
        }
    }

    // Update status bar time
    auto now = std::chrono::system_clock::now();
    auto time_t = std::chrono::system_clock::to_time_t(now);
    std::tm tm{};
#ifdef _WIN32
    localtime_s(&tm, &time_t);
#else
    localtime_r(&time_t, &tm);
#endif
    char buffer[16];
    std::strftime(buffer, sizeof(buffer), "%H:%M", &tm);
    m_impl->status_time = buffer;

    if (m_impl->status_time_handle) {
        m_impl->status_time_handle.DirtyVariable("time");
    }

    m_impl->context->Update();
}

void DesktopKernel::Render() {
    if (!m_impl->running || !m_impl->context) return;
    m_impl->context->Render();

    std::lock_guard lock(m_impl->mutex);
    for (auto& listener : m_impl->listeners) {
        listener->OnFrameAvailable();
    }
}

void DesktopKernel::SetContext(Rml::Context* context) {
    m_impl->context = context;

    // Setup status data model
    if (m_impl->context) {
        if (auto constructor = m_impl->context->CreateDataModel("status-data")) {
            constructor.Bind("time", &m_impl->status_time);
            m_impl->status_time_handle = constructor.GetModelHandle();
        }
    }
}

Rml::Context* DesktopKernel::GetContext() const {
    return m_impl->context;
}

void DesktopKernel::SetDocument(Rml::ElementDocument* doc) {
    m_impl->document = doc;
}

Rml::ElementDocument* DesktopKernel::GetDocument() const {
    return m_impl->document;
}

void DesktopKernel::SetCurrentDocumentPath(const std::string& path) {
    m_impl->current_document_path = path;
}

int DesktopKernel::LuaLoadScreen(lua_State* L) {
    if (!g_kernel_instance) {
        lua_pushboolean(L, false);
        return 1;
    }

    const char* path = luaL_checkstring(L, 1);
    Log(std::string("Loading screen: ") + path);

    auto& file_interface = GetPlatform().GetFileInterface();
    std::string full_path = file_interface.ResolvePath(path);

    if (!file_interface.FileExists(full_path)) {
        Log(std::string("Screen not found: ") + full_path);
        lua_pushboolean(L, false);
        return 1;
    }

    auto* impl = g_kernel_instance->m_impl.get();

    // Unload current document
    if (impl->document) {
        impl->context->UnloadDocument(impl->document);
        impl->document = nullptr;
    }

    // Load new document
    impl->document = impl->context->LoadDocument(full_path);
    if (impl->document) {
        impl->document->Show();
        impl->current_document_path = full_path;
        Log(std::string("Loaded screen: ") + path);
        lua_pushboolean(L, true);
    } else {
        Log(std::string("Failed to load screen: ") + path);
        lua_pushboolean(L, false);
    }

    return 1;
}

void DesktopKernel::RegisterLuaFunctions() {
    lua_State* L = Rml::Lua::Interpreter::GetLuaState();
    lua_pushcfunction(L, LuaLoadScreen);
    lua_setglobal(L, "loadScreen");
    std::cout << "Registered Lua loadScreen function" << std::endl;
}

// Factory function
std::unique_ptr<IKernel> CreateKernel(const KernelConfig& config) {
    return std::make_unique<DesktopKernel>(config);
}

} // namespace mosis
