import { describe, it, expect } from 'vitest';
import {
  makeFakePorts,
  replayTwice,
  isIdempotent,
  portalMessageBasic,
} from './index';
import { PolicyDeniedError, type IngestInteractionRequest } from '@insignia/iios-contracts';

describe('fake platform ports', () => {
  it('OPA defaults to allow, can be set to deny, and can time out', async () => {
    const ports = makeFakePorts();
    expect((await ports.opa.decide({})).allow).toBe(true);

    ports.opa.deny('nope');
    const denied = await ports.opa.decide({});
    expect(denied.allow).toBe(false);
    expect(denied.obligations[0]?.reason).toBe('nope');

    ports.opa.timeout();
    await expect(ports.opa.decide({})).rejects.toThrow('opa timeout');
  });

  it('MDM keeps an unknown handle UNRESOLVED, and can be made ambiguous', async () => {
    const ports = makeFakePorts();
    expect((await ports.mdm.resolveSourceHandle({})).status).toBe('UNRESOLVED');

    ports.mdm.ambiguous(0.42);
    const res = await ports.mdm.resolveSourceHandle({});
    expect(res.status).toBe('AMBIGUOUS');
    expect(res.canonicalEntityId).toBeUndefined();
    expect(res.confidence).toBe(0.42);
  });

  it('fail-closed pattern: a denied OPA decision yields PolicyDeniedError', async () => {
    const ports = makeFakePorts();
    ports.opa.deny();
    const decision = await ports.opa.decide({});
    // This is the guard the kernel will apply (Task 1.3).
    const guard = () => {
      if (!decision.allow) throw new PolicyDeniedError(decision.obligations[0]?.reason);
    };
    expect(guard).toThrow(PolicyDeniedError);
  });
});

describe('replay oracle', () => {
  it('replaying a fixture twice creates no duplicate business state', async () => {
    // In-memory "kernel": dedupes on the fixture's providerEventId.
    const store = new Map<string, string>();
    let seq = 0;
    const ingest = async (f: IngestInteractionRequest) => {
      const key = f.providerEventId ?? 'no-key';
      if (!store.has(key)) store.set(key, `int_${++seq}`);
      return { interactionId: store.get(key)! };
    };
    const countInteractions = async () => store.size;

    const result = await replayTwice(portalMessageBasic, ingest, countInteractions);
    expect(isIdempotent(result)).toBe(true);
    expect(result.countAfterFirst).toBe(1);
    expect(result.countAfterSecond).toBe(1);
  });
});
