import { describe, expect, it } from "vitest";
import {
  backfillRange,
  CanonicalEventStore,
} from "../../indexer/event-store";
import type { VersionedEvent } from "../../indexer/versioned-read-model";

const txHash = `0x${"11".repeat(32)}` as const;
const blockHash = `0x${"22".repeat(32)}` as const;

function event(overrides: Partial<VersionedEvent> = {}): VersionedEvent {
  return {
    uid: { chainId: 8453, txHash, logIndex: 7 },
    blockNumber: 100n,
    blockHash,
    removed: false,
    protocolVersion: "v3",
    contractStatus: "active",
    contract: "PartnershipBondV3Safe",
    address: `0x${"33".repeat(20)}`,
    eventSig: "BondCreatedV3(uint256,address,address,bytes32)",
    args: { bondId: "1" },
    observedAt: "2026-08-20T00:00:00.000Z",
    finality: "safe",
    ...overrides,
  };
}

describe("CanonicalEventStore", () => {
  it("deduplicates the required chainId/txHash/logIndex identity", () => {
    const store = new CanonicalEventStore();
    expect(store.apply(event())).toBe("inserted");
    expect(store.apply(event())).toBe("noop");
    expect(store.all()).toHaveLength(1);
  });

  it("replaces canonical block metadata after a reorg", () => {
    const store = new CanonicalEventStore();
    store.apply(event());
    const replacement = event({
      blockNumber: 101n,
      blockHash: `0x${"44".repeat(32)}`,
      args: { bondId: "1", canonical: true },
    });
    expect(store.apply(replacement)).toBe("updated");
    expect(store.get(replacement.uid)?.blockHash).toBe(replacement.blockHash);
    expect(store.all()).toHaveLength(1);
  });

  it("deletes removed logs", () => {
    const store = new CanonicalEventStore();
    store.apply(event());
    expect(store.apply(event({ removed: true }))).toBe("removed");
    expect(store.all()).toHaveLength(0);
  });

  it("advances a successful historical backfill checkpoint", async () => {
    const store = new CanonicalEventStore();
    const result = await backfillRange(store, async () => [event()], 8453, 90n, 100n);
    expect(result).toEqual({ applied: 1, failed: false });
    expect(store.checkpoint(8453)).toEqual({
      chainId: 8453,
      nextBlock: 101n,
      stale: false,
    });
  });

  it("marks only the failing chain stale without manufacturing rows", async () => {
    const store = new CanonicalEventStore();
    store.apply(event());
    const result = await backfillRange(
      store,
      async () => { throw new Error("base rpc unavailable"); },
      137,
      1n,
      10n,
    );
    expect(result).toEqual({ applied: 0, failed: true, error: "base rpc unavailable" });
    expect(store.checkpoint(137)?.stale).toBe(true);
    expect(store.all()).toHaveLength(1);
  });
});
