// sensor_interface.cpp - Sensor interface implementation for Lua sandbox
// Milestone 15: Motion sensors with fingerprinting prevention

#include "sensor_interface.h"
#include <algorithm>
#include <cmath>

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

namespace mosis {

// ============================================================================
// SensorSubscription Implementation
// ============================================================================

SensorSubscription::SensorSubscription(int id, SensorType type, const SensorOptions& options)
    : m_id(id)
    , m_type(type)
    , m_frequency(std::min(options.frequency, 60))  // Cap at 60 Hz
{
}

SensorSubscription::~SensorSubscription() {
    Stop();
}

void SensorSubscription::Start() {
    m_active = true;
}

void SensorSubscription::Stop() {
    m_active = false;
}

void SensorSubscription::SimulateData(const SensorData& data) {
    if (m_active && m_on_data) {
        m_on_data(data);
    }
}

// ============================================================================
// SensorInterface Implementation
// ============================================================================

SensorInterface::SensorInterface(const std::string& app_id)
    : m_app_id(app_id)
{
}

SensorInterface::~SensorInterface() {
    Shutdown();
}

bool SensorInterface::RequiresPermission(SensorType type) {
    switch (type) {
        case SensorType::Accelerometer:
        case SensorType::Gyroscope:
        case SensorType::Magnetometer:
            return true;  // Requires sensors.motion permission
        case SensorType::Proximity:
        case SensorType::AmbientLight:
            return false; // Auto-granted
        default:
            return true;
    }
}

bool SensorInterface::ParseSensorType(const std::string& name, SensorType& out_type) {
    if (name == "accelerometer") {
        out_type = SensorType::Accelerometer;
        return true;
    } else if (name == "gyroscope") {
        out_type = SensorType::Gyroscope;
        return true;
    } else if (name == "magnetometer") {
        out_type = SensorType::Magnetometer;
        return true;
    } else if (name == "proximity") {
        out_type = SensorType::Proximity;
        return true;
    } else if (name == "light" || name == "ambientlight") {
        out_type = SensorType::AmbientLight;
        return true;
    }
    return false;
}

const char* SensorInterface::GetSensorName(SensorType type) {
    switch (type) {
        case SensorType::Accelerometer: return "accelerometer";
        case SensorType::Gyroscope: return "gyroscope";
        case SensorType::Magnetometer: return "magnetometer";
        case SensorType::Proximity: return "proximity";
        case SensorType::AmbientLight: return "light";
        default: return "unknown";
    }
}

SensorData SensorInterface::ApplyPrecision(const SensorData& data) const {
    // Round to 2 decimal places to prevent device fingerprinting
    SensorData result = data;
    result.x = std::round(data.x * 100.0) / 100.0;
    result.y = std::round(data.y * 100.0) / 100.0;
    result.z = std::round(data.z * 100.0) / 100.0;
    return result;
}

std::shared_ptr<SensorSubscription> SensorInterface::Subscribe(
    SensorType type,
    const SensorOptions& options,
    std::string& error
) {
    std::lock_guard<std::mutex> lock(m_mutex);

    // Check permission for motion sensors
    if (RequiresPermission(type) && !m_has_motion_permission) {
        error = "Motion sensor permission denied (requires sensors.motion)";
        return nullptr;
    }

    // Clean up stopped subscriptions first
    CleanupStoppedSubscriptions();

    // Check subscription limit
    if (m_subscriptions.size() >= MAX_SUBSCRIPTIONS) {
        error = "Maximum subscription limit reached (10)";
        return nullptr;
    }

    // Create subscription with capped frequency
    int id = m_next_subscription_id++;
    SensorOptions capped_options = options;
    capped_options.frequency = std::min(options.frequency, MAX_FREQUENCY);

    auto subscription = std::make_shared<SensorSubscription>(id, type, capped_options);
    subscription->Start();

    m_subscriptions[id] = subscription;
    return subscription;
}

std::shared_ptr<SensorSubscription> SensorInterface::Subscribe(
    const std::string& sensor_name,
    const SensorOptions& options,
    std::string& error
) {
    SensorType type;
    if (!ParseSensorType(sensor_name, type)) {
        error = "Unknown sensor type: " + sensor_name;
        return nullptr;
    }
    return Subscribe(type, options, error);
}

void SensorInterface::Unsubscribe(int subscription_id) {
    std::lock_guard<std::mutex> lock(m_mutex);
    auto it = m_subscriptions.find(subscription_id);
    if (it != m_subscriptions.end()) {
        it->second->Stop();
        m_subscriptions.erase(it);
    }
}

void SensorInterface::UnsubscribeAll() {
    std::lock_guard<std::mutex> lock(m_mutex);
    for (auto& [id, subscription] : m_subscriptions) {
        subscription->Stop();
    }
    m_subscriptions.clear();
}

size_t SensorInterface::GetActiveSubscriptionCount() const {
    std::lock_guard<std::mutex> lock(m_mutex);
    size_t count = 0;
    for (const auto& [id, subscription] : m_subscriptions) {
        if (subscription->IsActive()) {
            count++;
        }
    }
    return count;
}

void SensorInterface::Shutdown() {
    UnsubscribeAll();
}

std::shared_ptr<SensorSubscription> SensorInterface::GetSubscription(int id) {
    std::lock_guard<std::mutex> lock(m_mutex);
    auto it = m_subscriptions.find(id);
    if (it != m_subscriptions.end()) {
        return it->second;
    }
    return nullptr;
}

void SensorInterface::CleanupStoppedSubscriptions() {
    // Called with lock held
    for (auto it = m_subscriptions.begin(); it != m_subscriptions.end();) {
        if (!it->second->IsActive()) {
            it = m_subscriptions.erase(it);
        } else {
            ++it;
        }
    }
}

// ============================================================================
// Lua API Implementation
// ============================================================================

// Weak reference to subscription stored in userdata
struct LuaSubscriptionRef {
    std::weak_ptr<SensorSubscription> subscription;
    SensorInterface* sensors;
};

static const char* SUBSCRIPTION_METATABLE = "mosis.SensorSubscription";

static LuaSubscriptionRef* GetSubscriptionRef(lua_State* L, int idx) {
    return static_cast<LuaSubscriptionRef*>(luaL_checkudata(L, idx, SUBSCRIPTION_METATABLE));
}

static std::shared_ptr<SensorSubscription> GetSubscription(lua_State* L, int idx) {
    auto ref = GetSubscriptionRef(L, idx);
    return ref->subscription.lock();
}

// Subscription methods

static int LuaSubscription_stop(lua_State* L) {
    auto ref = GetSubscriptionRef(L, 1);
    auto subscription = ref->subscription.lock();
    if (subscription && ref->sensors) {
        ref->sensors->Unsubscribe(subscription->GetId());
    }
    return 0;
}

static int LuaSubscription_getId(lua_State* L) {
    auto subscription = GetSubscription(L, 1);
    if (subscription) {
        lua_pushinteger(L, subscription->GetId());
    } else {
        lua_pushinteger(L, 0);
    }
    return 1;
}

static int LuaSubscription_isActive(lua_State* L) {
    auto subscription = GetSubscription(L, 1);
    lua_pushboolean(L, subscription && subscription->IsActive());
    return 1;
}

static int LuaSubscription_getFrequency(lua_State* L) {
    auto subscription = GetSubscription(L, 1);
    if (subscription) {
        lua_pushinteger(L, subscription->GetFrequency());
    } else {
        lua_pushinteger(L, 0);
    }
    return 1;
}

static int LuaSubscription_gc(lua_State* L) {
    auto ref = GetSubscriptionRef(L, 1);
    ref->~LuaSubscriptionRef();
    return 0;
}

static const luaL_Reg subscription_methods[] = {
    {"stop", LuaSubscription_stop},
    {"getId", LuaSubscription_getId},
    {"isActive", LuaSubscription_isActive},
    {"getFrequency", LuaSubscription_getFrequency},
    {nullptr, nullptr}
};

static void CreateSubscriptionMetatable(lua_State* L) {
    luaL_newmetatable(L, SUBSCRIPTION_METATABLE);

    // __index = methods table
    lua_newtable(L);
    luaL_setfuncs(L, subscription_methods, 0);
    lua_setfield(L, -2, "__index");

    // __gc
    lua_pushcfunction(L, LuaSubscription_gc);
    lua_setfield(L, -2, "__gc");

    lua_pop(L, 1);
}

static void PushSubscription(lua_State* L, std::shared_ptr<SensorSubscription> subscription, SensorInterface* sensors) {
    auto ref = static_cast<LuaSubscriptionRef*>(lua_newuserdata(L, sizeof(LuaSubscriptionRef)));
    new (ref) LuaSubscriptionRef{subscription, sensors};
    luaL_setmetatable(L, SUBSCRIPTION_METATABLE);
}

// sensors.subscribe(sensorName, callback, options)
static int LuaSensors_subscribe(lua_State* L) {
    auto sensors = static_cast<SensorInterface*>(lua_touserdata(L, lua_upvalueindex(1)));
    if (!sensors) {
        lua_pushnil(L);
        lua_pushstring(L, "Sensor interface not available");
        return 2;
    }

    // Get sensor name
    const char* sensor_name = luaL_checkstring(L, 1);

    // Validate callback
    if (!lua_isfunction(L, 2)) {
        return luaL_error(L, "Second argument must be a callback function");
    }

    // Parse options from third argument (optional table)
    SensorOptions options;
    if (lua_istable(L, 3)) {
        lua_getfield(L, 3, "frequency");
        if (lua_isnumber(L, -1)) {
            options.frequency = static_cast<int>(lua_tointeger(L, -1));
        }
        lua_pop(L, 1);
    }

    // Store callback in registry
    lua_pushvalue(L, 2);
    int callback_ref = luaL_ref(L, LUA_REGISTRYINDEX);

    std::string error;
    auto subscription = sensors->Subscribe(sensor_name, options, error);

    if (!subscription) {
        luaL_unref(L, LUA_REGISTRYINDEX, callback_ref);
        lua_pushnil(L);
        lua_pushstring(L, error.c_str());
        return 2;
    }

    // Set up callback that applies precision reduction
    subscription->SetOnData([L, callback_ref, sensors](const SensorData& raw_data) {
        // Apply precision reduction
        SensorData data = sensors->ApplyPrecision(raw_data);

        lua_rawgeti(L, LUA_REGISTRYINDEX, callback_ref);
        if (lua_isfunction(L, -1)) {
            // Create data table
            lua_newtable(L);
            lua_pushnumber(L, data.x);
            lua_setfield(L, -2, "x");
            lua_pushnumber(L, data.y);
            lua_setfield(L, -2, "y");
            lua_pushnumber(L, data.z);
            lua_setfield(L, -2, "z");
            lua_pushinteger(L, data.timestamp);
            lua_setfield(L, -2, "timestamp");

            lua_pcall(L, 1, 0, 0);
        } else {
            lua_pop(L, 1);
        }
    });

    PushSubscription(L, subscription, sensors);
    return 1;
}

// sensors.unsubscribeAll()
static int LuaSensors_unsubscribeAll(lua_State* L) {
    auto sensors = static_cast<SensorInterface*>(lua_touserdata(L, lua_upvalueindex(1)));
    if (sensors) {
        sensors->UnsubscribeAll();
    }
    return 0;
}

// sensors.getSubscriptionCount()
static int LuaSensors_getSubscriptionCount(lua_State* L) {
    auto sensors = static_cast<SensorInterface*>(lua_touserdata(L, lua_upvalueindex(1)));
    if (sensors) {
        lua_pushinteger(L, static_cast<lua_Integer>(sensors->GetActiveSubscriptionCount()));
    } else {
        lua_pushinteger(L, 0);
    }
    return 1;
}

// Helper to set a global in the real _G (bypassing any proxy)
static void SetGlobalInRealG(lua_State* L, const char* name) {
    lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);

    if (lua_getmetatable(L, -1)) {
        lua_getfield(L, -1, "__index");
        if (lua_istable(L, -1)) {
            lua_pushvalue(L, -4);
            lua_setfield(L, -2, name);
            lua_pop(L, 4);
            return;
        }
        lua_pop(L, 2);
    }

    lua_pushvalue(L, -2);
    lua_setfield(L, -2, name);
    lua_pop(L, 2);
}

void RegisterSensorAPI(lua_State* L, SensorInterface* sensors) {
    // Create subscription metatable
    CreateSubscriptionMetatable(L);

    // Create sensors table
    lua_newtable(L);

    // sensors.subscribe
    lua_pushlightuserdata(L, sensors);
    lua_pushcclosure(L, LuaSensors_subscribe, 1);
    lua_setfield(L, -2, "subscribe");

    // sensors.unsubscribeAll
    lua_pushlightuserdata(L, sensors);
    lua_pushcclosure(L, LuaSensors_unsubscribeAll, 1);
    lua_setfield(L, -2, "unsubscribeAll");

    // sensors.getSubscriptionCount
    lua_pushlightuserdata(L, sensors);
    lua_pushcclosure(L, LuaSensors_getSubscriptionCount, 1);
    lua_setfield(L, -2, "getSubscriptionCount");

    // Set as global (bypasses sandbox proxy)
    SetGlobalInRealG(L, "sensors");
}

} // namespace mosis
