// app_api.cpp - Lua API bindings for app management implementation
// Milestone 10: Device-Side App Management

#include "app_api.h"
#include "app_manager.h"
#include "update_service.h"
#include <mosis/util/logger.h>

#include <lua.hpp>
#include <string>
#include <chrono>

namespace mosis {

// Registry keys for storing pointers
static const char* APP_MANAGER_KEY = "mosis.app_manager";
static const char* UPDATE_SERVICE_KEY = "mosis.update_service";
static const char* CURRENT_APP_ID_KEY = "mosis.current_app_id";
static const char* IS_SYSTEM_APP_KEY = "mosis.is_system_app";

// Helper to get AppManager from Lua registry
static AppManager* GetAppManager(lua_State* L) {
    lua_getfield(L, LUA_REGISTRYINDEX, APP_MANAGER_KEY);
    auto* manager = static_cast<AppManager*>(lua_touserdata(L, -1));
    lua_pop(L, 1);
    return manager;
}

// Helper to get UpdateService from Lua registry
static UpdateService* GetUpdateService(lua_State* L) {
    lua_getfield(L, LUA_REGISTRYINDEX, UPDATE_SERVICE_KEY);
    auto* service = static_cast<UpdateService*>(lua_touserdata(L, -1));
    lua_pop(L, 1);
    return service;
}

// Helper to get current app ID
static std::string GetCurrentAppId(lua_State* L) {
    lua_getfield(L, LUA_REGISTRYINDEX, CURRENT_APP_ID_KEY);
    std::string id = lua_tostring(L, -1) ? lua_tostring(L, -1) : "";
    lua_pop(L, 1);
    return id;
}

// Helper to check if system app
static bool IsSystemApp(lua_State* L) {
    lua_getfield(L, LUA_REGISTRYINDEX, IS_SYSTEM_APP_KEY);
    bool is_system = lua_toboolean(L, -1);
    lua_pop(L, 1);
    return is_system;
}

// ============================================================================
// mosis.apps.* - System apps only
// ============================================================================

// mosis.apps.getInstalled() -> [{package_id, name, version_name, version_code, installed_at}]
static int apps_getInstalled(lua_State* L) {
    if (!IsSystemApp(L)) {
        return luaL_error(L, "mosis.apps.getInstalled requires system permission");
    }

    auto* manager = GetAppManager(L);
    if (!manager) {
        lua_newtable(L);
        return 1;
    }

    auto apps = manager->GetInstalledApps();

    lua_createtable(L, static_cast<int>(apps.size()), 0);
    int idx = 1;

    for (const auto& app : apps) {
        lua_createtable(L, 0, 8);

        lua_pushstring(L, app.package_id.c_str());
        lua_setfield(L, -2, "package_id");

        lua_pushstring(L, app.name.c_str());
        lua_setfield(L, -2, "name");

        lua_pushstring(L, app.version_name.c_str());
        lua_setfield(L, -2, "version_name");

        lua_pushinteger(L, app.version_code);
        lua_setfield(L, -2, "version_code");

        lua_pushboolean(L, app.is_system_app);
        lua_setfield(L, -2, "is_system_app");

        lua_pushstring(L, app.icon_path.c_str());
        lua_setfield(L, -2, "icon");

        lua_pushstring(L, app.developer_name.c_str());
        lua_setfield(L, -2, "developer");

        lua_pushstring(L, app.install_path.c_str());
        lua_setfield(L, -2, "install_path");

        lua_pushstring(L, app.entry_point.c_str());
        lua_setfield(L, -2, "entry_point");

        // installed_at as Unix timestamp
        auto ts = std::chrono::duration_cast<std::chrono::seconds>(
            app.installed_at.time_since_epoch()).count();
        lua_pushinteger(L, ts);
        lua_setfield(L, -2, "installed_at");

        lua_rawseti(L, -2, idx++);
    }

    return 1;
}

// mosis.apps.getInfo(package_id) -> {info} or nil
static int apps_getInfo(lua_State* L) {
    if (!IsSystemApp(L)) {
        return luaL_error(L, "mosis.apps.getInfo requires system permission");
    }

    const char* package_id = luaL_checkstring(L, 1);

    auto* manager = GetAppManager(L);
    if (!manager) {
        lua_pushnil(L);
        return 1;
    }

    auto app = manager->GetApp(package_id);
    if (!app) {
        lua_pushnil(L);
        return 1;
    }

    lua_createtable(L, 0, 12);

    lua_pushstring(L, app->package_id.c_str());
    lua_setfield(L, -2, "package_id");

    lua_pushstring(L, app->name.c_str());
    lua_setfield(L, -2, "name");

    lua_pushstring(L, app->version_name.c_str());
    lua_setfield(L, -2, "version_name");

    lua_pushinteger(L, app->version_code);
    lua_setfield(L, -2, "version_code");

    lua_pushboolean(L, app->is_system_app);
    lua_setfield(L, -2, "is_system_app");

    lua_pushinteger(L, app->package_size);
    lua_setfield(L, -2, "package_size");

    lua_pushinteger(L, app->data_size);
    lua_setfield(L, -2, "data_size");

    lua_pushstring(L, app->icon_path.c_str());
    lua_setfield(L, -2, "icon");

    lua_pushstring(L, app->developer_name.c_str());
    lua_setfield(L, -2, "developer");

    // Permissions array
    lua_createtable(L, static_cast<int>(app->permissions.size()), 0);
    int idx = 1;
    for (const auto& perm : app->permissions) {
        lua_pushstring(L, perm.c_str());
        lua_rawseti(L, -2, idx++);
    }
    lua_setfield(L, -2, "permissions");

    return 1;
}

// mosis.apps.install(url, signature, callback)
static int apps_install(lua_State* L) {
    if (!IsSystemApp(L)) {
        return luaL_error(L, "mosis.apps.install requires system permission");
    }

    const char* url = luaL_checkstring(L, 1);
    const char* signature = lua_isstring(L, 2) ? lua_tostring(L, 2) : "";

    // Callback is optional (argument 3)
    bool has_callback = lua_isfunction(L, 3);

    auto* manager = GetAppManager(L);
    if (!manager) {
        lua_pushboolean(L, false);
        return 1;
    }

    if (has_callback) {
        // Store callback reference
        lua_pushvalue(L, 3);
        int callback_ref = luaL_ref(L, LUA_REGISTRYINDEX);

        // Create progress callback that calls Lua
        // Note: This is simplified - real implementation needs thread safety
        ProgressCallback progress_cb = [L, callback_ref](const InstallProgress& progress) {
            lua_rawgeti(L, LUA_REGISTRYINDEX, callback_ref);

            lua_createtable(L, 0, 3);

            lua_pushstring(L, InstallProgress::StageName(progress.stage));
            lua_setfield(L, -2, "stage");

            lua_pushnumber(L, progress.progress);
            lua_setfield(L, -2, "progress");

            lua_pushstring(L, progress.error.c_str());
            lua_setfield(L, -2, "error");

            if (lua_pcall(L, 1, 0, 0) != LUA_OK) {
                LOG_ERROR("Install callback error: %s", lua_tostring(L, -1));
                lua_pop(L, 1);
            }

            // Clean up ref when complete
            if (progress.stage == InstallProgress::Stage::Complete ||
                progress.stage == InstallProgress::Stage::Failed) {
                luaL_unref(L, LUA_REGISTRYINDEX, callback_ref);
            }
        };

        bool success = manager->Install(url, signature, progress_cb);
        lua_pushboolean(L, success);
    } else {
        bool success = manager->Install(url, signature, nullptr);
        lua_pushboolean(L, success);
    }

    return 1;
}

// mosis.apps.uninstall(package_id) -> boolean
static int apps_uninstall(lua_State* L) {
    if (!IsSystemApp(L)) {
        return luaL_error(L, "mosis.apps.uninstall requires system permission");
    }

    const char* package_id = luaL_checkstring(L, 1);

    auto* manager = GetAppManager(L);
    if (!manager) {
        lua_pushboolean(L, false);
        return 1;
    }

    bool success = manager->Uninstall(package_id);
    lua_pushboolean(L, success);
    return 1;
}

// mosis.apps.launch(package_id) -> boolean
static int apps_launch(lua_State* L) {
    if (!IsSystemApp(L)) {
        return luaL_error(L, "mosis.apps.launch requires system permission");
    }

    const char* package_id = luaL_checkstring(L, 1);

    auto* manager = GetAppManager(L);
    if (!manager) {
        lua_pushboolean(L, false);
        return 1;
    }

    bool success = manager->LaunchApp(package_id);
    lua_pushboolean(L, success);
    return 1;
}

// mosis.apps.getDataSize(package_id) -> number
static int apps_getDataSize(lua_State* L) {
    if (!IsSystemApp(L)) {
        return luaL_error(L, "mosis.apps.getDataSize requires system permission");
    }

    const char* package_id = luaL_checkstring(L, 1);

    auto* manager = GetAppManager(L);
    if (!manager) {
        lua_pushinteger(L, 0);
        return 1;
    }

    int64_t size = manager->GetAppDataSize(package_id);
    lua_pushinteger(L, size);
    return 1;
}

// mosis.apps.clearCache(package_id) -> boolean
static int apps_clearCache(lua_State* L) {
    if (!IsSystemApp(L)) {
        return luaL_error(L, "mosis.apps.clearCache requires system permission");
    }

    const char* package_id = luaL_checkstring(L, 1);

    auto* manager = GetAppManager(L);
    if (!manager) {
        lua_pushboolean(L, false);
        return 1;
    }

    bool success = manager->ClearAppCache(package_id);
    lua_pushboolean(L, success);
    return 1;
}

// mosis.apps.clearData(package_id) -> boolean
static int apps_clearData(lua_State* L) {
    if (!IsSystemApp(L)) {
        return luaL_error(L, "mosis.apps.clearData requires system permission");
    }

    const char* package_id = luaL_checkstring(L, 1);

    auto* manager = GetAppManager(L);
    if (!manager) {
        lua_pushboolean(L, false);
        return 1;
    }

    bool success = manager->ClearAppData(package_id);
    lua_pushboolean(L, success);
    return 1;
}

// mosis.apps.checkUpdates(callback) -> void
static int apps_checkUpdates(lua_State* L) {
    if (!IsSystemApp(L)) {
        return luaL_error(L, "mosis.apps.checkUpdates requires system permission");
    }

    auto* service = GetUpdateService(L);
    if (!service) {
        lua_pushnil(L);
        return 1;
    }

    auto updates = service->CheckForUpdates();

    lua_createtable(L, static_cast<int>(updates.size()), 0);
    int idx = 1;

    for (const auto& update : updates) {
        lua_createtable(L, 0, 8);

        lua_pushstring(L, update.package_id.c_str());
        lua_setfield(L, -2, "package_id");

        lua_pushstring(L, update.name.c_str());
        lua_setfield(L, -2, "name");

        lua_pushstring(L, update.current_version.c_str());
        lua_setfield(L, -2, "current_version");

        lua_pushstring(L, update.new_version.c_str());
        lua_setfield(L, -2, "new_version");

        lua_pushinteger(L, update.download_size);
        lua_setfield(L, -2, "size");

        lua_pushstring(L, update.release_notes.c_str());
        lua_setfield(L, -2, "release_notes");

        lua_pushboolean(L, update.is_critical);
        lua_setfield(L, -2, "critical");

        lua_rawseti(L, -2, idx++);
    }

    return 1;
}

// ============================================================================
// mosis.app.* - All apps (info about current app)
// ============================================================================

// mosis.app.info() -> {package_id, name, version_name, version_code}
static int app_info(lua_State* L) {
    std::string app_id = GetCurrentAppId(L);
    if (app_id.empty()) {
        lua_pushnil(L);
        return 1;
    }

    auto* manager = GetAppManager(L);
    if (!manager) {
        lua_pushnil(L);
        return 1;
    }

    auto app = manager->GetApp(app_id);
    if (!app) {
        lua_pushnil(L);
        return 1;
    }

    lua_createtable(L, 0, 4);

    lua_pushstring(L, app->package_id.c_str());
    lua_setfield(L, -2, "package_id");

    lua_pushstring(L, app->name.c_str());
    lua_setfield(L, -2, "name");

    lua_pushstring(L, app->version_name.c_str());
    lua_setfield(L, -2, "version_name");

    lua_pushinteger(L, app->version_code);
    lua_setfield(L, -2, "version_code");

    return 1;
}

// mosis.app.checkUpdate(callback) -> void
static int app_checkUpdate(lua_State* L) {
    // Get callback
    if (!lua_isfunction(L, 1)) {
        return luaL_error(L, "callback function required");
    }

    std::string app_id = GetCurrentAppId(L);
    auto* service = GetUpdateService(L);

    if (app_id.empty() || !service) {
        // Call callback with no update
        lua_pushvalue(L, 1);
        lua_pushboolean(L, false);
        lua_pushnil(L);
        lua_pcall(L, 2, 0, 0);
        return 0;
    }

    auto updates = service->GetPendingUpdates();
    for (const auto& update : updates) {
        if (update.package_id == app_id) {
            // Call callback with update info
            lua_pushvalue(L, 1);
            lua_pushboolean(L, true);
            lua_pushstring(L, update.new_version.c_str());
            lua_pcall(L, 2, 0, 0);
            return 0;
        }
    }

    // No update available
    lua_pushvalue(L, 1);
    lua_pushboolean(L, false);
    lua_pushnil(L);
    lua_pcall(L, 2, 0, 0);

    return 0;
}

// mosis.app.openStorePage() -> void
static int app_openStorePage(lua_State* L) {
    std::string app_id = GetCurrentAppId(L);
    if (app_id.empty()) {
        return 0;
    }

    // TODO: Navigate to store page for this app
    // This would typically trigger navigation to:
    // mosis://store/app/{app_id}
    LOG_INFO("Open store page for: %s", app_id.c_str());

    return 0;
}

// ============================================================================
// Registration
// ============================================================================

static const luaL_Reg apps_functions[] = {
    {"getInstalled", apps_getInstalled},
    {"getInfo", apps_getInfo},
    {"install", apps_install},
    {"uninstall", apps_uninstall},
    {"launch", apps_launch},
    {"getDataSize", apps_getDataSize},
    {"clearCache", apps_clearCache},
    {"clearData", apps_clearData},
    {"checkUpdates", apps_checkUpdates},
    {nullptr, nullptr}
};

static const luaL_Reg app_functions[] = {
    {"info", app_info},
    {"checkUpdate", app_checkUpdate},
    {"openStorePage", app_openStorePage},
    {nullptr, nullptr}
};

void RegisterAppAPIs(lua_State* L,
                     AppManager* app_manager,
                     UpdateService* update_service,
                     const std::string& current_app_id,
                     bool is_system_app) {
    // Store pointers in registry
    lua_pushlightuserdata(L, app_manager);
    lua_setfield(L, LUA_REGISTRYINDEX, APP_MANAGER_KEY);

    lua_pushlightuserdata(L, update_service);
    lua_setfield(L, LUA_REGISTRYINDEX, UPDATE_SERVICE_KEY);

    lua_pushstring(L, current_app_id.c_str());
    lua_setfield(L, LUA_REGISTRYINDEX, CURRENT_APP_ID_KEY);

    lua_pushboolean(L, is_system_app);
    lua_setfield(L, LUA_REGISTRYINDEX, IS_SYSTEM_APP_KEY);

    // Get or create mosis table
    lua_getglobal(L, "mosis");
    if (lua_isnil(L, -1)) {
        lua_pop(L, 1);
        lua_newtable(L);
        lua_setglobal(L, "mosis");
        lua_getglobal(L, "mosis");
    }

    // Create mosis.apps table (system apps only)
    if (is_system_app) {
        lua_newtable(L);
        luaL_setfuncs(L, apps_functions, 0);
        lua_setfield(L, -2, "apps");
    }

    // Create mosis.app table (all apps)
    lua_newtable(L);
    luaL_setfuncs(L, app_functions, 0);
    lua_setfield(L, -2, "app");

    lua_pop(L, 1);  // pop mosis table

    LOG_DEBUG("Registered app APIs for: %s (system=%d)",
              current_app_id.c_str(), is_system_app);
}

} // namespace mosis
