import { describe, it, expect } from "vitest";
import {
  createPendingOp,
  isValidPendingOp,
} from "../../../src/contracts/pending-op.js";
import { PENDING_OP_FIXTURES } from "../../fixtures/pending-op.js";

describe("pending-op contract", () => {
  describe("createPendingOp()", () => {
    it("creates a pending op with required fields", () => {
      const op = createPendingOp("op-1", "user-1", "hidden", "active");
      expect(op.opId).toBe("op-1");
      expect(op.userId).toBe("user-1");
      expect(op.targetState).toBe("hidden");
      expect(op.previousState).toBe("active");
      expect(typeof op.issuedAt).toBe("number");
      expect(Number.isFinite(op.issuedAt)).toBe(true);
      expect(op.issuedAt).toBeGreaterThan(0);
      expect(op.timeoutId).toBeNull();
    });
  });

  describe("isValidPendingOp()", () => {
    it("accepts valid fixture", () => {
      expect(() => isValidPendingOp(PENDING_OP_FIXTURES.valid)).not.toThrow();
    });

    it("accepts timeoutId: null", () => {
      expect(() => isValidPendingOp(PENDING_OP_FIXTURES.timeoutNull)).not.toThrow();
    });

    it("accepts expired issuedAt (age check is caller's job)", () => {
      expect(() => isValidPendingOp(PENDING_OP_FIXTURES.expiredIssuedAt)).not.toThrow();
    });

    it("throws on empty opId", () => {
      expect(() => isValidPendingOp(PENDING_OP_FIXTURES.emptyOpId)).toThrow(TypeError);
    });

    it("throws on non-finite issuedAt", () => {
      const bad = { ...PENDING_OP_FIXTURES.valid, issuedAt: NaN };
      expect(() => isValidPendingOp(bad)).toThrow(TypeError);
    });

    it("throws on negative issuedAt", () => {
      const bad = { ...PENDING_OP_FIXTURES.valid, issuedAt: -1 };
      expect(() => isValidPendingOp(bad)).toThrow(TypeError);
    });

    it("throws if not an object", () => {
      expect(() => isValidPendingOp(null)).toThrow(TypeError);
    });

    it("throws on unknown keys", () => {
      const bad = { ...PENDING_OP_FIXTURES.valid, extra: true };
      expect(() => isValidPendingOp(bad)).toThrow(TypeError);
    });

    it("throws on string timeoutId", () => {
      const bad = { ...PENDING_OP_FIXTURES.valid, timeoutId: "not-a-number" };
      expect(() => isValidPendingOp(bad)).toThrow(TypeError);
    });
  });
});
