#include "test_harness.h"
#include "lua_sandbox.h"
#include "permission_gate.h"
#include <lua.hpp>
#include <iostream>
#include "audit_log.h"
#include "rate_limiter.h"
#include "path_sandbox.h"
#include "timer_manager.h"
#include "json_api.h"
#include "crypto_api.h"
#include "virtual_fs.h"
#include "database_manager.h"
#include "http_validator.h"
#include "network_manager.h"
#include "websocket_manager.h"
#include "camera_interface.h"
#include "microphone_interface.h"
#include "audio_output.h"
#include "location_interface.h"
#include "sensor_interface.h"
#include "bluetooth_interface.h"
#include "contacts_interface.h"
#include "message_bus.h"
#include "sandbox_manager.h"
#include "lua_fuzzer.h"
#include <filesystem>
#include <fstream>
#include <sstream>
#include <thread>
#include <chrono>
#include <cstdlib>
#ifdef _WIN32
#include <crtdbg.h>
#endif

// Get path to scripts directory
std::string GetScriptsDir() {
    // Scripts are copied to build directory by CMake
    return "scripts";
}

// Helper to create test context
SandboxContext TestContext() {
    return SandboxContext{
        .app_id = "test.app",
        .app_path = ".",
        .permissions = {},
        .is_system_app = false
    };
}

// Helper to read file contents
std::string ReadFile(const std::string& path) {
    std::ifstream f(path);
    if (!f) return "";
    std::stringstream ss;
    ss << f.rdbuf();
    return ss.str();
}

// Helper to setup a _test table in real _G for timer tests
// This allows test scripts to store state without triggering the proxy's __newindex
void SetupTestTable(lua_State* L) {
    lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
    if (lua_getmetatable(L, -1)) {
        lua_getfield(L, -1, "__index");
        if (lua_istable(L, -1)) {
            // Found real _G through proxy's __index
            lua_newtable(L);  // Create _test table
            lua_setfield(L, -2, "_test");
            lua_pop(L, 3);  // pop real _G, metatable, proxy
            return;
        }
        lua_pop(L, 2);  // pop __index, metatable
    }
    // No proxy, use directly
    lua_newtable(L);
    lua_setfield(L, -2, "_test");
    lua_pop(L, 1);  // pop _G
}

//=============================================================================
// TEST DEFINITIONS
//=============================================================================

bool Test_DangerousGlobalsRemoved(std::string& error_msg) {
    LuaSandbox sandbox(TestContext());
    std::string script = ReadFile(GetScriptsDir() + "/test_globals_removed.lua");
    EXPECT_FALSE(script.empty());
    EXPECT_TRUE(sandbox.LoadString(script, "test_globals_removed.lua"));
    return true;
}

bool Test_BytecodeRejected(std::string& error_msg) {
    LuaSandbox sandbox(TestContext());

    // Lua 5.4 bytecode signature
    std::string bytecode = "\x1bLua\x54\x00\x19\x93\r\n\x1a\n";

    EXPECT_FALSE(sandbox.LoadString(bytecode, "bytecode_test"));

    // Error should mention binary/bytecode
    std::string err = sandbox.GetLastError();
    bool mentions_binary = (err.find("binary") != std::string::npos ||
                           err.find("attempt to load") != std::string::npos ||
                           err.find("text") != std::string::npos);
    EXPECT_TRUE(mentions_binary);

    return true;
}

bool Test_MemoryLimitEnforced(std::string& error_msg) {
    SandboxLimits limits;
    limits.memory_bytes = 512 * 1024;  // 512 KB - very small

    LuaSandbox sandbox(TestContext(), limits);

    std::string script = ReadFile(GetScriptsDir() + "/test_memory_limit.lua");
    EXPECT_FALSE(script.empty());

    // Should fail due to memory exhaustion
    EXPECT_FALSE(sandbox.LoadString(script, "test_memory_limit.lua"));

    return true;
}

bool Test_CPULimitEnforced(std::string& error_msg) {
    SandboxLimits limits;
    limits.instructions_per_call = 10000;  // Very low

    LuaSandbox sandbox(TestContext(), limits);

    std::string script = ReadFile(GetScriptsDir() + "/test_cpu_limit.lua");
    EXPECT_FALSE(script.empty());

    // Should fail due to instruction limit
    EXPECT_FALSE(sandbox.LoadString(script, "test_cpu_limit.lua"));

    // Error should mention instructions
    std::string err = sandbox.GetLastError();
    EXPECT_CONTAINS(err, "instruction");

    return true;
}

bool Test_MetatableProtected(std::string& error_msg) {
    LuaSandbox sandbox(TestContext());
    std::string script = ReadFile(GetScriptsDir() + "/test_metatable_protected.lua");
    EXPECT_FALSE(script.empty());
    if (!sandbox.LoadString(script, "test_metatable_protected.lua")) {
        error_msg = "Script failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

bool Test_SafeOperationsWork(std::string& error_msg) {
    LuaSandbox sandbox(TestContext());
    std::string script = ReadFile(GetScriptsDir() + "/test_safe_operations.lua");
    EXPECT_FALSE(script.empty());
    EXPECT_TRUE(sandbox.LoadString(script, "test_safe_operations.lua"));
    return true;
}

bool Test_StringDumpRemoved(std::string& error_msg) {
    LuaSandbox sandbox(TestContext());
    std::string script = ReadFile(GetScriptsDir() + "/test_string_dump_removed.lua");
    EXPECT_FALSE(script.empty());
    EXPECT_TRUE(sandbox.LoadString(script, "test_string_dump_removed.lua"));
    return true;
}

bool Test_MemoryTracking(std::string& error_msg) {
    LuaSandbox sandbox(TestContext());

    // Initially should have some baseline memory
    size_t initial = sandbox.GetMemoryUsed();
    EXPECT_TRUE(initial > 0);

    // Allocate some data
    sandbox.LoadString("local t = {}; for i=1,1000 do t[i] = string.rep('x', 100) end", "alloc");

    // Memory should have increased
    size_t after = sandbox.GetMemoryUsed();
    EXPECT_TRUE(after > initial);

    return true;
}

bool Test_InstructionCounting(std::string& error_msg) {
    SandboxLimits limits;
    limits.instructions_per_call = 1000000;  // 1M instructions

    LuaSandbox sandbox(TestContext(), limits);

    // Run some code
    sandbox.LoadString("for i=1,10000 do local x = i * 2 end", "counting");

    // Should have used some instructions
    EXPECT_TRUE(sandbox.GetInstructionsUsed() > 0);

    return true;
}

bool Test_MultipleLoads(std::string& error_msg) {
    LuaSandbox sandbox(TestContext());

    // Should be able to load multiple scripts
    EXPECT_TRUE(sandbox.LoadString("local a = 1", "script1"));
    EXPECT_TRUE(sandbox.LoadString("local b = 2", "script2"));
    EXPECT_TRUE(sandbox.LoadString("local c = 3", "script3"));

    return true;
}

bool Test_ErrorRecovery(std::string& error_msg) {
    LuaSandbox sandbox(TestContext());

    // Script with error
    EXPECT_FALSE(sandbox.LoadString("error('test error')", "error_script"));

    // Should still be able to run more code after error
    EXPECT_TRUE(sandbox.LoadString("local x = 1", "after_error"));

    return true;
}

//=============================================================================
// PERMISSION SYSTEM TESTS (Milestone 2)
//=============================================================================

bool Test_NormalPermissionAutoGranted(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    ctx.permissions = {"internet", "vibrate"};  // Declare normal permissions

    mosis::PermissionGate gate(ctx);

    // Normal permissions should be auto-granted when declared
    EXPECT_TRUE(gate.HasPermission("internet"));
    EXPECT_TRUE(gate.HasPermission("vibrate"));

    return true;
}

bool Test_DangerousPermissionRequiresGrant(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    ctx.permissions = {"camera"};  // Declare dangerous permission

    mosis::PermissionGate gate(ctx);

    // Not granted yet (regular app)
    EXPECT_FALSE(gate.HasPermission("camera"));

    // Grant at runtime
    gate.GrantPermission("camera");

    // Now should have it
    EXPECT_TRUE(gate.HasPermission("camera"));

    // Revoke
    gate.RevokePermission("camera");
    EXPECT_FALSE(gate.HasPermission("camera"));

    return true;
}

bool Test_SignaturePermissionSystemOnly(std::string& error_msg) {
    // Non-system app
    SandboxContext ctx = TestContext();
    ctx.permissions = {"system.settings"};
    ctx.is_system_app = false;

    mosis::PermissionGate gate(ctx);
    EXPECT_FALSE(gate.HasPermission("system.settings"));

    // System app
    SandboxContext sys_ctx = TestContext();
    sys_ctx.permissions = {"system.settings"};
    sys_ctx.is_system_app = true;

    mosis::PermissionGate sys_gate(sys_ctx);
    EXPECT_TRUE(sys_gate.HasPermission("system.settings"));

    return true;
}

bool Test_UserGestureTracking(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    mosis::PermissionGate gate(ctx);

    // No recent gesture
    EXPECT_FALSE(gate.HasRecentUserGesture(5000));

    // Record gesture
    gate.RecordUserGesture();

    // Should have recent gesture
    EXPECT_TRUE(gate.HasRecentUserGesture(5000));

    // Wait for gesture to expire (use short window)
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    EXPECT_FALSE(gate.HasRecentUserGesture(50));  // 50ms window, we waited 100ms

    return true;
}

bool Test_UndeclaredPermissionDenied(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    ctx.permissions = {};  // No permissions declared

    mosis::PermissionGate gate(ctx);

    // Even normal permissions need to be declared
    EXPECT_FALSE(gate.HasPermission("internet"));

    // Dangerous permissions also denied
    EXPECT_FALSE(gate.HasPermission("camera"));

    return true;
}

bool Test_SystemAppGetsDangerousAuto(std::string& error_msg) {
    // System apps get dangerous permissions automatically (no runtime grant needed)
    SandboxContext ctx = TestContext();
    ctx.permissions = {"camera", "microphone"};
    ctx.is_system_app = true;

    mosis::PermissionGate gate(ctx);

    // System app should have dangerous perms without explicit grant
    EXPECT_TRUE(gate.HasPermission("camera"));
    EXPECT_TRUE(gate.HasPermission("microphone"));

    return true;
}

bool Test_PermissionCategoryCheck(std::string& error_msg) {
    // Check that permission categories are correct
    EXPECT_TRUE(mosis::PermissionGate::GetCategory("internet") == mosis::PermissionCategory::Normal);
    EXPECT_TRUE(mosis::PermissionGate::GetCategory("camera") == mosis::PermissionCategory::Dangerous);
    EXPECT_TRUE(mosis::PermissionGate::GetCategory("system.settings") == mosis::PermissionCategory::Signature);

    // Unknown permissions default to Dangerous
    EXPECT_TRUE(mosis::PermissionGate::GetCategory("unknown.perm") == mosis::PermissionCategory::Dangerous);

    return true;
}

//=============================================================================
// AUDIT LOG TESTS (Milestone 3)
//=============================================================================

bool Test_AuditLogBasic(std::string& error_msg) {
    mosis::AuditLog log(1000);

    log.Log(mosis::AuditEvent::AppStart, "test.app", "App started");
    log.Log(mosis::AuditEvent::PermissionCheck, "test.app", "camera", true);
    log.Log(mosis::AuditEvent::PermissionDenied, "test.app", "microphone", false);

    auto entries = log.GetEntries(10);
    EXPECT_TRUE(entries.size() == 3);

    auto app_entries = log.GetEntriesForApp("test.app", 10);
    EXPECT_TRUE(app_entries.size() == 3);

    // Check event filtering
    auto denied_entries = log.GetEntriesByEvent(mosis::AuditEvent::PermissionDenied, 10);
    EXPECT_TRUE(denied_entries.size() == 1);

    return true;
}

bool Test_AuditLogRingBuffer(std::string& error_msg) {
    mosis::AuditLog log(100);  // Small buffer

    // Log more than capacity
    for (int i = 0; i < 200; i++) {
        log.Log(mosis::AuditEvent::Custom, "test.app", std::to_string(i));
    }

    // Should only have latest 100 stored
    auto entries = log.GetEntries(200);
    EXPECT_TRUE(entries.size() == 100);

    // Total logged should be 200
    EXPECT_TRUE(log.GetTotalEntries() == 200);

    // Most recent should be "199"
    EXPECT_TRUE(entries[0].details == "199");

    return true;
}

bool Test_AuditLogThreadSafe(std::string& error_msg) {
    mosis::AuditLog log(10000);

    // Spawn multiple threads logging concurrently
    std::vector<std::thread> threads;
    for (int t = 0; t < 4; t++) {
        threads.emplace_back([&log, t]() {
            for (int i = 0; i < 1000; i++) {
                log.Log(mosis::AuditEvent::Custom, "app" + std::to_string(t), std::to_string(i));
            }
        });
    }

    for (auto& thread : threads) {
        thread.join();
    }

    // Should have logged 4000 entries
    EXPECT_TRUE(log.GetTotalEntries() == 4000);

    return true;
}

//=============================================================================
// RATE LIMITER TESTS (Milestone 3)
//=============================================================================

bool Test_RateLimiterBasic(std::string& error_msg) {
    mosis::RateLimiter limiter;

    // Should succeed initially (has tokens)
    EXPECT_TRUE(limiter.Check("test.app", "network.request"));

    return true;
}

bool Test_RateLimiterExhaustion(std::string& error_msg) {
    mosis::RateLimiter limiter;
    limiter.SetLimit("test.op", {0.0, 5.0});  // 5 tokens, no refill

    // Use all tokens
    for (int i = 0; i < 5; i++) {
        EXPECT_TRUE(limiter.Check("test.app", "test.op"));
    }

    // Should be denied now
    EXPECT_FALSE(limiter.Check("test.app", "test.op"));

    return true;
}

bool Test_RateLimiterRefill(std::string& error_msg) {
    mosis::RateLimiter limiter;
    limiter.SetLimit("test.op", {1000.0, 1.0});  // 1000/sec, max 1 token

    // Use the token
    EXPECT_TRUE(limiter.Check("test.app", "test.op"));
    EXPECT_FALSE(limiter.Check("test.app", "test.op"));

    // Wait a bit for refill (2ms = ~2 tokens at 1000/sec, but max is 1)
    std::this_thread::sleep_for(std::chrono::milliseconds(5));

    // Should have token again
    EXPECT_TRUE(limiter.Check("test.app", "test.op"));

    return true;
}

bool Test_RateLimiterAppIsolation(std::string& error_msg) {
    mosis::RateLimiter limiter;
    limiter.SetLimit("test.op", {0.0, 1.0});  // 1 token, no refill

    // App 1 uses its token
    EXPECT_TRUE(limiter.Check("app1", "test.op"));
    EXPECT_FALSE(limiter.Check("app1", "test.op"));

    // App 2 should still have its token
    EXPECT_TRUE(limiter.Check("app2", "test.op"));

    return true;
}

bool Test_RateLimiterReset(std::string& error_msg) {
    mosis::RateLimiter limiter;
    limiter.SetLimit("test.op", {0.0, 2.0});  // 2 tokens, no refill

    // Use all tokens
    EXPECT_TRUE(limiter.Check("test.app", "test.op"));
    EXPECT_TRUE(limiter.Check("test.app", "test.op"));
    EXPECT_FALSE(limiter.Check("test.app", "test.op"));

    // Reset the app
    limiter.ResetApp("test.app");

    // Should have tokens again
    EXPECT_TRUE(limiter.Check("test.app", "test.op"));

    return true;
}

bool Test_RateLimiterNoConfig(std::string& error_msg) {
    mosis::RateLimiter limiter;

    // Operation with no config should always succeed
    for (int i = 0; i < 100; i++) {
        EXPECT_TRUE(limiter.Check("test.app", "unconfigured.operation"));
    }

    return true;
}

//=============================================================================
// PATH SANDBOX TESTS (Milestone 4)
//=============================================================================

bool Test_PathRejectsTraversal(std::string& error_msg) {
    mosis::PathSandbox sandbox("D:/test/app");

    EXPECT_TRUE(mosis::PathSandbox::ContainsTraversal("../etc/passwd"));
    EXPECT_TRUE(mosis::PathSandbox::ContainsTraversal("foo/../../../bar"));
    EXPECT_TRUE(mosis::PathSandbox::ContainsTraversal("..\\windows\\system32"));
    EXPECT_TRUE(mosis::PathSandbox::ContainsTraversal("data/.."));
    EXPECT_TRUE(mosis::PathSandbox::ContainsTraversal(".."));

    // Should not match ".." in filenames
    EXPECT_FALSE(mosis::PathSandbox::ContainsTraversal("file..txt"));
    EXPECT_FALSE(mosis::PathSandbox::ContainsTraversal("test...name"));

    std::string canonical;
    EXPECT_FALSE(sandbox.ValidatePath("../etc/passwd", canonical));
    EXPECT_FALSE(sandbox.ValidatePath("data/../../../etc/passwd", canonical));

    return true;
}

bool Test_PathRejectsAbsolute(std::string& error_msg) {
    EXPECT_TRUE(mosis::PathSandbox::IsAbsolutePath("/etc/passwd"));
    EXPECT_TRUE(mosis::PathSandbox::IsAbsolutePath("C:\\Windows\\System32"));
    EXPECT_TRUE(mosis::PathSandbox::IsAbsolutePath("D:/test/file.txt"));
    EXPECT_TRUE(mosis::PathSandbox::IsAbsolutePath("\\\\server\\share"));
    EXPECT_TRUE(mosis::PathSandbox::IsAbsolutePath("//server/share"));

    EXPECT_FALSE(mosis::PathSandbox::IsAbsolutePath("scripts/utils.lua"));
    EXPECT_FALSE(mosis::PathSandbox::IsAbsolutePath("./data/file.txt"));
    EXPECT_FALSE(mosis::PathSandbox::IsAbsolutePath("data/config.json"));

    mosis::PathSandbox sandbox("D:/test/app");
    std::string canonical;
    EXPECT_FALSE(sandbox.ValidatePath("/etc/passwd", canonical));
    EXPECT_FALSE(sandbox.ValidatePath("C:\\Windows\\System32\\file.dll", canonical));

    return true;
}

bool Test_PathAcceptsValid(std::string& error_msg) {
    mosis::PathSandbox sandbox(GetScriptsDir());

    std::string canonical;
    EXPECT_TRUE(sandbox.ValidatePath("test_globals_removed.lua", canonical));
    EXPECT_TRUE(sandbox.ValidatePath("./test_memory_limit.lua", canonical));

    return true;
}

bool Test_ModuleNameValidation(std::string& error_msg) {
    // Valid names
    EXPECT_TRUE(mosis::PathSandbox::IsValidModuleName("utils"));
    EXPECT_TRUE(mosis::PathSandbox::IsValidModuleName("my_module"));
    EXPECT_TRUE(mosis::PathSandbox::IsValidModuleName("ui.button"));
    EXPECT_TRUE(mosis::PathSandbox::IsValidModuleName("a.b.c"));
    EXPECT_TRUE(mosis::PathSandbox::IsValidModuleName("Module123"));

    // Invalid names
    EXPECT_FALSE(mosis::PathSandbox::IsValidModuleName(""));
    EXPECT_FALSE(mosis::PathSandbox::IsValidModuleName(".utils"));
    EXPECT_FALSE(mosis::PathSandbox::IsValidModuleName("utils."));
    EXPECT_FALSE(mosis::PathSandbox::IsValidModuleName("ui..button"));
    EXPECT_FALSE(mosis::PathSandbox::IsValidModuleName("../evil"));
    EXPECT_FALSE(mosis::PathSandbox::IsValidModuleName("/etc/passwd"));
    EXPECT_FALSE(mosis::PathSandbox::IsValidModuleName("foo;bar"));
    EXPECT_FALSE(mosis::PathSandbox::IsValidModuleName("foo/bar"));
    EXPECT_FALSE(mosis::PathSandbox::IsValidModuleName("foo\\bar"));

    return true;
}

bool Test_ModuleToPath(std::string& error_msg) {
    EXPECT_TRUE(mosis::PathSandbox::ModuleToPath("utils") == "scripts/utils.lua");
    EXPECT_TRUE(mosis::PathSandbox::ModuleToPath("ui.button") == "scripts/ui/button.lua");
    EXPECT_TRUE(mosis::PathSandbox::ModuleToPath("a.b.c") == "scripts/a/b/c.lua");

    return true;
}

bool Test_SafeRequireLoads(std::string& error_msg) {
    // Create sandbox with scripts directory as app path
    // The test_module.lua is in scripts/scripts/ so after ModuleToPath
    // it becomes scripts/scripts/test_module.lua
    // Safe require is auto-registered by LuaSandbox when app_path is set
    SandboxContext ctx = TestContext();
    ctx.app_path = GetScriptsDir();  // "scripts"

    LuaSandbox sandbox(ctx);

    // Should be able to require a test module
    std::string script =
        "local m = require('test_module')\n"
        "if m.value ~= 42 then\n"
        "    error('module value mismatch')\n"
        "end\n"
        "return true\n";

    if (!sandbox.LoadString(script, "require_test")) {
        error_msg = "Failed to load module: " + sandbox.GetLastError();
        return false;
    }

    return true;
}

bool Test_SafeRequireCaches(std::string& error_msg) {
    // Safe require is auto-registered by LuaSandbox when app_path is set
    SandboxContext ctx = TestContext();
    ctx.app_path = GetScriptsDir();

    LuaSandbox sandbox(ctx);

    std::string script =
        "local m1 = require('test_module')\n"
        "local m2 = require('test_module')\n"
        "if m1 ~= m2 then\n"
        "    error('modules should be same (cached)')\n"
        "end\n"
        "return true\n";

    if (!sandbox.LoadString(script, "cache_test")) {
        error_msg = "Cache test failed: " + sandbox.GetLastError();
        return false;
    }

    return true;
}

bool Test_SafeRequireRejectsInvalid(std::string& error_msg) {
    // Safe require is auto-registered by LuaSandbox when app_path is set
    SandboxContext ctx = TestContext();
    ctx.app_path = GetScriptsDir();

    LuaSandbox sandbox(ctx);

    // Should reject path traversal in module name
    EXPECT_FALSE(sandbox.LoadString("require('../evil')", "evil_require"));

    // Should reject absolute paths
    EXPECT_FALSE(sandbox.LoadString("require('/etc/passwd')", "abs_require"));

    // Should reject special characters
    EXPECT_FALSE(sandbox.LoadString("require('foo;bar')", "special_require"));

    // Should reject empty
    EXPECT_FALSE(sandbox.LoadString("require('')", "empty_require"));

    return true;
}

//=============================================================================
// TIMER MANAGER TESTS (Milestone 5)
//=============================================================================

bool Test_SetTimeoutFires(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);
    // Manager must be declared AFTER sandbox so it's destroyed BEFORE sandbox
    mosis::TimerManager manager;

    // Setup _test table for storing state (bypasses proxy __newindex)
    SetupTestTable(sandbox.GetState());

    // Register timer API
    mosis::RegisterTimerAPI(sandbox.GetState(), &manager, ctx.app_id);

    // Set a timeout that modifies _test table
    std::string script =
        "_test.fired = false\n"
        "setTimeout(function() _test.fired = true end, 50)\n";

    if (!sandbox.LoadString(script, "timeout_test")) {
        error_msg = "Failed to set timeout: " + sandbox.GetLastError();
        return false;
    }

    // Process timers after delay
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    manager.ProcessTimers();

    // Check if callback fired
    if (!sandbox.LoadString("assert(_test.fired == true, 'callback did not fire')", "check")) {
        error_msg = "Timeout callback did not fire: " + sandbox.GetLastError();
        return false;
    }

    return true;
}

bool Test_SetIntervalFires(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);
    // Manager must be declared AFTER sandbox so it's destroyed BEFORE sandbox
    mosis::TimerManager manager;

    // Setup _test table for storing state
    SetupTestTable(sandbox.GetState());

    mosis::RegisterTimerAPI(sandbox.GetState(), &manager, ctx.app_id);

    std::string script =
        "_test.count = 0\n"
        "setInterval(function() _test.count = _test.count + 1 end, 30)\n";

    if (!sandbox.LoadString(script, "interval_test")) {
        error_msg = "Failed to set interval: " + sandbox.GetLastError();
        return false;
    }

    // Process multiple times
    for (int i = 0; i < 5; i++) {
        std::this_thread::sleep_for(std::chrono::milliseconds(40));
        manager.ProcessTimers();
    }

    // Should have fired multiple times
    if (!sandbox.LoadString("assert(_test.count >= 3, 'interval fired only ' .. _test.count .. ' times')", "check")) {
        error_msg = "Interval did not fire enough times: " + sandbox.GetLastError();
        return false;
    }

    return true;
}

bool Test_ClearTimeoutCancels(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);
    // Manager must be declared AFTER sandbox so it's destroyed BEFORE sandbox
    mosis::TimerManager manager;

    // Setup _test table for storing state
    SetupTestTable(sandbox.GetState());

    mosis::RegisterTimerAPI(sandbox.GetState(), &manager, ctx.app_id);

    std::string script =
        "_test.fired = false\n"
        "local id = setTimeout(function() _test.fired = true end, 100)\n"
        "clearTimeout(id)\n";

    if (!sandbox.LoadString(script, "clear_test")) {
        error_msg = "Failed to clear timeout: " + sandbox.GetLastError();
        return false;
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(150));
    manager.ProcessTimers();

    // Should NOT have fired
    if (!sandbox.LoadString("assert(_test.fired == false, 'callback should not have fired')", "check")) {
        error_msg = "Cancelled timeout still fired: " + sandbox.GetLastError();
        return false;
    }

    return true;
}

bool Test_ClearIntervalCancels(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);
    // Manager must be declared AFTER sandbox so it's destroyed BEFORE sandbox
    mosis::TimerManager manager;

    // Setup _test table for storing state
    SetupTestTable(sandbox.GetState());

    mosis::RegisterTimerAPI(sandbox.GetState(), &manager, ctx.app_id);

    // Store both count and interval ID in _test table so they persist across LoadString calls
    std::string script =
        "_test.count = 0\n"
        "_test.id = setInterval(function() _test.count = _test.count + 1 end, 30)\n";

    if (!sandbox.LoadString(script, "interval_setup")) {
        error_msg = "Failed to set interval: " + sandbox.GetLastError();
        return false;
    }

    // Let it fire once
    std::this_thread::sleep_for(std::chrono::milliseconds(40));
    manager.ProcessTimers();

    // Now cancel it
    sandbox.LoadString("clearInterval(_test.id)", "cancel");

    // Wait and process more
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    manager.ProcessTimers();

    // Should have fired only once (or maybe twice due to timing)
    if (!sandbox.LoadString("assert(_test.count <= 2, 'interval fired too many times: ' .. _test.count)", "check")) {
        error_msg = "Interval kept firing after cancel: " + sandbox.GetLastError();
        return false;
    }

    return true;
}

bool Test_TimerLimitEnforced(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);
    // Manager must be declared AFTER sandbox so it's destroyed BEFORE sandbox
    mosis::TimerManager manager;

    mosis::RegisterTimerAPI(sandbox.GetState(), &manager, ctx.app_id);

    // Try to create more than MAX_TIMERS_PER_APP (100) timers
    std::string script =
        "created = 0\n"
        "for i = 1, 150 do\n"
        "    local ok, err = pcall(function()\n"
        "        setTimeout(function() end, 1000000)\n"
        "    end)\n"
        "    if ok then created = created + 1 end\n"
        "end\n";

    sandbox.LoadString(script, "limit_test");

    // Should be capped at MAX_TIMERS_PER_APP
    size_t count = manager.GetTimerCount(ctx.app_id);
    if (count > 100) {
        error_msg = "Timer limit not enforced: " + std::to_string(count) + " timers created";
        return false;
    }

    return true;
}

bool Test_ClearAppTimersCleanup(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);
    // Manager must be declared AFTER sandbox so it's destroyed BEFORE sandbox
    mosis::TimerManager manager;

    mosis::RegisterTimerAPI(sandbox.GetState(), &manager, ctx.app_id);

    std::string script =
        "for i = 1, 10 do\n"
        "    setTimeout(function() end, 1000000)\n"
        "end\n";

    sandbox.LoadString(script, "cleanup_test");

    size_t before = manager.GetTimerCount(ctx.app_id);
    EXPECT_TRUE(before == 10);

    // Clear all timers for app (simulating app stop)
    manager.ClearAppTimers(ctx.app_id);

    size_t after = manager.GetTimerCount(ctx.app_id);
    EXPECT_TRUE(after == 0);

    return true;
}

bool Test_MinIntervalEnforced(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);
    // Manager must be declared AFTER sandbox so it's destroyed BEFORE sandbox
    mosis::TimerManager manager;

    // Setup _test table for storing state
    SetupTestTable(sandbox.GetState());

    mosis::RegisterTimerAPI(sandbox.GetState(), &manager, ctx.app_id);

    // Try to set interval less than minimum (10ms)
    std::string script =
        "_test.count = 0\n"
        "setInterval(function() _test.count = _test.count + 1 end, 1)\n";  // 1ms, should be clamped to 10ms

    sandbox.LoadString(script, "min_interval_test");

    // With 1ms interval, in 50ms we'd get 50 callbacks
    // With 10ms minimum, we should get ~5
    std::this_thread::sleep_for(std::chrono::milliseconds(55));
    for (int i = 0; i < 10; i++) {
        manager.ProcessTimers();
    }

    if (!sandbox.LoadString("assert(_test.count <= 10, 'interval fired too often: ' .. _test.count)", "check")) {
        error_msg = "Minimum interval not enforced: " + sandbox.GetLastError();
        return false;
    }

    return true;
}

//=============================================================================
// MILESTONE 6: JSON & CRYPTO API TESTS
//=============================================================================

bool Test_JsonDecodeValid(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);
    mosis::RegisterJsonAPI(sandbox.GetState());

    std::string script = R"(
        local obj = json.decode('{"name":"test","value":42,"arr":[1,2,3]}')
        assert(obj.name == "test", "name should be test")
        assert(obj.value == 42, "value should be 42")
        assert(#obj.arr == 3, "arr should have 3 elements")
        assert(obj.arr[1] == 1, "first element should be 1")
    )";

    if (!sandbox.LoadString(script, "decode_test")) {
        error_msg = "JSON decode failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

bool Test_JsonDecodeRejectsDeep(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::JsonLimits limits;
    limits.max_depth = 5;
    mosis::RegisterJsonAPI(sandbox.GetState(), limits);

    // Create deeply nested JSON (10 levels)
    std::string script = R"(
        local deep_json = '[[[[[[[[[[1]]]]]]]]]]'
        local result, err = json.decode(deep_json)
        assert(result == nil, 'should fail on deep nesting')
        assert(err and err:find('depth'), 'error should mention depth')
    )";

    if (!sandbox.LoadString(script, "deep_test")) {
        error_msg = "Test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

bool Test_JsonEncodeValid(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);
    mosis::RegisterJsonAPI(sandbox.GetState());

    std::string script = R"(
        local str = json.encode({name = "test", value = 42})
        assert(type(str) == "string", "should return string")
        -- Decode back to verify round-trip
        local obj = json.decode(str)
        assert(obj.name == "test", "round-trip name")
        assert(obj.value == 42, "round-trip value")
    )";

    if (!sandbox.LoadString(script, "encode_test")) {
        error_msg = "JSON encode failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

bool Test_JsonEncodeDetectsCycles(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);
    mosis::RegisterJsonAPI(sandbox.GetState());

    std::string script = R"(
        local t = {a = 1}
        t.self = t  -- Create cycle
        local result, err = json.encode(t)
        assert(result == nil, 'should fail on cycle')
        assert(err and (err:find('cycle') or err:find('circular')), 'should mention cycle')
    )";

    if (!sandbox.LoadString(script, "cycle_test")) {
        error_msg = "Test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

bool Test_JsonRejectsTooLarge(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::JsonLimits limits;
    limits.max_array_size = 10;
    mosis::RegisterJsonAPI(sandbox.GetState(), limits);

    std::string script = R"(
        -- Try to decode array with 20 elements
        local result, err = json.decode('[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]')
        assert(result == nil, 'should fail on large array')
        assert(err and (err:find('size') or err:find('limit')), 'should mention size limit')
    )";

    if (!sandbox.LoadString(script, "size_test")) {
        error_msg = "Test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

bool Test_CryptoRandomBytes(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);
    mosis::RegisterCryptoAPI(sandbox.GetState());

    std::string script = R"(
        local bytes = crypto.randomBytes(16)
        assert(#bytes == 16, 'should be 16 bytes')

        -- Should be different each time
        local bytes2 = crypto.randomBytes(16)
        assert(bytes ~= bytes2, 'should be random')
    )";

    if (!sandbox.LoadString(script, "random_test")) {
        error_msg = "Random bytes test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

bool Test_CryptoHashSHA256(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);
    mosis::RegisterCryptoAPI(sandbox.GetState());

    std::string script = R"(
        local hash = crypto.hash("sha256", "hello")
        -- Known SHA256 of "hello"
        local expected = "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
        assert(hash == expected, 'SHA256 mismatch: got ' .. hash)
    )";

    if (!sandbox.LoadString(script, "hash_test")) {
        error_msg = "Hash test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

bool Test_CryptoHMAC(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);
    mosis::RegisterCryptoAPI(sandbox.GetState());

    std::string script = R"(
        local hmac = crypto.hmac("sha256", "key", "message")
        -- Known HMAC-SHA256 of "message" with key "key"
        local expected = "6e9ef29b75fffc5b7abae527d58fdadb2fe42e7219011976917343065f58ed4a"
        assert(hmac == expected, 'HMAC mismatch: got ' .. hmac)
    )";

    if (!sandbox.LoadString(script, "hmac_test")) {
        error_msg = "HMAC test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

bool Test_SecureMathRandom(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::SecureRandom rng;
    mosis::RegisterSecureMathRandom(sandbox.GetState(), &rng);

    std::string script = R"(
        -- math.randomseed should be removed
        assert(math.randomseed == nil, 'randomseed should be removed')

        -- math.random should work
        local r1 = math.random()
        assert(r1 >= 0 and r1 < 1, 'random() should return [0,1)')

        local r2 = math.random(10)
        assert(r2 >= 1 and r2 <= 10, 'random(n) should return [1,n]')

        local r3 = math.random(5, 15)
        assert(r3 >= 5 and r3 <= 15, 'random(m,n) should return [m,n]')
    )";

    if (!sandbox.LoadString(script, "math_random_test")) {
        error_msg = "Math.random test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

//=============================================================================
// MILESTONE 7: VIRTUAL FILESYSTEM TESTS
//=============================================================================

bool Test_VirtualFSReadWrite(std::string& error_msg) {
    std::string test_root = "test_vfs_app";
    mosis::VirtualFS vfs("test.app", test_root);

    std::string err;

    // Write a file
    EXPECT_TRUE(vfs.Write("/data/test.txt", "Hello World", err));

    // Read it back
    auto content = vfs.Read("/data/test.txt", err);
    EXPECT_TRUE(content.has_value());
    EXPECT_TRUE(*content == "Hello World");

    // Cleanup
    vfs.ClearAll();
    return true;
}

bool Test_VirtualFSBlocksTraversal(std::string& error_msg) {
    std::string test_root = "test_vfs_app";
    mosis::VirtualFS vfs("test.app", test_root);

    std::string err;

    // Should reject traversal
    EXPECT_FALSE(vfs.Write("/data/../../../etc/passwd", "hack", err));
    EXPECT_TRUE(err.find("traversal") != std::string::npos ||
                err.find("invalid") != std::string::npos);

    // Should reject paths without valid prefix
    err.clear();
    EXPECT_FALSE(vfs.Write("/etc/passwd", "hack", err));

    vfs.ClearAll();
    return true;
}

bool Test_VirtualFSEnforcesQuota(std::string& error_msg) {
    std::string test_root = "test_vfs_app";
    mosis::VirtualFSLimits limits;
    limits.max_quota_bytes = 1024;  // 1 KB quota for testing
    mosis::VirtualFS vfs("test.app", test_root, limits);

    std::string err;

    // Write should succeed
    std::string small_data(500, 'a');
    EXPECT_TRUE(vfs.Write("/data/file1.txt", small_data, err));

    // Second write should fail (exceeds quota)
    std::string large_data(600, 'b');
    EXPECT_FALSE(vfs.Write("/data/file2.txt", large_data, err));
    EXPECT_TRUE(err.find("quota") != std::string::npos);

    vfs.ClearAll();
    return true;
}

bool Test_VirtualFSCleansUpTemp(std::string& error_msg) {
    std::string test_root = "test_vfs_app";
    mosis::VirtualFS vfs("test.app", test_root);

    std::string err;

    // Write to temp
    EXPECT_TRUE(vfs.Write("/temp/session.txt", "temp data", err));
    EXPECT_TRUE(vfs.Exists("/temp/session.txt"));

    // Clear temp
    vfs.ClearTemp();

    // Should be gone
    EXPECT_FALSE(vfs.Exists("/temp/session.txt"));

    vfs.ClearAll();
    return true;
}

bool Test_VirtualFSList(std::string& error_msg) {
    std::string test_root = "test_vfs_app";
    mosis::VirtualFS vfs("test.app", test_root);

    std::string err;

    // Create some files
    vfs.Write("/data/file1.txt", "content1", err);
    vfs.Write("/data/file2.txt", "content2", err);
    vfs.MakeDir("/data/subdir", err);

    // List directory
    auto files = vfs.List("/data/", err);
    EXPECT_TRUE(files.has_value());
    EXPECT_TRUE(files->size() == 3);

    vfs.ClearAll();
    return true;
}

bool Test_VirtualFSStat(std::string& error_msg) {
    std::string test_root = "test_vfs_app";
    mosis::VirtualFS vfs("test.app", test_root);

    std::string err;

    // Write a file
    vfs.Write("/data/test.txt", "Hello", err);

    // Get stat
    auto stat = vfs.Stat("/data/test.txt", err);
    EXPECT_TRUE(stat.has_value());
    EXPECT_TRUE(stat->size == 5);
    EXPECT_FALSE(stat->is_dir);

    // Directory stat
    vfs.MakeDir("/data/subdir", err);
    auto dir_stat = vfs.Stat("/data/subdir", err);
    EXPECT_TRUE(dir_stat.has_value());
    EXPECT_TRUE(dir_stat->is_dir);

    vfs.ClearAll();
    return true;
}

bool Test_VirtualFSLuaIntegration(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    std::string test_root = "test_vfs_lua";
    mosis::VirtualFS vfs("test.app", test_root);
    mosis::RegisterVirtualFS(sandbox.GetState(), &vfs);

    std::string script = R"(
        -- Write and read
        local ok, err = fs.write("/data/test.txt", "Hello from Lua")
        assert(ok, "write failed: " .. (err or ""))

        local content, err = fs.read("/data/test.txt")
        assert(content == "Hello from Lua", "content mismatch: " .. (content or "nil"))

        -- Check exists
        assert(fs.exists("/data/test.txt"), "file should exist")
        assert(not fs.exists("/data/nonexistent.txt"), "file should not exist")

        -- Stat
        local stat = fs.stat("/data/test.txt")
        assert(stat.size == 14, "size should be 14, got " .. tostring(stat.size))
        assert(not stat.isDir, "should not be dir")
    )";

    bool ok = sandbox.LoadString(script, "vfs_test");
    vfs.ClearAll();

    if (!ok) {
        error_msg = "Lua test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

bool Test_VirtualFSMaxFileSize(std::string& error_msg) {
    std::string test_root = "test_vfs_app";
    mosis::VirtualFSLimits limits;
    limits.max_file_size = 100;  // 100 bytes max
    limits.max_quota_bytes = 10000;  // Large quota
    mosis::VirtualFS vfs("test.app", test_root, limits);

    std::string err;

    // Small file should succeed
    EXPECT_TRUE(vfs.Write("/data/small.txt", std::string(50, 'a'), err));

    // Large file should fail
    EXPECT_FALSE(vfs.Write("/data/large.txt", std::string(200, 'b'), err));
    EXPECT_TRUE(err.find("size") != std::string::npos);

    vfs.ClearAll();
    return true;
}

//=============================================================================
// MILESTONE 8: SQLITE DATABASE TESTS
//=============================================================================

bool Test_DatabaseCreatesTables(std::string& error_msg) {
    std::string test_root = "test_db_app";
    mosis::DatabaseManager manager("test.app", test_root);

    std::string err;
    auto db = manager.Open("test", err);
    EXPECT_TRUE(db != nullptr);

    // Create table
    EXPECT_TRUE(db->Execute(
        "CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY, name TEXT)",
        {}, err));

    // Insert
    EXPECT_TRUE(db->Execute(
        "INSERT INTO items (name) VALUES (?)",
        {std::string("Test Item")}, err));

    // Query
    auto rows = db->Query("SELECT * FROM items", {}, err);
    EXPECT_TRUE(rows.has_value());
    EXPECT_TRUE(rows->size() == 1);

    db->Close();
    manager.CloseAll();

    // Cleanup
    std::filesystem::remove_all(test_root);
    return true;
}

bool Test_DatabasePreparedStatements(std::string& error_msg) {
    std::string test_root = "test_db_app";
    mosis::DatabaseManager manager("test.app", test_root);

    std::string err;
    auto db = manager.Open("test", err);
    EXPECT_TRUE(db != nullptr);

    db->Execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)", {}, err);
    db->Execute("INSERT INTO users (name) VALUES (?)", {std::string("Alice")}, err);

    // Attempt SQL injection via parameter - should be safely escaped
    std::string malicious = "'; DROP TABLE users; --";
    auto rows = db->Query("SELECT * FROM users WHERE name = ?", {malicious}, err);

    // Query should succeed (finding nothing) and table should still exist
    EXPECT_TRUE(rows.has_value());
    EXPECT_TRUE(rows->size() == 0);

    // Table should still exist
    auto check = db->Query("SELECT * FROM users", {}, err);
    EXPECT_TRUE(check.has_value());
    EXPECT_TRUE(check->size() == 1);

    db->Close();
    std::filesystem::remove_all(test_root);
    return true;
}

bool Test_DatabaseBlocksAttach(std::string& error_msg) {
    std::string test_root = "test_db_app";
    mosis::DatabaseManager manager("test.app", test_root);

    std::string err;
    auto db = manager.Open("test", err);
    EXPECT_TRUE(db != nullptr);

    // Try to attach another database - should fail
    EXPECT_FALSE(db->Execute("ATTACH DATABASE '/etc/passwd' AS evil", {}, err));
    EXPECT_TRUE(err.find("authorized") != std::string::npos ||
                err.find("denied") != std::string::npos ||
                err.find("not authorized") != std::string::npos);

    db->Close();
    std::filesystem::remove_all(test_root);
    return true;
}

bool Test_DatabaseBlocksDangerousPragma(std::string& error_msg) {
    std::string test_root = "test_db_app";
    mosis::DatabaseManager manager("test.app", test_root);

    std::string err;
    auto db = manager.Open("test", err);
    EXPECT_TRUE(db != nullptr);

    // Try dangerous PRAGMAs - should fail
    EXPECT_FALSE(db->Execute("PRAGMA journal_mode = OFF", {}, err));
    err.clear();
    EXPECT_FALSE(db->Execute("PRAGMA synchronous = OFF", {}, err));

    // Safe PRAGMAs should work
    err.clear();
    auto rows = db->Query("PRAGMA table_info(sqlite_master)", {}, err);
    EXPECT_TRUE(rows.has_value());

    db->Close();
    std::filesystem::remove_all(test_root);
    return true;
}

bool Test_DatabaseMultiple(std::string& error_msg) {
    std::string test_root = "test_db_app";
    mosis::DatabaseManager manager("test.app", test_root);

    std::string err;
    auto db1 = manager.Open("db1", err);
    auto db2 = manager.Open("db2", err);

    EXPECT_TRUE(db1 != nullptr);
    EXPECT_TRUE(db2 != nullptr);
    EXPECT_TRUE(manager.GetOpenDatabaseCount() == 2);

    // They should be independent
    db1->Execute("CREATE TABLE t1 (x INTEGER)", {}, err);
    db2->Execute("CREATE TABLE t2 (y INTEGER)", {}, err);

    // t1 shouldn't exist in db2
    auto rows = db2->Query("SELECT * FROM t1", {}, err);
    EXPECT_FALSE(rows.has_value());  // Should fail - table doesn't exist

    manager.CloseAll();
    std::filesystem::remove_all(test_root);
    return true;
}

bool Test_DatabaseLuaIntegration(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    std::string test_root = "test_db_lua";
    mosis::DatabaseManager manager("test.app", test_root);
    mosis::RegisterDatabaseAPI(sandbox.GetState(), &manager);

    std::string script = R"lua(
        -- Just test if database global exists
        if not database then
            error("database global not found")
        end
        if not database.open then
            error("database.open not found")
        end
    )lua";

    bool ok = sandbox.LoadString(script, "db_test");
    manager.CloseAll();
    std::filesystem::remove_all(test_root);

    if (!ok) {
        error_msg = "Lua test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

bool Test_DatabaseInvalidNames(std::string& error_msg) {
    std::string test_root = "test_db_app";
    mosis::DatabaseManager manager("test.app", test_root);

    std::string err;

    // Path traversal
    auto db1 = manager.Open("../evil", err);
    EXPECT_TRUE(db1 == nullptr);

    // Absolute path component
    err.clear();
    auto db2 = manager.Open("/etc/passwd", err);
    EXPECT_TRUE(db2 == nullptr);

    // Special characters
    err.clear();
    auto db3 = manager.Open("test;drop", err);
    EXPECT_TRUE(db3 == nullptr);

    std::filesystem::remove_all(test_root);
    return true;
}

bool Test_DatabaseLastInsertAndChanges(std::string& error_msg) {
    std::string test_root = "test_db_app";
    mosis::DatabaseManager manager("test.app", test_root);

    std::string err;
    auto db = manager.Open("test", err);
    EXPECT_TRUE(db != nullptr);

    db->Execute("CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT)", {}, err);

    db->Execute("INSERT INTO items (name) VALUES (?)", {std::string("Item 1")}, err);
    EXPECT_TRUE(db->GetLastInsertRowId() == 1);
    EXPECT_TRUE(db->GetChanges() == 1);

    db->Execute("INSERT INTO items (name) VALUES (?)", {std::string("Item 2")}, err);
    EXPECT_TRUE(db->GetLastInsertRowId() == 2);

    db->Execute("UPDATE items SET name = ?", {std::string("Updated")}, err);
    EXPECT_TRUE(db->GetChanges() == 2);  // Updated 2 rows

    db->Close();
    std::filesystem::remove_all(test_root);
    return true;
}

//=============================================================================
// MILESTONE 9: NETWORK HTTP TESTS
//=============================================================================

bool Test_NetworkBlocksPrivateIP(std::string& error_msg) {
    mosis::NetworkManager manager("test.app");
    manager.ClearDomainRestrictions();

    std::string err;

    // All these should be blocked
    std::vector<std::string> private_urls = {
        "https://127.0.0.1/api",
        "https://10.0.0.1/api",
        "https://192.168.1.1/api",
        "https://172.16.0.1/api",
        "https://169.254.169.254/latest/meta-data/",
        "https://localhost/api",
        "https://0.0.0.0/api"
    };

    for (const auto& url : private_urls) {
        mosis::HttpRequest req;
        req.url = url;
        auto response = manager.Request(req, err);
        if (response.status_code != 0 || err.empty()) {
            error_msg = "Expected " + url + " to be blocked, but it wasn't";
            return false;
        }
        err.clear();
    }

    return true;
}

bool Test_NetworkBlocksPlainHttp(std::string& error_msg) {
    mosis::NetworkManager manager("test.app");
    manager.ClearDomainRestrictions();

    std::string err;
    mosis::HttpRequest req;
    req.url = "http://example.com/api";  // No HTTPS

    auto response = manager.Request(req, err);
    EXPECT_TRUE(response.status_code == 0);
    EXPECT_TRUE(err.find("HTTPS") != std::string::npos);

    return true;
}

bool Test_NetworkRequiresHttps(std::string& error_msg) {
    mosis::HttpValidator validator;
    std::string err;

    // HTTPS should validate
    auto parsed = validator.Validate("https://example.com/api", err);
    EXPECT_TRUE(parsed.has_value());
    EXPECT_TRUE(parsed->scheme == "https");

    // HTTP should fail validation
    err.clear();
    parsed = validator.Validate("http://example.com/api", err);
    EXPECT_FALSE(parsed.has_value());
    EXPECT_TRUE(err.find("HTTPS") != std::string::npos);

    return true;
}

bool Test_NetworkEnforcesDomainWhitelist(std::string& error_msg) {
    mosis::NetworkManager manager("test.app");

    // Set allowed domains
    manager.SetAllowedDomains({"api.example.com", "cdn.example.com"});

    std::string err;

    // Allowed domain should validate
    auto parsed = manager.GetValidator().Validate("https://api.example.com/data", err);
    EXPECT_TRUE(parsed.has_value());

    // Disallowed domain should fail
    err.clear();
    parsed = manager.GetValidator().Validate("https://evil.com/steal", err);
    EXPECT_FALSE(parsed.has_value());
    EXPECT_TRUE(err.find("allowed") != std::string::npos ||
                err.find("whitelist") != std::string::npos ||
                err.find("not in allowed") != std::string::npos);

    return true;
}

bool Test_NetworkUrlParsing(std::string& error_msg) {
    mosis::HttpValidator validator;
    std::string err;

    // Full URL with port
    auto parsed = validator.Validate("https://api.example.com:8443/path/to/resource?key=value", err);
    EXPECT_TRUE(parsed.has_value());
    EXPECT_TRUE(parsed->scheme == "https");
    EXPECT_TRUE(parsed->host == "api.example.com");
    EXPECT_TRUE(parsed->port == 8443);
    EXPECT_TRUE(parsed->path == "/path/to/resource");
    EXPECT_TRUE(parsed->query == "?key=value");

    // Default port
    err.clear();
    parsed = validator.Validate("https://example.com/api", err);
    EXPECT_TRUE(parsed.has_value());
    EXPECT_TRUE(parsed->port == 443);

    // IP address
    err.clear();
    parsed = validator.Validate("https://192.0.2.1/api", err);  // TEST-NET-1, documentation IP
    EXPECT_TRUE(parsed.has_value());
    EXPECT_TRUE(parsed->is_ip_address);

    return true;
}

bool Test_NetworkBlocksMetadata(std::string& error_msg) {
    mosis::NetworkManager manager("test.app");
    manager.ClearDomainRestrictions();

    std::string err;

    // AWS metadata
    mosis::HttpRequest req;
    req.url = "https://169.254.169.254/latest/meta-data/";
    auto response = manager.Request(req, err);
    EXPECT_TRUE(response.status_code == 0);

    // GCP metadata hostname
    err.clear();
    req.url = "https://metadata.google.internal/computeMetadata/v1/";
    response = manager.Request(req, err);
    EXPECT_TRUE(response.status_code == 0);

    return true;
}

bool Test_NetworkRequestLimits(std::string& error_msg) {
    mosis::NetworkLimits limits;
    limits.max_request_body = 1024;  // 1 KB for testing

    mosis::NetworkManager manager("test.app", limits);
    manager.ClearDomainRestrictions();

    std::string err;
    mosis::HttpRequest req;
    req.url = "https://example.com/api";
    req.method = "POST";
    req.body = std::string(2048, 'X');  // 2 KB - exceeds limit

    auto response = manager.Request(req, err);
    EXPECT_TRUE(response.status_code == 0);
    EXPECT_TRUE(err.find("size") != std::string::npos ||
                err.find("limit") != std::string::npos ||
                err.find("large") != std::string::npos);

    return true;
}

bool Test_NetworkLuaIntegration(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::NetworkManager manager("test.app");
    manager.ClearDomainRestrictions();
    mosis::RegisterNetworkAPI(sandbox.GetState(), &manager);

    std::string script = R"lua(
        -- Test that network global exists
        if not network then
            error("network global not found")
        end
        if not network.request then
            error("network.request not found")
        end

        -- Test validation rejection (private IP)
        local response, err = network.request({
            url = "https://127.0.0.1/api"
        })
        if response then
            error("expected private IP to be blocked")
        end
    )lua";

    bool ok = sandbox.LoadString(script, "network_test");
    if (!ok) {
        error_msg = "Lua test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

//=============================================================================
// MILESTONE 10: WebSocket
//=============================================================================

bool Test_WebSocketUrlValidation(std::string& error_msg) {
    mosis::WebSocketManager manager("test.app");
    manager.ClearDomainRestrictions();

    std::string err;

    // WSS should be allowed (will fail in mock mode but validation passes)
    auto ws = manager.Connect("wss://example.com/socket", err);
    EXPECT_TRUE(err.find("mock") != std::string::npos ||
                err.find("disabled") != std::string::npos);

    // WS (plain) should be blocked at validation
    ws = manager.Connect("ws://example.com/socket", err);
    EXPECT_TRUE(ws == nullptr);
    EXPECT_TRUE(err.find("WSS") != std::string::npos ||
                err.find("HTTPS") != std::string::npos ||
                err.find("required") != std::string::npos);

    return true;
}

bool Test_WebSocketConnectionLimits(std::string& error_msg) {
    mosis::WebSocketLimits limits;
    limits.max_connections_per_app = 2;

    mosis::WebSocketManager manager("test.app", limits);
    manager.ClearDomainRestrictions();
    manager.SetMockMode(false);  // Disable mock to test connection tracking

    std::string err;

    // Create max connections
    auto ws1 = manager.Connect("wss://example.com/socket1", err);
    auto ws2 = manager.Connect("wss://example.com/socket2", err);

    // Third should fail
    auto ws3 = manager.Connect("wss://example.com/socket3", err);
    EXPECT_TRUE(ws3 == nullptr);
    EXPECT_TRUE(err.find("limit") != std::string::npos ||
                err.find("Connection") != std::string::npos);

    return true;
}

bool Test_WebSocketBlocksPrivateIP(std::string& error_msg) {
    mosis::WebSocketManager manager("test.app");
    manager.ClearDomainRestrictions();

    std::string err;

    std::vector<std::string> private_urls = {
        "wss://127.0.0.1/socket",
        "wss://localhost/socket",
        "wss://10.0.0.1/socket",
        "wss://192.168.1.1/socket",
        "wss://169.254.169.254/socket"
    };

    for (const auto& url : private_urls) {
        auto ws = manager.Connect(url, err);
        EXPECT_TRUE(ws == nullptr);
    }

    return true;
}

bool Test_WebSocketDomainWhitelist(std::string& error_msg) {
    mosis::WebSocketManager manager("test.app");
    manager.SetAllowedDomains({"api.example.com"});

    std::string err;

    // Allowed domain - should pass validation (may fail in mock mode for other reasons)
    auto ws1 = manager.Connect("wss://api.example.com/socket", err);
    bool allowed_passed = (ws1 != nullptr) ||
                          (err.find("mock") != std::string::npos) ||
                          (err.find("disabled") != std::string::npos);
    EXPECT_TRUE(allowed_passed);

    // Disallowed domain - should fail validation
    auto ws2 = manager.Connect("wss://evil.com/socket", err);
    EXPECT_TRUE(ws2 == nullptr);
    EXPECT_TRUE(err.find("allowed") != std::string::npos ||
                err.find("whitelist") != std::string::npos ||
                err.find("Domain") != std::string::npos);

    return true;
}

bool Test_WebSocketMessageLimits(std::string& error_msg) {
    // Create a WebSocket directly to test send limits
    mosis::WebSocket ws(1, "wss://example.com/socket", 1024);  // 1 KB limit

    // WebSocket is in Connecting state, so send should fail
    std::string small_message(512, 'X');
    bool send_result = ws.Send(small_message);
    EXPECT_FALSE(send_result);  // Not connected

    // Simulate open
    ws.SimulateOpen();

    // Now small message should work
    send_result = ws.Send(small_message);
    EXPECT_TRUE(send_result);

    // Large message should fail
    std::string large_message(2048, 'X');  // 2 KB
    send_result = ws.Send(large_message);
    EXPECT_FALSE(send_result);

    return true;
}

bool Test_WebSocketCloseAll(std::string& error_msg) {
    mosis::WebSocketManager manager("test.app");
    manager.ClearDomainRestrictions();
    manager.SetMockMode(false);  // Disable mock to track connections

    std::string err;

    // Create some connections
    manager.Connect("wss://example.com/socket1", err);
    manager.Connect("wss://example.com/socket2", err);

    EXPECT_TRUE(manager.GetActiveConnectionCount() == 2);

    // Close all
    manager.CloseAll();

    // Should have no active connections
    EXPECT_TRUE(manager.GetActiveConnectionCount() == 0);

    return true;
}

bool Test_WebSocketLuaIntegration(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::WebSocketManager manager("test.app");
    manager.ClearDomainRestrictions();
    mosis::RegisterWebSocketAPI(sandbox.GetState(), &manager);

    std::string script = R"lua(
        -- Test that network.websocket exists
        if not network then
            error("network global not found")
        end
        if not network.websocket then
            error("network.websocket not found")
        end

        -- Test validation rejection (private IP)
        local ws, err = network.websocket("wss://127.0.0.1/socket")
        if ws then
            error("expected private IP to be blocked")
        end
    )lua";

    bool ok = sandbox.LoadString(script, "websocket_test");
    if (!ok) {
        error_msg = "Lua test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

//=============================================================================
// MILESTONE 11: Camera
//=============================================================================

bool Test_CameraRequiresPermission(std::string& error_msg) {
    // Create sandbox context WITHOUT camera permission
    SandboxContext ctx;
    ctx.app_id = "test.app";
    ctx.permissions = {};  // No camera permission declared
    ctx.is_system_app = false;
    PermissionGate permissions(ctx);

    mosis::CameraInterface camera("test.app", &permissions);
    camera.SimulateUserGesture();

    std::string err;
    mosis::CameraConfig config;
    auto session = camera.StartSession(config, err);

    EXPECT_TRUE(session == nullptr);
    EXPECT_TRUE(err.find("permission") != std::string::npos);

    return true;
}

bool Test_CameraRequiresUserGesture(std::string& error_msg) {
    // Create sandbox context WITH camera permission
    SandboxContext ctx;
    ctx.app_id = "test.app";
    ctx.permissions = {"camera"};
    ctx.is_system_app = false;
    PermissionGate permissions(ctx);
    permissions.GrantPermission("camera");

    mosis::CameraInterface camera("test.app", &permissions);
    // Note: NOT calling SimulateUserGesture()

    std::string err;
    mosis::CameraConfig config;
    auto session = camera.StartSession(config, err);

    EXPECT_TRUE(session == nullptr);
    EXPECT_TRUE(err.find("gesture") != std::string::npos);

    return true;
}

bool Test_CameraShowsIndicator(std::string& error_msg) {
    SandboxContext ctx;
    ctx.app_id = "test.app";
    ctx.permissions = {"camera"};
    ctx.is_system_app = false;
    PermissionGate permissions(ctx);
    permissions.GrantPermission("camera");

    mosis::CameraInterface camera("test.app", &permissions);
    camera.SimulateUserGesture();

    EXPECT_FALSE(camera.IsIndicatorVisible());

    std::string err;
    mosis::CameraConfig config;
    auto session = camera.StartSession(config, err);

    EXPECT_TRUE(session != nullptr);
    EXPECT_TRUE(camera.IsIndicatorVisible());

    camera.StopSession();

    EXPECT_FALSE(camera.IsIndicatorVisible());

    return true;
}

bool Test_CameraSingleSession(std::string& error_msg) {
    SandboxContext ctx;
    ctx.app_id = "test.app";
    ctx.permissions = {"camera"};
    ctx.is_system_app = false;
    PermissionGate permissions(ctx);
    permissions.GrantPermission("camera");

    mosis::CameraInterface camera("test.app", &permissions);
    camera.SimulateUserGesture();

    std::string err;
    mosis::CameraConfig config;

    // First session should succeed
    auto session1 = camera.StartSession(config, err);
    EXPECT_TRUE(session1 != nullptr);

    // Second session should fail
    camera.SimulateUserGesture();
    auto session2 = camera.StartSession(config, err);
    EXPECT_TRUE(session2 == nullptr);
    EXPECT_TRUE(err.find("active") != std::string::npos ||
                err.find("already") != std::string::npos);

    return true;
}

bool Test_CameraStopsOnShutdown(std::string& error_msg) {
    SandboxContext ctx;
    ctx.app_id = "test.app";
    ctx.permissions = {"camera"};
    ctx.is_system_app = false;
    PermissionGate permissions(ctx);
    permissions.GrantPermission("camera");

    mosis::CameraInterface camera("test.app", &permissions);
    camera.SimulateUserGesture();

    std::string err;
    mosis::CameraConfig config;
    auto session = camera.StartSession(config, err);

    EXPECT_TRUE(session != nullptr);
    EXPECT_TRUE(camera.HasActiveSession());

    // Simulate app stop
    camera.Shutdown();

    EXPECT_FALSE(camera.HasActiveSession());
    EXPECT_FALSE(camera.IsIndicatorVisible());

    return true;
}

bool Test_CameraLuaIntegration(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    ctx.permissions = {"camera"};
    LuaSandbox sandbox(ctx);

    PermissionGate permissions(ctx);
    permissions.GrantPermission("camera");

    mosis::CameraInterface camera("test.app", &permissions);
    camera.SimulateUserGesture();
    mosis::RegisterCameraAPI(sandbox.GetState(), &camera);

    std::string script = R"lua(
        -- Test that camera global exists
        if not camera then
            error("camera global not found")
        end
        if not camera.start then
            error("camera.start not found")
        end
        if not camera.isActive then
            error("camera.isActive not found")
        end

        -- isActive should be false initially
        if camera.isActive() then
            error("camera should not be active initially")
        end
    )lua";

    bool ok = sandbox.LoadString(script, "camera_test");
    if (!ok) {
        error_msg = "Lua test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

//=============================================================================
// MILESTONE 12: Microphone
//=============================================================================

bool Test_MicrophoneRequiresPermission(std::string& error_msg) {
    // Create sandbox context WITHOUT microphone permission
    SandboxContext ctx;
    ctx.app_id = "test.app";
    ctx.permissions = {};  // No microphone permission declared
    ctx.is_system_app = false;
    PermissionGate permissions(ctx);

    mosis::MicrophoneInterface mic("test.app", &permissions);
    mic.SimulateUserGesture();

    std::string err;
    mosis::RecordingConfig config;
    auto session = mic.StartSession(config, err);

    EXPECT_TRUE(session == nullptr);
    EXPECT_TRUE(err.find("permission") != std::string::npos);

    return true;
}

bool Test_MicrophoneRequiresUserGesture(std::string& error_msg) {
    // Create sandbox context WITH microphone permission
    SandboxContext ctx;
    ctx.app_id = "test.app";
    ctx.permissions = {"microphone"};
    ctx.is_system_app = false;
    PermissionGate permissions(ctx);
    permissions.GrantPermission("microphone");

    mosis::MicrophoneInterface mic("test.app", &permissions);
    // Note: NOT calling SimulateUserGesture()

    std::string err;
    mosis::RecordingConfig config;
    auto session = mic.StartSession(config, err);

    EXPECT_TRUE(session == nullptr);
    EXPECT_TRUE(err.find("gesture") != std::string::npos);

    return true;
}

bool Test_MicrophoneShowsIndicator(std::string& error_msg) {
    SandboxContext ctx;
    ctx.app_id = "test.app";
    ctx.permissions = {"microphone"};
    ctx.is_system_app = false;
    PermissionGate permissions(ctx);
    permissions.GrantPermission("microphone");

    mosis::MicrophoneInterface mic("test.app", &permissions);
    mic.SimulateUserGesture();

    EXPECT_FALSE(mic.IsIndicatorVisible());

    std::string err;
    mosis::RecordingConfig config;
    auto session = mic.StartSession(config, err);

    EXPECT_TRUE(session != nullptr);
    EXPECT_TRUE(mic.IsIndicatorVisible());

    mic.StopSession();

    EXPECT_FALSE(mic.IsIndicatorVisible());

    return true;
}

bool Test_MicrophoneSingleSession(std::string& error_msg) {
    SandboxContext ctx;
    ctx.app_id = "test.app";
    ctx.permissions = {"microphone"};
    ctx.is_system_app = false;
    PermissionGate permissions(ctx);
    permissions.GrantPermission("microphone");

    mosis::MicrophoneInterface mic("test.app", &permissions);
    mic.SimulateUserGesture();

    std::string err;
    mosis::RecordingConfig config;

    // First session should succeed
    auto session1 = mic.StartSession(config, err);
    EXPECT_TRUE(session1 != nullptr);

    // Second session should fail
    mic.SimulateUserGesture();
    auto session2 = mic.StartSession(config, err);
    EXPECT_TRUE(session2 == nullptr);
    EXPECT_TRUE(err.find("active") != std::string::npos ||
                err.find("already") != std::string::npos);

    return true;
}

bool Test_MicrophoneStopsOnShutdown(std::string& error_msg) {
    SandboxContext ctx;
    ctx.app_id = "test.app";
    ctx.permissions = {"microphone"};
    ctx.is_system_app = false;
    PermissionGate permissions(ctx);
    permissions.GrantPermission("microphone");

    mosis::MicrophoneInterface mic("test.app", &permissions);
    mic.SimulateUserGesture();

    std::string err;
    mosis::RecordingConfig config;
    auto session = mic.StartSession(config, err);

    EXPECT_TRUE(session != nullptr);
    EXPECT_TRUE(mic.HasActiveSession());

    // Simulate app stop
    mic.Shutdown();

    EXPECT_FALSE(mic.HasActiveSession());
    EXPECT_FALSE(mic.IsIndicatorVisible());

    return true;
}

bool Test_MicrophoneLuaIntegration(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    ctx.permissions = {"microphone"};
    LuaSandbox sandbox(ctx);

    PermissionGate permissions(ctx);
    permissions.GrantPermission("microphone");

    mosis::MicrophoneInterface mic("test.app", &permissions);
    mic.SimulateUserGesture();
    mosis::RegisterMicrophoneAPI(sandbox.GetState(), &mic);

    std::string script = R"lua(
        -- Test that microphone global exists
        if not microphone then
            error("microphone global not found")
        end
        if not microphone.start then
            error("microphone.start not found")
        end
        if not microphone.isActive then
            error("microphone.isActive not found")
        end

        -- isActive should be false initially
        if microphone.isActive() then
            error("microphone should not be active initially")
        end
    )lua";

    bool ok = sandbox.LoadString(script, "microphone_test");
    if (!ok) {
        error_msg = "Lua test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

//=============================================================================
// MILESTONE 13: Audio Output
//=============================================================================

bool Test_AudioPlaysSound(std::string& error_msg) {
    mosis::AudioOutputInterface audio("test.app");

    mosis::AudioData data;
    data.sample_rate = 44100;
    data.channels = 1;
    data.bits_per_sample = 16;
    data.data = std::vector<uint8_t>(1000);  // Dummy data

    mosis::PlaybackConfig config;
    std::string err;
    auto player = audio.Play(data, config, err);

    EXPECT_TRUE(player != nullptr);
    EXPECT_TRUE(player->GetState() == mosis::AudioState::Playing);

    return true;
}

bool Test_AudioRespectsSystemVolume(std::string& error_msg) {
    mosis::AudioOutputInterface audio("test.app");
    audio.SetSystemVolume(0.5f);

    mosis::AudioData data;
    data.data = std::vector<uint8_t>(1000);

    mosis::PlaybackConfig config;
    config.volume = 1.0f;  // App requests full volume

    std::string err;
    auto player = audio.Play(data, config, err);

    // Player is created with requested volume
    EXPECT_TRUE(player != nullptr);
    EXPECT_TRUE(player->GetVolume() == 1.0f);

    // But effective volume is capped by system
    // (In real implementation, audio subsystem applies this)

    return true;
}

bool Test_AudioLimitsConcurrent(std::string& error_msg) {
    mosis::AudioOutputInterface audio("test.app");

    mosis::AudioData data;
    data.data = std::vector<uint8_t>(1000);
    mosis::PlaybackConfig config;

    // Create MAX_CONCURRENT_SOUNDS players
    std::vector<std::shared_ptr<mosis::SoundPlayer>> players;
    for (int i = 0; i < 10; i++) {
        std::string err;
        auto player = audio.Play(data, config, err);
        EXPECT_TRUE(player != nullptr);
        players.push_back(player);
    }

    // 11th should fail
    std::string err;
    auto extra = audio.Play(data, config, err);
    EXPECT_TRUE(extra == nullptr);
    EXPECT_TRUE(err.find("limit") != std::string::npos ||
                err.find("concurrent") != std::string::npos);

    return true;
}

bool Test_AudioStopAll(std::string& error_msg) {
    mosis::AudioOutputInterface audio("test.app");

    mosis::AudioData data;
    data.data = std::vector<uint8_t>(1000);
    mosis::PlaybackConfig config;

    std::string err;
    auto player1 = audio.Play(data, config, err);
    auto player2 = audio.Play(data, config, err);

    EXPECT_TRUE(audio.GetActivePlayerCount() == 2);

    audio.StopAll();

    EXPECT_TRUE(audio.GetActivePlayerCount() == 0);

    return true;
}

bool Test_AudioStopsOnShutdown(std::string& error_msg) {
    mosis::AudioOutputInterface audio("test.app");

    mosis::AudioData data;
    data.data = std::vector<uint8_t>(1000);
    mosis::PlaybackConfig config;

    std::string err;
    auto player = audio.Play(data, config, err);
    EXPECT_TRUE(player != nullptr);

    // Simulate app stop
    audio.Shutdown();

    EXPECT_TRUE(audio.GetActivePlayerCount() == 0);

    return true;
}

bool Test_AudioLuaIntegration(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::AudioOutputInterface audio("test.app");
    mosis::RegisterAudioAPI(sandbox.GetState(), &audio);

    std::string script = R"lua(
        -- Test that audio global exists
        if not audio then
            error("audio global not found")
        end
        if not audio.play then
            error("audio.play not found")
        end
        if not audio.stopAll then
            error("audio.stopAll not found")
        end
        if not audio.getActiveCount then
            error("audio.getActiveCount not found")
        end

        -- Active count should be 0 initially
        if audio.getActiveCount() ~= 0 then
            error("should have no active sounds initially")
        end
    )lua";

    bool ok = sandbox.LoadString(script, "audio_test");
    if (!ok) {
        error_msg = "Lua test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

//=============================================================================
// MILESTONE 14: Location
//=============================================================================

bool Test_LocationRequiresPermission(std::string& error_msg) {
    mosis::LocationInterface location("test.app");
    // No permissions granted

    mosis::LocationOptions options;
    std::string err;
    bool started = location.GetCurrentPosition(
        options,
        [](const mosis::Position&) {},
        [](mosis::LocationError, const std::string&) {},
        err
    );

    EXPECT_TRUE(!started);
    EXPECT_TRUE(err.find("permission") != std::string::npos);

    return true;
}

bool Test_LocationCoarseReducesPrecision(std::string& error_msg) {
    mosis::LocationInterface location("test.app");
    location.SetCoarsePermission(true);  // Only coarse, not fine

    // Set mock position with high precision
    mosis::Position precise;
    precise.latitude = 37.7749295;
    precise.longitude = -122.4194155;
    precise.accuracy = 5.0;
    precise.altitude = 100.0;
    location.SetMockPosition(precise);

    bool got_position = false;
    mosis::Position received;

    mosis::LocationOptions options;
    std::string err;
    location.GetCurrentPosition(
        options,
        [&](const mosis::Position& pos) {
            got_position = true;
            received = pos;
        },
        [](mosis::LocationError, const std::string&) {},
        err
    );

    EXPECT_TRUE(got_position);
    // Should be rounded to ~0.01 degrees
    EXPECT_TRUE(std::abs(received.latitude - 37.77) < 0.001);
    EXPECT_TRUE(std::abs(received.longitude - (-122.42)) < 0.001);
    // Accuracy should be at least 1000m
    EXPECT_TRUE(received.accuracy >= 1000.0);
    // Fine details should be zeroed
    EXPECT_TRUE(received.altitude == 0.0);

    return true;
}

bool Test_LocationRateLimits(std::string& error_msg) {
    mosis::LocationInterface location("test.app");
    location.SetCoarsePermission(true);

    mosis::Position pos;
    pos.latitude = 37.77;
    pos.longitude = -122.42;
    location.SetMockPosition(pos);

    mosis::LocationOptions options;

    // First request should succeed
    std::string err;
    bool ok1 = location.GetCurrentPosition(
        options,
        [](const mosis::Position&) {},
        [](mosis::LocationError, const std::string&) {},
        err
    );
    EXPECT_TRUE(ok1);

    // Immediate second request should be rate limited
    bool ok2 = location.GetCurrentPosition(
        options,
        [](const mosis::Position&) {},
        [](mosis::LocationError, const std::string&) {},
        err
    );
    EXPECT_TRUE(!ok2);
    EXPECT_TRUE(err.find("rate") != std::string::npos ||
                err.find("limit") != std::string::npos);

    return true;
}

bool Test_LocationWatchPosition(std::string& error_msg) {
    mosis::LocationInterface location("test.app");
    location.SetFinePermission(true);

    std::string err;
    auto watch = location.WatchPosition({}, err);

    EXPECT_TRUE(watch != nullptr);
    EXPECT_TRUE(watch->IsActive());
    EXPECT_TRUE(location.GetActiveWatchCount() == 1);

    watch->Stop();
    EXPECT_TRUE(!watch->IsActive());

    return true;
}

bool Test_LocationWatchLimit(std::string& error_msg) {
    mosis::LocationInterface location("test.app");
    location.SetCoarsePermission(true);

    std::vector<std::shared_ptr<mosis::LocationWatch>> watches;
    std::string err;

    // Create MAX_WATCHES (5) watches
    for (int i = 0; i < 5; i++) {
        auto watch = location.WatchPosition({}, err);
        EXPECT_TRUE(watch != nullptr);
        watches.push_back(watch);
    }

    // 6th should fail
    auto extra = location.WatchPosition({}, err);
    EXPECT_TRUE(extra == nullptr);
    EXPECT_TRUE(err.find("limit") != std::string::npos ||
                err.find("maximum") != std::string::npos);

    return true;
}

bool Test_LocationCleansUpOnShutdown(std::string& error_msg) {
    mosis::LocationInterface location("test.app");
    location.SetCoarsePermission(true);

    std::string err;
    auto watch1 = location.WatchPosition({}, err);
    auto watch2 = location.WatchPosition({}, err);

    EXPECT_TRUE(location.GetActiveWatchCount() == 2);

    location.Shutdown();

    EXPECT_TRUE(location.GetActiveWatchCount() == 0);
    EXPECT_TRUE(!watch1->IsActive());
    EXPECT_TRUE(!watch2->IsActive());

    return true;
}

bool Test_LocationLuaIntegration(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::LocationInterface location("test.app");
    mosis::RegisterLocationAPI(sandbox.GetState(), &location);

    std::string script = R"lua(
        -- Test that location global exists
        if not location then
            error("location global not found")
        end
        if not location.getCurrentPosition then
            error("location.getCurrentPosition not found")
        end
        if not location.watchPosition then
            error("location.watchPosition not found")
        end
        if not location.clearWatch then
            error("location.clearWatch not found")
        end
        if not location.getWatchCount then
            error("location.getWatchCount not found")
        end

        -- Watch count should be 0 initially
        if location.getWatchCount() ~= 0 then
            error("should have no active watches initially")
        end
    )lua";

    bool ok = sandbox.LoadString(script, "location_test");
    if (!ok) {
        error_msg = "Lua test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

//=============================================================================
// Milestone 15: Sensors
//=============================================================================

bool Test_SensorRequiresPermission(std::string& error_msg) {
    mosis::SensorInterface sensors("test.app");
    // No permissions granted

    std::string err;
    auto sub = sensors.Subscribe("accelerometer", {}, err);

    EXPECT_TRUE(sub == nullptr);
    EXPECT_TRUE(err.find("permission") != std::string::npos);

    return true;
}

bool Test_SensorAutoGrantsEnvironmental(std::string& error_msg) {
    mosis::SensorInterface sensors("test.app");
    // No motion permission

    std::string err;

    // Proximity should work without permission
    auto proxSub = sensors.Subscribe("proximity", {}, err);
    EXPECT_TRUE(proxSub != nullptr);
    EXPECT_TRUE(proxSub->IsActive());

    // Light should work without permission
    auto lightSub = sensors.Subscribe("light", {}, err);
    EXPECT_TRUE(lightSub != nullptr);
    EXPECT_TRUE(lightSub->IsActive());

    return true;
}

bool Test_SensorReducesPrecision(std::string& error_msg) {
    mosis::SensorInterface sensors("test.app");
    sensors.SetMotionPermission(true);

    std::string err;
    auto sub = sensors.Subscribe("accelerometer", {}, err);
    EXPECT_TRUE(sub != nullptr);

    mosis::SensorData received;
    bool got_data = false;

    sub->SetOnData([&](const mosis::SensorData& data) {
        got_data = true;
        received = data;
    });

    // Simulate high-precision data
    mosis::SensorData precise;
    precise.x = 9.80665123456;
    precise.y = 0.12345678901;
    precise.z = -0.98765432109;
    precise.timestamp = 1234567890;

    // Apply precision reduction manually (as SimulateData bypasses it)
    mosis::SensorData reduced = sensors.ApplyPrecision(precise);
    sub->SimulateData(reduced);

    EXPECT_TRUE(got_data);
    // Values should be rounded to 2 decimal places
    EXPECT_TRUE(std::abs(received.x - 9.81) < 0.001);
    EXPECT_TRUE(std::abs(received.y - 0.12) < 0.001);
    EXPECT_TRUE(std::abs(received.z - (-0.99)) < 0.001);

    return true;
}

bool Test_SensorLimitsFrequency(std::string& error_msg) {
    mosis::SensorInterface sensors("test.app");
    sensors.SetMotionPermission(true);

    mosis::SensorOptions options;
    options.frequency = 120;  // Request 120 Hz

    std::string err;
    auto sub = sensors.Subscribe("gyroscope", options, err);

    EXPECT_TRUE(sub != nullptr);
    // Should be capped at 60 Hz
    EXPECT_TRUE(sub->GetFrequency() <= 60);

    return true;
}

bool Test_SensorSubscriptionLimit(std::string& error_msg) {
    mosis::SensorInterface sensors("test.app");
    sensors.SetMotionPermission(true);

    std::vector<std::shared_ptr<mosis::SensorSubscription>> subs;
    std::string err;

    // Create MAX_SUBSCRIPTIONS (10)
    for (int i = 0; i < 10; i++) {
        auto sub = sensors.Subscribe("accelerometer", {}, err);
        EXPECT_TRUE(sub != nullptr);
        subs.push_back(sub);
    }

    // 11th should fail
    auto extra = sensors.Subscribe("accelerometer", {}, err);
    EXPECT_TRUE(extra == nullptr);
    EXPECT_TRUE(err.find("limit") != std::string::npos ||
                err.find("maximum") != std::string::npos);

    return true;
}

bool Test_SensorCleansUpOnShutdown(std::string& error_msg) {
    mosis::SensorInterface sensors("test.app");
    sensors.SetMotionPermission(true);

    std::string err;
    auto sub1 = sensors.Subscribe("accelerometer", {}, err);
    auto sub2 = sensors.Subscribe("gyroscope", {}, err);

    EXPECT_TRUE(sensors.GetActiveSubscriptionCount() == 2);

    sensors.Shutdown();

    EXPECT_TRUE(sensors.GetActiveSubscriptionCount() == 0);
    EXPECT_TRUE(!sub1->IsActive());
    EXPECT_TRUE(!sub2->IsActive());

    return true;
}

bool Test_SensorLuaIntegration(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::SensorInterface sensors("test.app");
    mosis::RegisterSensorAPI(sandbox.GetState(), &sensors);

    std::string script = R"lua(
        -- Test that sensors global exists
        if not sensors then
            error("sensors global not found")
        end
        if not sensors.subscribe then
            error("sensors.subscribe not found")
        end
        if not sensors.unsubscribeAll then
            error("sensors.unsubscribeAll not found")
        end
        if not sensors.getSubscriptionCount then
            error("sensors.getSubscriptionCount not found")
        end

        -- Subscription count should be 0 initially
        if sensors.getSubscriptionCount() ~= 0 then
            error("should have no active subscriptions initially")
        end
    )lua";

    bool ok = sandbox.LoadString(script, "sensor_test");
    if (!ok) {
        error_msg = "Lua test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

//=============================================================================
// Milestone 16: Bluetooth
//=============================================================================

bool Test_BluetoothRequiresPermission(std::string& error_msg) {
    mosis::BluetoothInterface bluetooth("test.app");
    // No permission granted

    std::string err;
    bool started = bluetooth.StartDiscovery(
        {},
        [](const std::vector<mosis::BluetoothDevice>&) {},
        [](mosis::BluetoothError, const std::string&) {},
        err
    );

    EXPECT_TRUE(!started);
    EXPECT_TRUE(err.find("permission") != std::string::npos);

    return true;
}

bool Test_BluetoothRequiresUserConsent(std::string& error_msg) {
    mosis::BluetoothInterface bluetooth("test.app");
    bluetooth.SetBluetoothPermission(true);
    // User consent NOT granted

    std::string err;
    bool started = bluetooth.StartDiscovery(
        {},
        [](const std::vector<mosis::BluetoothDevice>&) {},
        [](mosis::BluetoothError, const std::string&) {},
        err
    );

    EXPECT_TRUE(!started);
    EXPECT_TRUE(err.find("consent") != std::string::npos ||
                err.find("gesture") != std::string::npos);

    return true;
}

bool Test_BluetoothDiscoveryWorks(std::string& error_msg) {
    mosis::BluetoothInterface bluetooth("test.app");
    bluetooth.SetBluetoothPermission(true);
    bluetooth.SetUserConsent(true);

    // Set mock devices
    std::vector<mosis::BluetoothDevice> devices = {
        {"Device A", "AA:BB:CC:DD:EE:FF"},
        {"Device B", "11:22:33:44:55:66"}
    };
    bluetooth.SetMockDevices(devices);

    std::vector<mosis::BluetoothDevice> discovered;
    bool got_devices = false;

    std::string err;
    bool started = bluetooth.StartDiscovery(
        {},
        [&](const std::vector<mosis::BluetoothDevice>& devs) {
            got_devices = true;
            discovered = devs;
        },
        [](mosis::BluetoothError, const std::string&) {},
        err
    );

    EXPECT_TRUE(started);
    EXPECT_TRUE(got_devices);
    EXPECT_TRUE(discovered.size() == 2);
    EXPECT_TRUE(discovered[0].name == "Device A");
    EXPECT_TRUE(discovered[0].address == "AA:BB:CC:DD:EE:FF");

    return true;
}

bool Test_BluetoothConnectionLimit(std::string& error_msg) {
    mosis::BluetoothInterface bluetooth("test.app");
    bluetooth.SetBluetoothPermission(true);

    std::vector<std::shared_ptr<mosis::BluetoothConnection>> connections;
    std::string err;

    // Create MAX_CONNECTIONS (5)
    for (int i = 0; i < 5; i++) {
        char addr[20];
        snprintf(addr, sizeof(addr), "AA:BB:CC:DD:EE:%02X", i);
        auto conn = bluetooth.Connect(addr, {}, err);
        EXPECT_TRUE(conn != nullptr);
        connections.push_back(conn);
    }

    // 6th should fail
    auto extra = bluetooth.Connect("AA:BB:CC:DD:EE:FF", {}, err);
    EXPECT_TRUE(extra == nullptr);
    EXPECT_TRUE(err.find("limit") != std::string::npos);

    return true;
}

bool Test_BluetoothSendReceive(std::string& error_msg) {
    mosis::BluetoothInterface bluetooth("test.app");
    bluetooth.SetBluetoothPermission(true);

    std::string err;
    auto conn = bluetooth.Connect("AA:BB:CC:DD:EE:FF", {}, err);
    EXPECT_TRUE(conn != nullptr);
    EXPECT_TRUE(conn->IsConnected());

    // Test send
    std::vector<uint8_t> data = {0x01, 0x02, 0x03};
    bool sent = conn->Send(data, err);
    EXPECT_TRUE(sent);

    // Test receive via simulation
    std::vector<uint8_t> received;
    bool got_data = false;
    conn->SetOnData([&](const std::vector<uint8_t>& d) {
        got_data = true;
        received = d;
    });

    std::vector<uint8_t> incoming = {0xAA, 0xBB};
    conn->SimulateData(incoming);

    EXPECT_TRUE(got_data);
    EXPECT_TRUE(received.size() == 2);
    EXPECT_TRUE(received[0] == 0xAA);

    return true;
}

bool Test_BluetoothCleansUpOnShutdown(std::string& error_msg) {
    mosis::BluetoothInterface bluetooth("test.app");
    bluetooth.SetBluetoothPermission(true);

    std::string err;
    auto conn1 = bluetooth.Connect("AA:BB:CC:DD:EE:01", {}, err);
    auto conn2 = bluetooth.Connect("AA:BB:CC:DD:EE:02", {}, err);

    EXPECT_TRUE(bluetooth.GetActiveConnectionCount() == 2);

    bluetooth.Shutdown();

    EXPECT_TRUE(bluetooth.GetActiveConnectionCount() == 0);
    EXPECT_TRUE(!conn1->IsConnected());
    EXPECT_TRUE(!conn2->IsConnected());

    return true;
}

bool Test_BluetoothLuaIntegration(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::BluetoothInterface bluetooth("test.app");
    mosis::RegisterBluetoothAPI(sandbox.GetState(), &bluetooth);

    std::string script = R"lua(
        -- Test that bluetooth global exists
        if not bluetooth then
            error("bluetooth global not found")
        end
        if not bluetooth.startDiscovery then
            error("bluetooth.startDiscovery not found")
        end
        if not bluetooth.stopDiscovery then
            error("bluetooth.stopDiscovery not found")
        end
        if not bluetooth.connect then
            error("bluetooth.connect not found")
        end
        if not bluetooth.disconnectAll then
            error("bluetooth.disconnectAll not found")
        end
        if not bluetooth.getConnectionCount then
            error("bluetooth.getConnectionCount not found")
        end
        if not bluetooth.isDiscovering then
            error("bluetooth.isDiscovering not found")
        end

        -- Connection count should be 0 initially
        if bluetooth.getConnectionCount() ~= 0 then
            error("should have no active connections initially")
        end

        -- Should not be discovering initially
        if bluetooth.isDiscovering() then
            error("should not be discovering initially")
        end
    )lua";

    bool ok = sandbox.LoadString(script, "bluetooth_test");
    if (!ok) {
        error_msg = "Lua test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

//=============================================================================
// Milestone 17: Contacts
//=============================================================================

bool Test_ContactsRequiresReadPermission(std::string& error_msg) {
    mosis::ContactsInterface contacts("test.app");
    // No permission granted

    std::string err;
    auto results = contacts.Query({}, err);

    EXPECT_TRUE(results.empty());
    EXPECT_TRUE(err.find("permission") != std::string::npos);

    return true;
}

bool Test_ContactsRequiresWritePermission(std::string& error_msg) {
    mosis::ContactsInterface contacts("test.app");
    contacts.SetReadPermission(true);  // Read OK, but not write

    std::string err;
    mosis::Contact contact;
    contact.name = "John Doe";
    contact.phone = "555-1234";
    contact.email = "john@test.com";
    int id = contacts.Add(contact, err);

    EXPECT_TRUE(id == 0);  // Failed
    EXPECT_TRUE(err.find("permission") != std::string::npos);

    return true;
}

bool Test_ContactsQueryWorks(std::string& error_msg) {
    mosis::ContactsInterface contacts("test.app");
    contacts.SetReadPermission(true);

    // Set mock contacts
    std::vector<mosis::Contact> mockContacts = {
        {1, "John Doe", "555-1234", "john@test.com"},
        {2, "Jane Doe", "555-5678", "jane@test.com"},
        {3, "Bob Smith", "555-9999", "bob@test.com"}
    };
    contacts.SetMockContacts(mockContacts);

    std::string err;
    mosis::QueryOptions opts;
    opts.search = "Doe";
    auto results = contacts.Query(opts, err);

    EXPECT_TRUE(err.empty());
    EXPECT_TRUE(results.size() == 2);  // John and Jane

    return true;
}

bool Test_ContactsQueryLimit(std::string& error_msg) {
    mosis::ContactsInterface contacts("test.app");
    contacts.SetReadPermission(true);

    // Set 50 mock contacts
    std::vector<mosis::Contact> mockContacts;
    for (int i = 0; i < 50; i++) {
        mosis::Contact c;
        c.id = i + 1;
        c.name = "Contact " + std::to_string(i);
        c.phone = "555-" + std::to_string(i);
        mockContacts.push_back(c);
    }
    contacts.SetMockContacts(mockContacts);

    std::string err;

    // Query with limit
    mosis::QueryOptions opts;
    opts.limit = 10;
    auto results = contacts.Query(opts, err);
    EXPECT_TRUE(results.size() == 10);

    // Query with excessive limit (gets all contacts < MAX_RESULTS)
    opts.limit = 200;
    results = contacts.Query(opts, err);
    EXPECT_TRUE(results.size() == 50);  // All contacts

    return true;
}

bool Test_ContactsCRUD(std::string& error_msg) {
    mosis::ContactsInterface contacts("test.app");
    contacts.SetReadPermission(true);
    contacts.SetWritePermission(true);

    std::string err;

    // Add
    mosis::Contact newContact;
    newContact.name = "Test User";
    newContact.phone = "555-1111";
    newContact.email = "test@test.com";
    int id = contacts.Add(newContact, err);
    EXPECT_TRUE(id > 0);
    EXPECT_TRUE(err.empty());

    // Read back
    auto contact = contacts.GetById(id, err);
    EXPECT_TRUE(contact.has_value());
    EXPECT_TRUE(contact->name == "Test User");

    // Update
    contact->name = "Updated User";
    contact->phone = "555-2222";
    bool updated = contacts.Update(*contact, err);
    EXPECT_TRUE(updated);

    // Verify update
    contact = contacts.GetById(id, err);
    EXPECT_TRUE(contact->name == "Updated User");
    EXPECT_TRUE(contact->phone == "555-2222");

    // Delete
    bool deleted = contacts.Delete(id, err);
    EXPECT_TRUE(deleted);

    // Verify deleted
    contact = contacts.GetById(id, err);
    EXPECT_TRUE(!contact.has_value());

    return true;
}

bool Test_ContactsDeleteNotFound(std::string& error_msg) {
    mosis::ContactsInterface contacts("test.app");
    contacts.SetWritePermission(true);

    std::string err;
    bool deleted = contacts.Delete(99999, err);

    EXPECT_TRUE(!deleted);
    EXPECT_TRUE(err.find("not found") != std::string::npos);

    return true;
}

bool Test_ContactsLuaIntegration(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::ContactsInterface contacts("test.app");
    mosis::RegisterContactsAPI(sandbox.GetState(), &contacts);

    std::string script = R"lua(
        -- Test that contacts global exists
        if not contacts then
            error("contacts global not found")
        end
        if not contacts.query then
            error("contacts.query not found")
        end
        if not contacts.getById then
            error("contacts.getById not found")
        end
        if not contacts.getCount then
            error("contacts.getCount not found")
        end
        if not contacts.add then
            error("contacts.add not found")
        end
        if not contacts.update then
            error("contacts.update not found")
        end
        if not contacts.delete then
            error("contacts.delete not found")
        end
    )lua";

    bool ok = sandbox.LoadString(script, "contacts_test");
    if (!ok) {
        error_msg = "Lua test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

//=============================================================================
// MILESTONE 18: MESSAGE BUS TESTS
//=============================================================================

bool Test_MessageBusSendToReceiver(std::string& error_msg) {
    mosis::MessageBus bus;

    bool received = false;
    mosis::Intent receivedIntent;

    // Register receiver
    bus.RegisterReceiver("receiver.app", {"share", {}}, [&](const mosis::Intent& i) {
        received = true;
        receivedIntent = i;
    });

    // Send intent
    mosis::Intent intent{"share", "text/plain", "Hello", "sender.app"};
    std::string err;
    auto result = bus.Send("sender.app", "receiver.app", intent, err);

    EXPECT_TRUE(result == mosis::MessageError::None);
    EXPECT_TRUE(received);
    EXPECT_TRUE(receivedIntent.action == "share");
    EXPECT_TRUE(receivedIntent.data == "Hello");
    EXPECT_TRUE(receivedIntent.from_app == "sender.app");

    return true;
}

bool Test_MessageBusBlockUnregistered(std::string& error_msg) {
    mosis::MessageBus bus;

    // No receivers registered
    mosis::Intent intent{"share", "text/plain", "Hello", "sender.app"};
    std::string err;
    auto result = bus.Send("sender.app", "receiver.app", intent, err);

    EXPECT_TRUE(result == mosis::MessageError::ReceiverNotFound);
    EXPECT_TRUE(err.find("not found") != std::string::npos ||
                err.find("no receiver") != std::string::npos);

    return true;
}

bool Test_MessageBusBroadcast(std::string& error_msg) {
    mosis::MessageBus bus;

    int receiveCount = 0;

    // Register multiple receivers for same action
    bus.RegisterReceiver("app1", {"share", {}}, [&](const mosis::Intent&) { receiveCount++; });
    bus.RegisterReceiver("app2", {"share", {}}, [&](const mosis::Intent&) { receiveCount++; });
    bus.RegisterReceiver("app3", {"other", {}}, [&](const mosis::Intent&) { receiveCount++; });

    // Broadcast share intent
    mosis::Intent intent{"share", "text/plain", "Hello", "sender.app"};
    std::string err;
    auto result = bus.Broadcast("sender.app", intent, err);

    EXPECT_TRUE(result == mosis::MessageError::None);
    EXPECT_TRUE(receiveCount == 2);  // Only app1 and app2

    return true;
}

bool Test_MessageBusMimeFilter(std::string& error_msg) {
    mosis::MessageBus bus;

    bool imageReceived = false;
    bool textReceived = false;

    // Register with MIME filter
    bus.RegisterReceiver("image.viewer", {"view", {"image/png", "image/jpeg"}},
        [&](const mosis::Intent&) { imageReceived = true; });
    bus.RegisterReceiver("text.viewer", {"view", {"text/plain"}},
        [&](const mosis::Intent&) { textReceived = true; });

    // Send image intent
    mosis::Intent imgIntent{"view", "image/png", "data", "sender"};
    std::string err;
    bus.Broadcast("sender", imgIntent, err);

    EXPECT_TRUE(imageReceived);
    EXPECT_TRUE(!textReceived);

    return true;
}

bool Test_MessageBusDataLimit(std::string& error_msg) {
    mosis::MessageBus bus;

    bus.RegisterReceiver("receiver", {"share", {}}, [](const mosis::Intent&) {});

    // Create oversized data (> 1MB)
    std::string largeData(2 * 1024 * 1024, 'x');

    mosis::Intent intent{"share", "text/plain", largeData, "sender"};
    std::string err;
    auto result = bus.Send("sender", "receiver", intent, err);

    EXPECT_TRUE(result == mosis::MessageError::DataTooLarge);

    return true;
}

bool Test_MessageBusBlockedApp(std::string& error_msg) {
    mosis::MessageBus bus;

    bus.RegisterReceiver("receiver", {"share", {}}, [](const mosis::Intent&) {});
    bus.BlockApp("bad.app");

    mosis::Intent intent{"share", "text/plain", "Hello", "bad.app"};
    std::string err;
    auto result = bus.Send("bad.app", "receiver", intent, err);

    EXPECT_TRUE(result == mosis::MessageError::SenderBlocked);

    return true;
}

bool Test_MessageBusLuaIntegration(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    mosis::MessageBus bus;
    mosis::RegisterIntentsAPI(sandbox.GetState(), &bus, "test.app");

    std::string script = R"lua(
        -- Test that intents global exists
        if not intents then
            error("intents global not found")
        end
        if not intents.on then
            error("intents.on not found")
        end
        if not intents.send then
            error("intents.send not found")
        end
        if not intents.broadcast then
            error("intents.broadcast not found")
        end
        if not intents.hasReceiver then
            error("intents.hasReceiver not found")
        end
        if not intents.getReceivers then
            error("intents.getReceivers not found")
        end
        if not intents.unregisterAll then
            error("intents.unregisterAll not found")
        end
    )lua";

    bool ok = sandbox.LoadString(script, "intents_test");
    if (!ok) {
        error_msg = "Lua test failed: " + sandbox.GetLastError();
        return false;
    }
    return true;
}

//=============================================================================
// MILESTONE 19: SECURITY TESTING SUITE
//=============================================================================

bool Test_FuzzerNoCrashes(std::string& error_msg) {
    mosis::LuaFuzzer fuzzer(12345);  // Deterministic seed

    auto result = fuzzer.Run(100);  // 100 iterations

    EXPECT_TRUE(!result.crashed);
    EXPECT_TRUE(result.sandbox_intact);
    EXPECT_TRUE(result.iterations == 100);

    return true;
}

bool Test_FuzzerCatchesErrors(std::string& error_msg) {
    mosis::LuaFuzzer fuzzer(54321);

    auto result = fuzzer.Run(50);

    // Some generated code may produce errors (caught gracefully)
    // Not all random code produces errors, so we just check no crash
    EXPECT_TRUE(!result.crashed);

    return true;
}

bool Test_FuzzerSandboxIntegrity(std::string& error_msg) {
    mosis::LuaFuzzer fuzzer(99999);

    // Run iterations
    auto result = fuzzer.Run(100);

    // Sandbox must still be intact
    EXPECT_TRUE(result.sandbox_intact);

    // Verify by running a normal script
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);
    EXPECT_TRUE(sandbox.LoadString("return 1 + 1", "verify"));

    return true;
}

bool Test_AuditDangerousGlobalsBlocked(std::string& error_msg) {
    SandboxContext ctx = TestContext();
    LuaSandbox sandbox(ctx);

    // All these must fail (globals should not exist)
    std::vector<std::string> dangerous = {
        "return os",
        "return io",
        "return loadfile",
        "return dofile",
        "return package"
    };

    for (const auto& code : dangerous) {
        bool ok = sandbox.LoadString(code, "audit");
        if (ok) {
            // Check if the result is nil (global doesn't exist)
            // The script runs but returns nil for non-existent globals
        }
    }

    // Verify specific dangerous functions don't exist
    std::string check = R"lua(
        local function check(name, obj)
            if obj ~= nil then
                error(name .. " should not exist")
            end
        end
        check("os", os)
        check("io", io)
        check("loadfile", loadfile)
        check("dofile", dofile)
        check("package", package)
        return true
    )lua";

    bool ok = sandbox.LoadString(check, "audit_check");
    EXPECT_TRUE(ok);

    return true;
}

bool Test_AuditResourceLimits(std::string& error_msg) {
    SandboxContext ctx = TestContext();

    // Memory limit test
    {
        SandboxLimits limits;
        limits.memory_bytes = 1024 * 1024;  // 1MB
        LuaSandbox sandbox(ctx, limits);
        bool ok = sandbox.LoadString(R"lua(
            local t = {}
            for i = 1, 10000000 do
                t[i] = string.rep("x", 1000)
            end
        )lua", "mem_test");
        EXPECT_FALSE(ok);  // Should fail due to memory limit
    }

    // CPU limit test
    {
        SandboxLimits limits;
        limits.instructions_per_call = 10000;
        LuaSandbox sandbox(ctx, limits);
        bool ok = sandbox.LoadString("while true do end", "cpu_test");
        EXPECT_FALSE(ok);  // Should fail due to CPU limit
    }

    return true;
}

bool Test_IntegrationAppLifecycle(std::string& error_msg) {
    // Create app context with permissions
    SandboxContext ctx{
        .app_id = "lifecycle.test.app",
        .app_path = ".",
        .permissions = {"storage"},
        .is_system_app = false
    };

    // Create sandbox
    LuaSandbox sandbox(ctx);

    // Register JSON API (needed for the test)
    mosis::RegisterJsonAPI(sandbox.GetState());

    // Load app script that uses multiple features
    std::string script = R"lua(
        -- App initialization - test multiple features
        local data = {started = true, count = 42}
        local encoded = json.encode(data)

        -- Verify JSON round-trip
        local decoded = json.decode(encoded)
        if decoded.count ~= 42 then
            error("JSON round-trip failed")
        end

        -- Test string operations
        local s = string.format("App %s initialized", "test")
        if not string.find(s, "test") then
            error("String operations failed")
        end

        -- Test table operations
        local t = {1, 2, 3, 4, 5}
        if #t ~= 5 then
            error("Table operations failed")
        end

        -- Test math operations
        local sum = 0
        for i = 1, 100 do
            sum = sum + i
        end
        if sum ~= 5050 then
            error("Math operations failed")
        end

        return true
    )lua";

    bool ok = sandbox.LoadString(script, "lifecycle_test");
    if (!ok) {
        error_msg = "Lifecycle test failed: " + sandbox.GetLastError();
        return false;
    }

    return true;
}

//=============================================================================
// Milestone 20: Kernel Integration (Sandbox Manager)
//=============================================================================

bool Test_ManagerStartStopApp(std::string& error_msg) {
    // Create temporary directory for test
    std::string test_dir = "test_manager_data";
    std::filesystem::create_directories(test_dir);

    mosis::LuaSandboxManager manager(test_dir);

    // Start an app
    bool started = manager.StartApp("test.app.1", ".", {"storage"}, false);
    EXPECT_TRUE(started);

    // Verify it's running
    EXPECT_TRUE(manager.IsAppRunning("test.app.1"));
    EXPECT_TRUE(manager.GetRunningAppCount() == 1);

    // Stop the app
    bool stopped = manager.StopApp("test.app.1");
    EXPECT_TRUE(stopped);

    // Verify it's stopped
    EXPECT_TRUE(!manager.IsAppRunning("test.app.1"));
    EXPECT_TRUE(manager.GetRunningAppCount() == 0);

    // Clean up
    std::filesystem::remove_all(test_dir);

    return true;
}

bool Test_ManagerMultipleApps(std::string& error_msg) {
    std::string test_dir = "test_manager_multi";
    std::filesystem::create_directories(test_dir);

    mosis::LuaSandboxManager manager(test_dir);

    // Start multiple apps
    EXPECT_TRUE(manager.StartApp("app.one", ".", {}, false));
    EXPECT_TRUE(manager.StartApp("app.two", ".", {}, false));
    EXPECT_TRUE(manager.StartApp("app.three", ".", {}, false));

    // Verify all running
    EXPECT_TRUE(manager.GetRunningAppCount() == 3);
    EXPECT_TRUE(manager.IsAppRunning("app.one"));
    EXPECT_TRUE(manager.IsAppRunning("app.two"));
    EXPECT_TRUE(manager.IsAppRunning("app.three"));

    // Get running apps list
    auto apps = manager.GetRunningApps();
    EXPECT_TRUE(apps.size() == 3);

    // Stop one
    EXPECT_TRUE(manager.StopApp("app.two"));
    EXPECT_TRUE(manager.GetRunningAppCount() == 2);
    EXPECT_TRUE(!manager.IsAppRunning("app.two"));

    // Stop all remaining
    EXPECT_TRUE(manager.StopApp("app.one"));
    EXPECT_TRUE(manager.StopApp("app.three"));
    EXPECT_TRUE(manager.GetRunningAppCount() == 0);

    // Clean up
    std::filesystem::remove_all(test_dir);

    return true;
}

bool Test_ManagerAppIsolation(std::string& error_msg) {
    std::string test_dir = "test_manager_isolation";
    std::filesystem::create_directories(test_dir);

    mosis::LuaSandboxManager manager(test_dir);

    // Start two apps
    EXPECT_TRUE(manager.StartApp("app.a", ".", {}, false));
    EXPECT_TRUE(manager.StartApp("app.b", ".", {}, false));

    // Execute valid code in both apps (using local variables since globals are blocked)
    bool ok1 = manager.ExecuteCode("app.a", "local x = 1 + 1; return x == 2", "test1");
    EXPECT_TRUE(ok1);

    bool ok2 = manager.ExecuteCode("app.b", "local y = 2 + 2; return y == 4", "test2");
    EXPECT_TRUE(ok2);

    // Verify apps have different Lua states (true isolation)
    auto* app_a = manager.GetApp("app.a");
    auto* app_b = manager.GetApp("app.b");
    EXPECT_TRUE(app_a != nullptr);
    EXPECT_TRUE(app_b != nullptr);
    EXPECT_TRUE(app_a->lua->GetState() != app_b->lua->GetState());

    // Clean up
    manager.StopApp("app.a");
    manager.StopApp("app.b");
    std::filesystem::remove_all(test_dir);

    return true;
}

bool Test_ManagerExecuteCode(std::string& error_msg) {
    std::string test_dir = "test_manager_exec";
    std::filesystem::create_directories(test_dir);

    mosis::LuaSandboxManager manager(test_dir);
    EXPECT_TRUE(manager.StartApp("exec.test", ".", {}, false));

    // Execute valid code
    EXPECT_TRUE(manager.ExecuteCode("exec.test", "local x = 1 + 1", "valid"));

    // Execute code with error should return false but not crash
    bool result = manager.ExecuteCode("exec.test", "this is not valid lua!!!", "invalid");
    EXPECT_TRUE(!result);  // Should fail gracefully

    // Execute code on non-existent app should return false
    EXPECT_TRUE(!manager.ExecuteCode("nonexistent.app", "return 1", "test"));

    // Clean up
    manager.StopApp("exec.test");
    std::filesystem::remove_all(test_dir);

    return true;
}

bool Test_ManagerResourceCleanup(std::string& error_msg) {
    std::string test_dir = "test_manager_cleanup";
    std::filesystem::create_directories(test_dir);

    mosis::LuaSandboxManager manager(test_dir);
    EXPECT_TRUE(manager.StartApp("cleanup.test", ".", {}, false));

    // Verify app is running and accessible
    auto* app = manager.GetApp("cleanup.test");
    EXPECT_TRUE(app != nullptr);
    EXPECT_TRUE(app->is_running);

    // Stop the app - should clean up all resources
    EXPECT_TRUE(manager.StopApp("cleanup.test"));

    // Verify app is no longer accessible
    EXPECT_TRUE(manager.GetApp("cleanup.test") == nullptr);

    // Clean up
    std::filesystem::remove_all(test_dir);

    return true;
}

bool Test_ManagerUserGesture(std::string& error_msg) {
    std::string test_dir = "test_manager_gesture";
    std::filesystem::create_directories(test_dir);

    mosis::LuaSandboxManager manager(test_dir);
    EXPECT_TRUE(manager.StartApp("gesture.test", ".", {"camera"}, false));

    auto* app = manager.GetApp("gesture.test");
    EXPECT_TRUE(app != nullptr);

    // Initially no recent gesture
    EXPECT_TRUE(!app->permissions->HasRecentUserGesture(1000));

    // Record a gesture through the manager
    manager.RecordUserGesture("gesture.test");

    // Now should have recent gesture
    EXPECT_TRUE(app->permissions->HasRecentUserGesture(1000));

    // Clean up
    manager.StopApp("gesture.test");
    std::filesystem::remove_all(test_dir);

    return true;
}

bool Test_ManagerDoubleStartStop(std::string& error_msg) {
    std::string test_dir = "test_manager_double";
    std::filesystem::create_directories(test_dir);

    mosis::LuaSandboxManager manager(test_dir);

    // Start app
    EXPECT_TRUE(manager.StartApp("double.test", ".", {}, false));

    // Try to start same app again - should fail
    EXPECT_TRUE(!manager.StartApp("double.test", ".", {}, false));

    // Stop app
    EXPECT_TRUE(manager.StopApp("double.test"));

    // Try to stop again - should fail
    EXPECT_TRUE(!manager.StopApp("double.test"));

    // Now can start again
    EXPECT_TRUE(manager.StartApp("double.test", ".", {}, false));
    EXPECT_TRUE(manager.StopApp("double.test"));

    // Clean up
    std::filesystem::remove_all(test_dir);

    return true;
}

bool Test_ManagerSharedComponents(std::string& error_msg) {
    std::string test_dir = "test_manager_shared";
    std::filesystem::create_directories(test_dir);

    mosis::LuaSandboxManager manager(test_dir);

    // Verify shared components are accessible
    auto& audit = manager.GetAuditLog();
    auto& rate_limiter = manager.GetRateLimiter();
    auto& message_bus = manager.GetMessageBus();
    auto& timers = manager.GetTimerManager();

    // Log an event through shared audit log
    audit.Log(mosis::AuditEvent::AppStart, "test.shared", "TEST", "Testing shared audit");

    // Verify audit log has the entry
    auto entries = audit.GetEntries();
    bool found = false;
    for (const auto& e : entries) {
        if (e.app_id == "test.shared") {
            found = true;
            break;
        }
    }
    EXPECT_TRUE(found);

    // Clean up
    std::filesystem::remove_all(test_dir);

    return true;
}

//=============================================================================
// MAIN
//=============================================================================

int main(int argc, char* argv[]) {
#ifdef _WIN32
    // Disable VC runtime abort dialog - write to console instead
    _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
    _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
#endif

    std::string filter;
    std::string output_file = "test_results.json";

    // Parse args
    for (int i = 1; i < argc; i++) {
        std::string arg = argv[i];
        if (arg == "--test" && i + 1 < argc) {
            filter = argv[++i];
        } else if (arg == "--output" && i + 1 < argc) {
            output_file = argv[++i];
        } else if (arg == "--help") {
            std::cout << "Usage: sandbox-test [options]\n";
            std::cout << "Options:\n";
            std::cout << "  --test <name>    Run only tests containing <name>\n";
            std::cout << "  --output <file>  Write JSON report to <file>\n";
            std::cout << "  --help           Show this help\n";
            return 0;
        }
    }

    std::cout << "========================================\n";
    std::cout << "    LUA SANDBOX SECURITY TESTS\n";
    std::cout << "========================================\n\n";

    // Check scripts directory exists
    if (!std::filesystem::exists(GetScriptsDir())) {
        std::cerr << "ERROR: Scripts directory not found: " << GetScriptsDir() << "\n";
        std::cerr << "Make sure to run from the build directory.\n";
        return 1;
    }

    // Register tests
    TestHarness harness;

    // Milestone 1: Core Sandbox
    harness.AddTest("DangerousGlobalsRemoved", Test_DangerousGlobalsRemoved);
    harness.AddTest("BytecodeRejected", Test_BytecodeRejected);
    harness.AddTest("MemoryLimitEnforced", Test_MemoryLimitEnforced);
    harness.AddTest("CPULimitEnforced", Test_CPULimitEnforced);
    harness.AddTest("MetatableProtected", Test_MetatableProtected);
    harness.AddTest("SafeOperationsWork", Test_SafeOperationsWork);
    harness.AddTest("StringDumpRemoved", Test_StringDumpRemoved);
    harness.AddTest("MemoryTracking", Test_MemoryTracking);
    harness.AddTest("InstructionCounting", Test_InstructionCounting);
    harness.AddTest("MultipleLoads", Test_MultipleLoads);
    harness.AddTest("ErrorRecovery", Test_ErrorRecovery);

    // Milestone 2: Permission System
    harness.AddTest("NormalPermissionAutoGranted", Test_NormalPermissionAutoGranted);
    harness.AddTest("DangerousPermissionRequiresGrant", Test_DangerousPermissionRequiresGrant);
    harness.AddTest("SignaturePermissionSystemOnly", Test_SignaturePermissionSystemOnly);
    harness.AddTest("UserGestureTracking", Test_UserGestureTracking);
    harness.AddTest("UndeclaredPermissionDenied", Test_UndeclaredPermissionDenied);
    harness.AddTest("SystemAppGetsDangerousAuto", Test_SystemAppGetsDangerousAuto);
    harness.AddTest("PermissionCategoryCheck", Test_PermissionCategoryCheck);

    // Milestone 3: Audit Logging & Rate Limiting
    harness.AddTest("AuditLogBasic", Test_AuditLogBasic);
    harness.AddTest("AuditLogRingBuffer", Test_AuditLogRingBuffer);
    harness.AddTest("AuditLogThreadSafe", Test_AuditLogThreadSafe);
    harness.AddTest("RateLimiterBasic", Test_RateLimiterBasic);
    harness.AddTest("RateLimiterExhaustion", Test_RateLimiterExhaustion);
    harness.AddTest("RateLimiterRefill", Test_RateLimiterRefill);
    harness.AddTest("RateLimiterAppIsolation", Test_RateLimiterAppIsolation);
    harness.AddTest("RateLimiterReset", Test_RateLimiterReset);
    harness.AddTest("RateLimiterNoConfig", Test_RateLimiterNoConfig);

    // Milestone 4: Safe Path & Require
    harness.AddTest("PathRejectsTraversal", Test_PathRejectsTraversal);
    harness.AddTest("PathRejectsAbsolute", Test_PathRejectsAbsolute);
    harness.AddTest("PathAcceptsValid", Test_PathAcceptsValid);
    harness.AddTest("ModuleNameValidation", Test_ModuleNameValidation);
    harness.AddTest("ModuleToPath", Test_ModuleToPath);
    harness.AddTest("SafeRequireLoads", Test_SafeRequireLoads);
    harness.AddTest("SafeRequireCaches", Test_SafeRequireCaches);
    harness.AddTest("SafeRequireRejectsInvalid", Test_SafeRequireRejectsInvalid);

    // Milestone 5: Timer & Callback System
    harness.AddTest("SetTimeoutFires", Test_SetTimeoutFires);
    harness.AddTest("SetIntervalFires", Test_SetIntervalFires);
    harness.AddTest("ClearTimeoutCancels", Test_ClearTimeoutCancels);
    harness.AddTest("ClearIntervalCancels", Test_ClearIntervalCancels);
    harness.AddTest("TimerLimitEnforced", Test_TimerLimitEnforced);
    harness.AddTest("ClearAppTimersCleanup", Test_ClearAppTimersCleanup);
    harness.AddTest("MinIntervalEnforced", Test_MinIntervalEnforced);

    // Milestone 6: JSON & Crypto APIs
    harness.AddTest("JsonDecodeValid", Test_JsonDecodeValid);
    harness.AddTest("JsonDecodeRejectsDeep", Test_JsonDecodeRejectsDeep);
    harness.AddTest("JsonEncodeValid", Test_JsonEncodeValid);
    harness.AddTest("JsonEncodeDetectsCycles", Test_JsonEncodeDetectsCycles);
    harness.AddTest("JsonRejectsTooLarge", Test_JsonRejectsTooLarge);
    harness.AddTest("CryptoRandomBytes", Test_CryptoRandomBytes);
    harness.AddTest("CryptoHashSHA256", Test_CryptoHashSHA256);
    harness.AddTest("CryptoHMAC", Test_CryptoHMAC);
    harness.AddTest("SecureMathRandom", Test_SecureMathRandom);

    // Milestone 7: Virtual Filesystem
    harness.AddTest("VirtualFSReadWrite", Test_VirtualFSReadWrite);
    harness.AddTest("VirtualFSBlocksTraversal", Test_VirtualFSBlocksTraversal);
    harness.AddTest("VirtualFSEnforcesQuota", Test_VirtualFSEnforcesQuota);
    harness.AddTest("VirtualFSCleansUpTemp", Test_VirtualFSCleansUpTemp);
    harness.AddTest("VirtualFSList", Test_VirtualFSList);
    harness.AddTest("VirtualFSStat", Test_VirtualFSStat);
    harness.AddTest("VirtualFSLuaIntegration", Test_VirtualFSLuaIntegration);
    harness.AddTest("VirtualFSMaxFileSize", Test_VirtualFSMaxFileSize);

    // Milestone 8: SQLite Database
    harness.AddTest("DatabaseCreatesTables", Test_DatabaseCreatesTables);
    harness.AddTest("DatabasePreparedStatements", Test_DatabasePreparedStatements);
    harness.AddTest("DatabaseBlocksAttach", Test_DatabaseBlocksAttach);
    harness.AddTest("DatabaseBlocksDangerousPragma", Test_DatabaseBlocksDangerousPragma);
    harness.AddTest("DatabaseMultiple", Test_DatabaseMultiple);
    harness.AddTest("DatabaseLuaIntegration", Test_DatabaseLuaIntegration);
    harness.AddTest("DatabaseInvalidNames", Test_DatabaseInvalidNames);
    harness.AddTest("DatabaseLastInsertAndChanges", Test_DatabaseLastInsertAndChanges);

    // Milestone 9: Network HTTP
    harness.AddTest("NetworkBlocksPrivateIP", Test_NetworkBlocksPrivateIP);
    harness.AddTest("NetworkBlocksPlainHttp", Test_NetworkBlocksPlainHttp);
    harness.AddTest("NetworkRequiresHttps", Test_NetworkRequiresHttps);
    harness.AddTest("NetworkEnforcesDomainWhitelist", Test_NetworkEnforcesDomainWhitelist);
    harness.AddTest("NetworkUrlParsing", Test_NetworkUrlParsing);
    harness.AddTest("NetworkBlocksMetadata", Test_NetworkBlocksMetadata);
    harness.AddTest("NetworkRequestLimits", Test_NetworkRequestLimits);
    harness.AddTest("NetworkLuaIntegration", Test_NetworkLuaIntegration);

    // Milestone 10: WebSocket
    harness.AddTest("WebSocketUrlValidation", Test_WebSocketUrlValidation);
    harness.AddTest("WebSocketConnectionLimits", Test_WebSocketConnectionLimits);
    harness.AddTest("WebSocketBlocksPrivateIP", Test_WebSocketBlocksPrivateIP);
    harness.AddTest("WebSocketDomainWhitelist", Test_WebSocketDomainWhitelist);
    harness.AddTest("WebSocketMessageLimits", Test_WebSocketMessageLimits);
    harness.AddTest("WebSocketCloseAll", Test_WebSocketCloseAll);
    harness.AddTest("WebSocketLuaIntegration", Test_WebSocketLuaIntegration);

    // Milestone 11: Camera
    harness.AddTest("CameraRequiresPermission", Test_CameraRequiresPermission);
    harness.AddTest("CameraRequiresUserGesture", Test_CameraRequiresUserGesture);
    harness.AddTest("CameraShowsIndicator", Test_CameraShowsIndicator);
    harness.AddTest("CameraSingleSession", Test_CameraSingleSession);
    harness.AddTest("CameraStopsOnShutdown", Test_CameraStopsOnShutdown);
    harness.AddTest("CameraLuaIntegration", Test_CameraLuaIntegration);

    // Milestone 12: Microphone
    harness.AddTest("MicrophoneRequiresPermission", Test_MicrophoneRequiresPermission);
    harness.AddTest("MicrophoneRequiresUserGesture", Test_MicrophoneRequiresUserGesture);
    harness.AddTest("MicrophoneShowsIndicator", Test_MicrophoneShowsIndicator);
    harness.AddTest("MicrophoneSingleSession", Test_MicrophoneSingleSession);
    harness.AddTest("MicrophoneStopsOnShutdown", Test_MicrophoneStopsOnShutdown);
    harness.AddTest("MicrophoneLuaIntegration", Test_MicrophoneLuaIntegration);

    // Milestone 13: Audio Output
    harness.AddTest("AudioPlaysSound", Test_AudioPlaysSound);
    harness.AddTest("AudioRespectsSystemVolume", Test_AudioRespectsSystemVolume);
    harness.AddTest("AudioLimitsConcurrent", Test_AudioLimitsConcurrent);
    harness.AddTest("AudioStopAll", Test_AudioStopAll);
    harness.AddTest("AudioStopsOnShutdown", Test_AudioStopsOnShutdown);
    harness.AddTest("AudioLuaIntegration", Test_AudioLuaIntegration);

    // Milestone 14: Location
    harness.AddTest("LocationRequiresPermission", Test_LocationRequiresPermission);
    harness.AddTest("LocationCoarseReducesPrecision", Test_LocationCoarseReducesPrecision);
    harness.AddTest("LocationRateLimits", Test_LocationRateLimits);
    harness.AddTest("LocationWatchPosition", Test_LocationWatchPosition);
    harness.AddTest("LocationWatchLimit", Test_LocationWatchLimit);
    harness.AddTest("LocationCleansUpOnShutdown", Test_LocationCleansUpOnShutdown);
    harness.AddTest("LocationLuaIntegration", Test_LocationLuaIntegration);

    // Milestone 15: Sensors
    harness.AddTest("SensorRequiresPermission", Test_SensorRequiresPermission);
    harness.AddTest("SensorAutoGrantsEnvironmental", Test_SensorAutoGrantsEnvironmental);
    harness.AddTest("SensorReducesPrecision", Test_SensorReducesPrecision);
    harness.AddTest("SensorLimitsFrequency", Test_SensorLimitsFrequency);
    harness.AddTest("SensorSubscriptionLimit", Test_SensorSubscriptionLimit);
    harness.AddTest("SensorCleansUpOnShutdown", Test_SensorCleansUpOnShutdown);
    harness.AddTest("SensorLuaIntegration", Test_SensorLuaIntegration);

    // Milestone 16: Bluetooth
    harness.AddTest("BluetoothRequiresPermission", Test_BluetoothRequiresPermission);
    harness.AddTest("BluetoothRequiresUserConsent", Test_BluetoothRequiresUserConsent);
    harness.AddTest("BluetoothDiscoveryWorks", Test_BluetoothDiscoveryWorks);
    harness.AddTest("BluetoothConnectionLimit", Test_BluetoothConnectionLimit);
    harness.AddTest("BluetoothSendReceive", Test_BluetoothSendReceive);
    harness.AddTest("BluetoothCleansUpOnShutdown", Test_BluetoothCleansUpOnShutdown);
    harness.AddTest("BluetoothLuaIntegration", Test_BluetoothLuaIntegration);

    // Milestone 17: Contacts
    harness.AddTest("ContactsRequiresReadPermission", Test_ContactsRequiresReadPermission);
    harness.AddTest("ContactsRequiresWritePermission", Test_ContactsRequiresWritePermission);
    harness.AddTest("ContactsQueryWorks", Test_ContactsQueryWorks);
    harness.AddTest("ContactsQueryLimit", Test_ContactsQueryLimit);
    harness.AddTest("ContactsCRUD", Test_ContactsCRUD);
    harness.AddTest("ContactsDeleteNotFound", Test_ContactsDeleteNotFound);
    harness.AddTest("ContactsLuaIntegration", Test_ContactsLuaIntegration);

    // Milestone 18: Message Bus
    harness.AddTest("MessageBusSendToReceiver", Test_MessageBusSendToReceiver);
    harness.AddTest("MessageBusBlockUnregistered", Test_MessageBusBlockUnregistered);
    harness.AddTest("MessageBusBroadcast", Test_MessageBusBroadcast);
    harness.AddTest("MessageBusMimeFilter", Test_MessageBusMimeFilter);
    harness.AddTest("MessageBusDataLimit", Test_MessageBusDataLimit);
    harness.AddTest("MessageBusBlockedApp", Test_MessageBusBlockedApp);
    harness.AddTest("MessageBusLuaIntegration", Test_MessageBusLuaIntegration);

    // Milestone 19: Security Testing Suite
    harness.AddTest("FuzzerNoCrashes", Test_FuzzerNoCrashes);
    harness.AddTest("FuzzerCatchesErrors", Test_FuzzerCatchesErrors);
    harness.AddTest("FuzzerSandboxIntegrity", Test_FuzzerSandboxIntegrity);
    harness.AddTest("AuditDangerousGlobalsBlocked", Test_AuditDangerousGlobalsBlocked);
    harness.AddTest("AuditResourceLimits", Test_AuditResourceLimits);
    harness.AddTest("IntegrationAppLifecycle", Test_IntegrationAppLifecycle);

    // Milestone 20: Kernel Integration (Sandbox Manager)
    harness.AddTest("ManagerStartStopApp", Test_ManagerStartStopApp);
    harness.AddTest("ManagerMultipleApps", Test_ManagerMultipleApps);
    harness.AddTest("ManagerAppIsolation", Test_ManagerAppIsolation);
    harness.AddTest("ManagerExecuteCode", Test_ManagerExecuteCode);
    harness.AddTest("ManagerResourceCleanup", Test_ManagerResourceCleanup);
    harness.AddTest("ManagerUserGesture", Test_ManagerUserGesture);
    harness.AddTest("ManagerDoubleStartStop", Test_ManagerDoubleStartStop);
    harness.AddTest("ManagerSharedComponents", Test_ManagerSharedComponents);

    // Run tests
    auto results = harness.Run(filter);

    // Output
    harness.PrintResults(results);
    harness.WriteJsonReport(results, output_file);

    std::cout << "\nJSON report written to: " << output_file << "\n";

    // Return non-zero if any tests failed
    int failed = 0;
    for (const auto& r : results) {
        if (!r.passed) failed++;
    }

    return failed > 0 ? 1 : 0;
}
