// A chrome.storage.local stub that behaves like the real one.
//
// The real extension storage API is a serialization boundary: `set` writes a
// structured clone of what it is given, and `get` hands back a structured
// clone of what is stored. Nothing an extension page holds is ever the object
// storage holds.
//
// A stub that skips the clone aliases them together, and that hides an entire
// class of defect rather than merely being imprecise. loadState() assigns
// nested references straight out of the get result, so over an aliasing stub a
// test can assert "the endpoint was persisted" and pass on a build that never
// called saveState() at all: the in-memory mutation IS the stored record.
// Measured, not theorised — with an aliasing `get` restored over the handler
// fixed in https://git.eeqj.de/sneak/AutistMask/pulls/319, the whole suite
// passed 794/794 (https://git.eeqj.de/sneak/AutistMask/issues/324).
//
// So every test that drives real persistence uses this, and nothing rebuilds
// a storage stub by hand.

// `initial` is the starting contents, keyed as storage is: { autistmask: {...} }.
// `onOp` runs before each operation, for a test that needs to advance a clock
// or count round trips.
function makeStorageStub(initial, onOp) {
    const store = initial ? structuredClone(initial) : {};
    const tick = onOp || (() => {});

    const get = jest.fn(async (key) => {
        tick();
        if (key === undefined || key === null) return structuredClone(store);
        const keys = Array.isArray(key) ? key : [key];
        const out = {};
        for (const k of keys) {
            if (Object.prototype.hasOwnProperty.call(store, k)) {
                out[k] = structuredClone(store[k]);
            }
        }
        return out;
    });

    const set = jest.fn(async (items) => {
        tick();
        for (const k of Object.keys(items)) {
            store[k] = structuredClone(items[k]);
        }
    });

    const remove = jest.fn(async (key) => {
        tick();
        for (const k of Array.isArray(key) ? key : [key]) delete store[k];
    });

    return {
        // Drop this straight in as chrome.storage.
        local: { get, set, remove },
        get,
        set,
        remove,
        // What is stored, cloned on the way out: a test can neither observe a
        // later write through an object it read nor reach into the store by
        // mutating one.
        read: (key) =>
            key === undefined
                ? structuredClone(store)
                : structuredClone(store[key]),
        // Seed or replace a record without going through the module under
        // test — for standing in as "another page wrote this".
        write: (key, value) => {
            store[key] = structuredClone(value);
        },
    };
}

module.exports = { makeStorageStub };
