#include "desktop_sandbox.h"
#include "timer_manager.h"
#include "json_api.h"
#include "crypto_api.h"
#include "virtual_fs.h"
#include <lua.hpp>
#include <filesystem>
#include <iostream>

namespace fs = std::filesystem;

namespace mosis {

DesktopSandbox::DesktopSandbox(const DesktopSandboxConfig& config)
    : m_config(config) {
    // Create timer manager
    m_timer_manager = std::make_unique<TimerManager>();

    // Create virtual filesystem
    // Ensure data root exists
    std::error_code ec;
    fs::create_directories(m_config.data_root, ec);

    m_vfs = std::make_unique<VirtualFS>(
        m_config.app_id,
        m_config.data_root,
        VirtualFSLimits{}
    );

    std::cout << "DesktopSandbox initialized for app: " << m_config.app_id << std::endl;
    std::cout << "  Data root: " << m_config.data_root << std::endl;
}

DesktopSandbox::~DesktopSandbox() {
    std::cout << "DesktopSandbox destroyed" << std::endl;
}

// crypto.sha256(data) -> hash
// Convenience wrapper for crypto.hash("sha256", data)
static int lua_crypto_sha256(lua_State* L) {
    // Get the crypto table
    lua_getglobal(L, "crypto");
    if (!lua_istable(L, -1)) {
        lua_pop(L, 1);
        return luaL_error(L, "crypto table not found");
    }

    // Get crypto.hash function
    lua_getfield(L, -1, "hash");
    if (!lua_isfunction(L, -1)) {
        lua_pop(L, 2);
        return luaL_error(L, "crypto.hash function not found");
    }

    // Call hash("sha256", data)
    lua_pushstring(L, "sha256");
    lua_pushvalue(L, 1);  // Push the original data argument
    lua_call(L, 2, 1);

    // Remove the crypto table from stack, keep result
    lua_remove(L, -2);
    return 1;
}

// crypto.sha512(data) -> hash
static int lua_crypto_sha512(lua_State* L) {
    lua_getglobal(L, "crypto");
    if (!lua_istable(L, -1)) {
        lua_pop(L, 1);
        return luaL_error(L, "crypto table not found");
    }

    lua_getfield(L, -1, "hash");
    if (!lua_isfunction(L, -1)) {
        lua_pop(L, 2);
        return luaL_error(L, "crypto.hash function not found");
    }

    lua_pushstring(L, "sha512");
    lua_pushvalue(L, 1);
    lua_call(L, 2, 1);

    lua_remove(L, -2);
    return 1;
}

void DesktopSandbox::RegisterAPIs(lua_State* L) {
    std::cout << "Registering sandbox APIs..." << std::endl;

    // Register timer API (setTimeout, setInterval, clearTimeout, clearInterval)
    RegisterTimerAPI(L, m_timer_manager.get(), m_config.app_id);
    std::cout << "  Timer API registered" << std::endl;

    // Register JSON API (json.encode, json.decode)
    RegisterJsonAPI(L, JsonLimits{});
    std::cout << "  JSON API registered" << std::endl;

    // Register crypto API (crypto.randomBytes, crypto.hash, crypto.hmac)
    RegisterCryptoAPI(L);

    // Add convenience wrappers for common hash algorithms
    lua_getglobal(L, "crypto");
    if (lua_istable(L, -1)) {
        lua_pushcfunction(L, lua_crypto_sha256);
        lua_setfield(L, -2, "sha256");

        lua_pushcfunction(L, lua_crypto_sha512);
        lua_setfield(L, -2, "sha512");
    }
    lua_pop(L, 1);
    std::cout << "  Crypto API registered" << std::endl;

    // Register virtual filesystem API (fs.read, fs.write, fs.delete, etc.)
    RegisterVirtualFS(L, m_vfs.get());
    std::cout << "  VirtualFS API registered" << std::endl;

    std::cout << "All sandbox APIs registered" << std::endl;
}

void DesktopSandbox::UnregisterAPIs(lua_State* L) {
    // Clear the global tables by setting them to nil
    // This ensures clean state on hot-reload
    lua_pushnil(L);
    lua_setglobal(L, "setTimeout");

    lua_pushnil(L);
    lua_setglobal(L, "clearTimeout");

    lua_pushnil(L);
    lua_setglobal(L, "setInterval");

    lua_pushnil(L);
    lua_setglobal(L, "clearInterval");

    lua_pushnil(L);
    lua_setglobal(L, "json");

    lua_pushnil(L);
    lua_setglobal(L, "crypto");

    lua_pushnil(L);
    lua_setglobal(L, "fs");

    std::cout << "Sandbox APIs unregistered" << std::endl;
}

void DesktopSandbox::Update() {
    // Process any pending timers
    if (m_timer_manager) {
        m_timer_manager->ProcessTimers();
    }
}

void DesktopSandbox::Reset() {
    // Clear all timers for the app
    if (m_timer_manager) {
        m_timer_manager->ClearAppTimers(m_config.app_id);
        std::cout << "Timers cleared for hot-reload" << std::endl;
    }

    // Optionally clear temp files
    if (m_vfs) {
        m_vfs->ClearTemp();
    }
}

} // namespace mosis
