import { describe, it, expect } from "@jest/globals";
import { deepCopy } from "../checkpoint/base.js";
import { MemorySaver } from "../checkpoint/memory.js";
import { SqliteSaver } from "../checkpoint/sqlite.js";
import { convert1to6, uuid6 } from "../checkpoint/id.js";
const checkpoint1 = {
    v: 1,
    id: uuid6(-1),
    ts: "2024-04-19T17:19:07.952Z",
    channel_values: {
        someKey1: "someValue1",
    },
    channel_versions: {
        someKey2: 1,
    },
    versions_seen: {
        someKey3: {
            someKey4: 1,
        },
    },
};
const checkpoint2 = {
    v: 1,
    id: uuid6(1),
    ts: "2024-04-20T17:19:07.952Z",
    channel_values: {
        someKey1: "someValue2",
    },
    channel_versions: {
        someKey2: 2,
    },
    versions_seen: {
        someKey3: {
            someKey4: 2,
        },
    },
};
describe("Base", () => {
    it("should deep copy a simple object", () => {
        const obj = { a: 1, b: { c: 2 } };
        const copiedObj = deepCopy(obj);
        // Check if the copied object is equal to the original object
        expect(copiedObj).toEqual(obj);
        // Check if the copied object is not the same object reference as the original object
        expect(copiedObj).not.toBe(obj);
        // Check if the nested object is also deep copied
        expect(copiedObj.b).toEqual(obj.b);
        expect(copiedObj.b).not.toBe(obj.b);
    });
    it("should deep copy an array", () => {
        const arr = [1, 2, 3];
        const copiedArr = deepCopy(arr);
        // Check if the copied array is equal to the original array
        expect(copiedArr).toEqual(arr);
    });
    it("should deep copy an array of objects", () => {
        const arr = [{ a: 1 }, { b: 2 }];
        const copiedArr = deepCopy(arr);
        // Check if the copied array is equal to the original array
        expect(copiedArr).toEqual(arr);
        // Check if the copied array is not the same array reference as the original array
        expect(copiedArr).not.toBe(arr);
        // Check if the nested objects in the array are also deep copied
        expect(copiedArr[0]).toEqual(arr[0]);
        expect(copiedArr[0]).not.toBe(arr[0]);
    });
});
describe("MemorySaver", () => {
    it("should save and retrieve checkpoints correctly", async () => {
        const memorySaver = new MemorySaver();
        // save checkpoint
        const runnableConfig = await memorySaver.put({ configurable: { thread_id: "1" } }, checkpoint1, { source: "update", step: -1, writes: null });
        expect(runnableConfig).toEqual({
            configurable: {
                thread_id: "1",
                checkpoint_id: checkpoint1.id,
            },
        });
        // get checkpoint tuple
        const checkpointTuple = await memorySaver.getTuple({
            configurable: { thread_id: "1" },
        });
        expect(checkpointTuple?.config).toEqual({
            configurable: {
                thread_id: "1",
                checkpoint_id: checkpoint1.id,
            },
        });
        expect(checkpointTuple?.checkpoint).toEqual(checkpoint1);
        // save another checkpoint
        await memorySaver.put({ configurable: { thread_id: "1" } }, checkpoint2, {
            source: "update",
            step: -1,
            writes: null,
        });
        // list checkpoints
        const checkpointTupleGenerator = await memorySaver.list({
            configurable: { thread_id: "1" },
        });
        const checkpointTuples = [];
        for await (const checkpoint of checkpointTupleGenerator) {
            checkpointTuples.push(checkpoint);
        }
        expect(checkpointTuples.length).toBe(2);
        const checkpointTuple1 = checkpointTuples[0];
        const checkpointTuple2 = checkpointTuples[1];
        expect(checkpointTuple1.checkpoint.ts).toBe("2024-04-20T17:19:07.952Z");
        expect(checkpointTuple2.checkpoint.ts).toBe("2024-04-19T17:19:07.952Z");
    });
});
describe("SqliteSaver", () => {
    it("should save and retrieve checkpoints correctly", async () => {
        const sqliteSaver = SqliteSaver.fromConnString(":memory:");
        // get undefined checkpoint
        const undefinedCheckpoint = await sqliteSaver.getTuple({
            configurable: { thread_id: "1" },
        });
        expect(undefinedCheckpoint).toBeUndefined();
        // save first checkpoint
        const runnableConfig = await sqliteSaver.put({ configurable: { thread_id: "1" } }, checkpoint1, { source: "update", step: -1, writes: null });
        expect(runnableConfig).toEqual({
            configurable: {
                thread_id: "1",
                checkpoint_id: checkpoint1.id,
            },
        });
        // get first checkpoint tuple
        const firstCheckpointTuple = await sqliteSaver.getTuple({
            configurable: { thread_id: "1" },
        });
        expect(firstCheckpointTuple?.config).toEqual({
            configurable: {
                thread_id: "1",
                checkpoint_id: checkpoint1.id,
            },
        });
        expect(firstCheckpointTuple?.checkpoint).toEqual(checkpoint1);
        expect(firstCheckpointTuple?.parentConfig).toBeUndefined();
        // save second checkpoint
        await sqliteSaver.put({
            configurable: {
                thread_id: "1",
                checkpoint_id: "2024-04-18T17:19:07.952Z",
            },
        }, checkpoint2, { source: "update", step: -1, writes: null });
        // verify that parentTs is set and retrieved correctly for second checkpoint
        const secondCheckpointTuple = await sqliteSaver.getTuple({
            configurable: { thread_id: "1" },
        });
        expect(secondCheckpointTuple?.parentConfig).toEqual({
            configurable: {
                thread_id: "1",
                checkpoint_id: "2024-04-18T17:19:07.952Z",
            },
        });
        // list checkpoints
        const checkpointTupleGenerator = await sqliteSaver.list({
            configurable: { thread_id: "1" },
        });
        const checkpointTuples = [];
        for await (const checkpoint of checkpointTupleGenerator) {
            checkpointTuples.push(checkpoint);
        }
        expect(checkpointTuples.length).toBe(2);
        const checkpointTuple1 = checkpointTuples[0];
        const checkpointTuple2 = checkpointTuples[1];
        expect(checkpointTuple1.checkpoint.ts).toBe("2024-04-20T17:19:07.952Z");
        expect(checkpointTuple2.checkpoint.ts).toBe("2024-04-19T17:19:07.952Z");
    });
});
describe("id", () => {
    it("should convert uuid1 to uuid6", () => {
        const regex = /^[0-9a-f]{8}-[0-9a-f]{4}-6[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
        // [UUIDv1, UUIDv6]
        const cases = [
            [
                "5714f720-1268-11e7-a24b-96d95aa38c32",
                "1e712685-714f-6720-a24b-96d95aa38c32",
            ],
            [
                "68f820c0-1268-11e7-a24b-671acd892c6a",
                "1e712686-8f82-60c0-a24b-671acd892c6a",
            ],
        ];
        cases.forEach(([v1, v6]) => {
            const converted = convert1to6(v1);
            expect(converted).toBe(v6);
            expect(converted).toMatch(regex);
        });
    });
});
