import { describe, expect, test } from "bun:test";
import {
  RescheduleGateway,
  SchedulingTransactionGate,
  type AdvancedMdWritePort,
  type RescheduleRequest,
} from "./scheduling-gateway.ts";

function request(): RescheduleRequest {
  return {
    patient: { patientId: "p1", legalName: "Jane Smith" },
    currentAppointment: {
      appointmentId: "old",
      patientId: "p1",
      providerId: "prov",
      serviceCode: "therapy",
      startsAt: "2026-06-02T14:00:00.000Z",
    },
    requestedSlot: {
      providerId: "prov",
      serviceCode: "therapy",
      startsAt: "2026-06-03T14:00:00.000Z",
      durationMinutes: 60,
    },
  };
}

describe("RescheduleGateway", () => {
  test("refuses writes without approval", async () => {
    const gateway = new RescheduleGateway(fakeWrites());
    const result = await gateway.reschedule(request());
    expect(result.disposition).toBe("approval_required");
  });

  test("creates and verifies replacement before deleting old appointment", async () => {
    const calls: string[] = [];
    const gateway = new RescheduleGateway(fakeWrites(calls));
    const result = await gateway.reschedule({
      ...request(),
      approval: {
        approvedBy: "gautam",
        approvedAt: "2026-06-01T00:00:00.000Z",
        scope: "test-patient-reschedule",
      },
    });

    expect(result.ok).toBe(true);
    expect(calls).toEqual(["create", "verify:new", "delete", "audit"]);
  });

  test("creates urgent callback when a write fails", async () => {
    const calls: string[] = [];
    const writes = fakeWrites(calls);
    writes.deleteOldAppointment = async () => {
      calls.push("delete");
      throw new Error("delete failed");
    };
    const gateway = new RescheduleGateway(writes);
    const result = await gateway.reschedule({
      ...request(),
      approval: {
        approvedBy: "gautam",
        approvedAt: "2026-06-01T00:00:00.000Z",
        scope: "test-patient-reschedule",
      },
    });

    expect(result.disposition).toBe("amd_write_failed");
    expect(calls).toContain("callback:delete failed");
  });

  test("transaction gate rejects concurrent work", async () => {
    const gate = new SchedulingTransactionGate(1);
    let release!: () => void;
    const first = gate.run(
      () =>
        new Promise<void>((resolve) => {
          release = resolve;
        }),
    );

    await expect(gate.run(async () => undefined)).rejects.toThrow(/busy/);
    release();
    await first;
  });
});

function fakeWrites(calls: string[] = []): AdvancedMdWritePort {
  return {
    async createReplacementAppointment() {
      calls.push("create");
      return { appointmentId: "new" };
    },
    async deleteOldAppointment() {
      calls.push("delete");
    },
    async verifyAppointment(id: string) {
      calls.push(`verify:${id}`);
      return true;
    },
    async writeAuditNote() {
      calls.push("audit");
    },
    async createUrgentCallback(_request, reason) {
      calls.push(`callback:${reason}`);
    },
  };
}
