/**
 * rbxl-lua-services.test.js — тесты Humanoid, RemoteEvent, DataStore, HttpService.
 */
import { LuaFactory } from 'wasmoon';
import { registerRobloxApi } from '../src/engine/roblox-shim.js';
import { RobloxScheduler } from '../src/engine/roblox-scheduler.js';
import { installRobloxServices } from '../src/engine/roblox-services.js';

const SCENE = { primitives: {} };

const STORE = new Map();

async function run(luaSource, ticks = []) {
    const factory = new LuaFactory();
    const lua = await factory.createEngine();
    const sent = [];
    const send = (cmd, payload) => sent.push({ cmd, payload });
    let playerState = { x: 0, y: 5, z: 0 };

    registerRobloxApi(lua, { getSceneSnap: () => SCENE, targetPrimitiveId: null, send });
    const sched = new RobloxScheduler(lua);
    sched.install();
    installRobloxServices(lua, {
        send,
        getPlayerState: () => playerState,
        loadSave: (k) => STORE.get(k),
        saveSave: (k, v) => STORE.set(k, v),
        removeSave: (k) => STORE.delete(k),
    });

    await sched.spawnMain(luaSource);
    for (const dt of ticks) await sched.tick(dt);
    lua.global.close();
    return { logs: sent.filter(s => s.cmd === 'log').map(s => s.payload),
             playerCmds: sent.filter(s => s.cmd === 'playerCmd').map(s => s.payload),
             broadcasts: sent.filter(s => s.cmd === 'broadcast').map(s => s.payload) };
}

const TESTS = [
    {
        name: 'Players.LocalPlayer.Character.Humanoid существует',
        lua: `
            local p = game:GetService("Players").LocalPlayer
            local h = p.Character:WaitForChild("Humanoid")
            print("hp:", h.Health, "ws:", h.WalkSpeed)
        `,
        expect: [{ level: 'info', text: 'hp:\t100\tws:\t16' }],
    },
    {
        name: 'Humanoid.WalkSpeed = 50 → playerCmd setWalkSpeed',
        lua: `
            local h = game:GetService("Players").LocalPlayer.Character.Humanoid
            h.WalkSpeed = 50
        `,
        expectPlayerCmd: { method: 'setWalkSpeed', argsCheck: (a) => a[0] === 50 },
    },
    {
        name: 'Humanoid:TakeDamage уменьшает HP',
        lua: `
            local h = game:GetService("Players").LocalPlayer.Character.Humanoid
            h:TakeDamage(30)
            print("after damage:", h.Health)
        `,
        expect: [{ level: 'info', text: 'after damage:\t70' }],
    },
    {
        name: 'Humanoid.Health = 0 → Died fires',
        lua: `
            local h = game:GetService("Players").LocalPlayer.Character.Humanoid
            h.Died:Connect(function() print("DIED") end)
            h.Health = 0
        `,
        expect: [{ level: 'info', text: 'DIED' }],
    },
    {
        name: 'DataStoreService GetAsync/SetAsync',
        lua: `
            local DSS = game:GetService("DataStoreService")
            local store = DSS:GetDataStore("coins")
            store:SetAsync("player1", 100)
            print("got:", store:GetAsync("player1"))
        `,
        expect: [{ level: 'info', text: 'got:\t100' }],
    },
    {
        name: 'DataStoreService IncrementAsync',
        lua: `
            local store = game:GetService("DataStoreService"):GetDataStore("score")
            store:SetAsync("p1", 50)
            store:IncrementAsync("p1", 25)
            print("final:", store:GetAsync("p1"))
        `,
        expect: [{ level: 'info', text: 'final:\t75' }],
    },
    {
        name: 'HttpService:JSONEncode/Decode',
        lua: `
            local HS = game:GetService("HttpService")
            local s = HS:JSONEncode({a=1, b="two"})
            print("encoded len:", #s)
            local d = HS:JSONDecode('{"x":42}')
            print("decoded x:", d.x)
        `,
        expect: [{ level: 'info', text: 'decoded x:\t42' }],
    },
    {
        name: 'RemoteEvent FireServer + OnServerEvent',
        lua: `
            local re = Instance.new("RemoteEvent", workspace)
            re.Name = "MyEvent"
            re.OnServerEvent:Connect(function(player, msg)
                print("server got:", msg)
            end)
            re:FireServer("hello")
        `,
        expect: [{ level: 'info', text: 'server got:\thello' }],
    },
];

(async () => {
    let passed = 0, failed = 0;
    for (const t of TESTS) {
        try {
            const r = await run(t.lua, t.ticks);
            let ok = true; let reason = '';
            for (const exp of (t.expect || [])) {
                const found = r.logs.find(l => l.level === exp.level && l.text === exp.text);
                if (!found) { ok = false; reason = `missing log: ${exp.text}; got: ${JSON.stringify(r.logs)}`; break; }
            }
            if (t.expectPlayerCmd) {
                const found = r.playerCmds.find(c => c.method === t.expectPlayerCmd.method
                    && (!t.expectPlayerCmd.argsCheck || t.expectPlayerCmd.argsCheck(c.args)));
                if (!found) { ok = false; reason = `missing playerCmd ${t.expectPlayerCmd.method}; got: ${JSON.stringify(r.playerCmds)}`; }
            }
            if (ok) { console.log(`✓ ${t.name}`); passed++; }
            else { console.log(`✗ ${t.name} — ${reason}`); failed++; }
        } catch (e) {
            console.log(`✗ ${t.name} — exception: ${e.message || e}`);
            failed++;
        }
    }
    console.log(`\n${passed} passed, ${failed} failed`);
    process.exit(failed > 0 ? 1 : 0);
})();
