import { describe, it, expect, vi, beforeEach } from "vitest";
import { int } from "neo4j-driver";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
vi.mock("../../lib/neo4j.js", () => {
    const run = vi.fn();
    const close = vi.fn();
    return {
        getSession: () => ({ run, close }),
        __mocks: { run, close },
    };
});
import { memoryCompiledTruthHistory } from "../memory-compiled-truth-history.js";
import * as neo4jMock from "../../lib/neo4j.js";
const { run, close } = neo4jMock.__mocks;
function row(props) {
    return { get: (key) => props[key] };
}
// Helper: set up two sequential mock responses (revisions query, then timeline events query).
function mockBothQueries(revisionRows, eventRows) {
    run
        .mockResolvedValueOnce({ records: revisionRows.map(row) })
        .mockResolvedValueOnce({ records: eventRows.map(row) });
}
describe("memoryCompiledTruthHistory", () => {
    beforeEach(() => {
        run.mockReset();
        close.mockReset();
    });
    it("returns revisions newest first, account-scoped", async () => {
        mockBothQueries([
            {
                revisionId: "00000000-0000-0000-0000-000000000002",
                truth: "Newer truth",
                writtenAt: "2026-05-25T10:00:00Z",
                twin: "compiledTruth",
                source: "rewriter",
            },
            {
                revisionId: "00000000-0000-0000-0000-000000000001",
                truth: "Older truth",
                writtenAt: "2026-05-20T10:00:00Z",
                twin: "compiledTruth",
                source: "rewriter",
            },
        ], []);
        const result = await memoryCompiledTruthHistory({
            accountId: "acc-1",
            nodeId: "4:abc:1",
        });
        expect(result.entityNodeId).toBe("4:abc:1");
        expect(result.revisions).toHaveLength(2);
        expect(result.revisions[0].truth).toBe("Newer truth");
        expect(result.revisions[1].truth).toBe("Older truth");
        const revArgs = run.mock.calls[0];
        expect(revArgs[0]).toContain("ORDER BY r.writtenAt DESC");
        expect(revArgs[0]).toContain("e.accountId = $accountId");
        expect(revArgs[0]).toContain("r.accountId = $accountId");
        expect(revArgs[1]).toEqual({ accountId: "acc-1", nodeId: "4:abc:1", limit: int(20) });
    });
    it("returns timeline events with eventDate and learnedAt (Task 597)", async () => {
        mockBothQueries([], [
            {
                timelineEventId: "evt-1",
                eventDate: "1990-01-01T00:00:00Z",
                learnedAt: "2026-05-25T10:00:00Z",
                kind: "mentioned",
                summary: "Alice mentioned in archive from 1990",
            },
        ]);
        const result = await memoryCompiledTruthHistory({
            accountId: "acc-1",
            nodeId: "4:abc:1",
        });
        expect(result.timelineEvents).toHaveLength(1);
        const evt = result.timelineEvents[0];
        expect(evt.timelineEventId).toBe("evt-1");
        expect(evt.eventDate).toBe("1990-01-01T00:00:00Z");
        expect(evt.learnedAt).toBe("2026-05-25T10:00:00Z");
        expect(evt.kind).toBe("mentioned");
        // The two timestamps must differ — this is the point of bi-temporal storage.
        expect(evt.eventDate).not.toBe(evt.learnedAt);
        expect(evt.learnedAt).not.toBeNull();
    });
    it("returns null learnedAt for pre-migration nodes (learnedAt IS NULL in DB)", async () => {
        mockBothQueries([], [
            {
                timelineEventId: "evt-old",
                eventDate: "2020-01-01T00:00:00Z",
                learnedAt: null, // node predates Task 597 backfill
                kind: "met",
                summary: "Pre-migration event",
            },
        ]);
        const result = await memoryCompiledTruthHistory({
            accountId: "acc-1",
            nodeId: "4:abc:3",
        });
        expect(result.timelineEvents).toHaveLength(1);
        expect(result.timelineEvents[0].learnedAt).toBeNull();
    });
    it("returns empty arrays when no revisions or events exist", async () => {
        mockBothQueries([], []);
        const result = await memoryCompiledTruthHistory({
            accountId: "acc-1",
            nodeId: "4:abc:2",
        });
        expect(result.revisions).toEqual([]);
        expect(result.timelineEvents).toEqual([]);
    });
    it("throws when accountId or nodeId is missing", async () => {
        await expect(memoryCompiledTruthHistory({ accountId: "", nodeId: "4:abc:1" })).rejects.toThrow(/accountId/);
        await expect(memoryCompiledTruthHistory({ accountId: "acc-1", nodeId: "" })).rejects.toThrow(/nodeId/);
    });
});
describe("memory-compiled-truth-history registration", () => {
    it("registered in tool server index.ts as an eagerTool", () => {
        const src = readFileSync(resolve(import.meta.dirname, "../../index.ts"), "utf-8");
        expect(src).toMatch(/eagerTool\(server,\s*\n?\s*"memory-compiled-truth-history"/);
        expect(src).toContain('registered tool=memory-compiled-truth-history');
    });
    it("declared in PLUGIN.md with admin allowlist", () => {
        const plugin = readFileSync(resolve(import.meta.dirname, "../../../../PLUGIN.md"), "utf-8");
        expect(plugin).toContain("name: memory-compiled-truth-history");
        const idx = plugin.indexOf("name: memory-compiled-truth-history");
        expect(plugin.slice(idx, idx + 120)).toContain("adminAllowlist: true");
    });
});
//# sourceMappingURL=memory-compiled-truth-history.test.js.map