import { describe, expect, test } from "bun:test";
import {
  findNextAppointment,
  ReadOnlyAdvancedMdSchedulingPort,
} from "./advancedmd-scheduling-port.ts";
import type { AdvancedMdReadGateway } from "./advancedmd-gateway.ts";

describe("ReadOnlyAdvancedMdSchedulingPort", () => {
  test("verifies a single matching AMD patient before allowing appointment history lookup", async () => {
    const reads = fakeReads();
    const port = new ReadOnlyAdvancedMdSchedulingPort({
      reads,
      callerNumber: "+19725550100",
    });

    const verified = await port.verifyPatientIdentity({
      stated_legal_name: "Ada Lovelace",
      stated_dob: "1815-12-10",
    });
    expect(verified.action).toBe("continue");
    expect(verified.data?.patient_id).toBe("patient-1");
    expect(verified.data?.next_appointment).toMatchObject({
      appointment_id: "appt-1",
      provider_name: "Dr. Turing",
      spoken_date_time: "Thursday, January 15, 2099 at 9:30 AM",
    });

    const options = await port.findRescheduleOptions({
      patient_id: "patient-1",
      appointment_id: "appt-1",
    });
    expect(options.action).toBe("callback_required");
    expect(reads.historyLookups).toEqual(["patient-1", "patient-1"]);
  });

  test("uses incoming phone to select the right AMD patient when names collide", async () => {
    const port = new ReadOnlyAdvancedMdSchedulingPort({
      reads: {
        ...fakeReads(),
        async searchPatients() {
          return [
            {
              patientId: "patient-1",
              legalName: "Ada Lovelace",
              dob: "1815-12-10",
              phones: ["214-555-0100"],
            },
            {
              patientId: "patient-2",
              legalName: "Ada Lovelace",
              dob: "1815-12-10",
              phones: ["972-555-0100"],
            },
          ];
        },
      },
      callerNumber: "+19725550100",
    });

    const verified = await port.verifyPatientIdentity({
      stated_legal_name: "Ada Lovelace",
      stated_dob: "1815-12-10",
    });

    expect(verified.action).toBe("continue");
    expect(verified.data?.patient_id).toBe("patient-2");
  });

  test("transfers when identity cannot be verified", async () => {
    const port = new ReadOnlyAdvancedMdSchedulingPort({
      reads: fakeReads(),
      callerNumber: "+19725550100",
    });

    const result = await port.verifyPatientIdentity({
      stated_legal_name: "Grace Hopper",
      stated_dob: "1815-12-10",
    });

    expect(result.action).toBe("transfer_to_staff");
    expect(result.data?.reason).toBe("name_mismatch");
  });

  test("returns callback path when AMD patient search exceeds timeout", async () => {
    const port = new ReadOnlyAdvancedMdSchedulingPort({
      reads: {
        ...fakeReads(),
        async searchPatients() {
          return new Promise(() => {});
        },
      },
      callerNumber: "+19725550100",
      lookupTimeoutMs: 5,
    });

    const result = await port.verifyPatientIdentity({
      stated_legal_name: "Ada Lovelace",
      stated_dob: "1815-12-10",
    });

    expect(result.action).toBe("callback_required");
    expect(result.data).toMatchObject({
      reason: "amd_lookup_timeout",
      step: "patient verification",
    });
  });

  test("returns callback path when AMD appointment history exceeds timeout during verification", async () => {
    const port = new ReadOnlyAdvancedMdSchedulingPort({
      reads: {
        ...fakeReads(),
        async getAppointmentHistory() {
          return new Promise(() => {});
        },
      },
      callerNumber: "+19725550100",
      lookupTimeoutMs: 5,
    });

    const result = await port.verifyPatientIdentity({
      stated_legal_name: "Ada Lovelace",
      stated_dob: "1815-12-10",
    });

    expect(result.action).toBe("callback_required");
    expect(result.data).toMatchObject({
      reason: "amd_lookup_timeout",
      step: "appointment lookup",
    });
  });

  test("blocks AdvancedMD write request before positive patient identification", async () => {
    const port = new ReadOnlyAdvancedMdSchedulingPort({
      reads: fakeReads(),
      callerNumber: "+19725550100",
    });

    const result = await port.requestReschedule(
      { patient_id: "patient-1", appointment_id: "appt-1" },
      {
        approvedBy: "gautam",
        approvedAt: "2026-06-01T00:00:00.000Z",
        scope: "verified-patient-reschedule",
      },
    );

    expect(result.action).toBe("transfer_to_staff");
    expect(result.data).toMatchObject({ reason: "patient_not_verified" });
  });

  test("requires the write request patient to match the verified patient", async () => {
    const port = new ReadOnlyAdvancedMdSchedulingPort({
      reads: fakeReads(),
      callerNumber: "+19725550100",
    });

    await port.verifyPatientIdentity({
      stated_legal_name: "Ada Lovelace",
      stated_dob: "1815-12-10",
    });
    const result = await port.requestReschedule(
      { patient_id: "other-patient", appointment_id: "appt-1" },
      {
        approvedBy: "gautam",
        approvedAt: "2026-06-01T00:00:00.000Z",
        scope: "verified-patient-reschedule",
      },
    );

    expect(result.action).toBe("transfer_to_staff");
    expect(result.data).toMatchObject({ reason: "patient_not_verified" });
  });

  test("passes the verified-patient gate but fails closed without a narrow write gateway", async () => {
    const port = new ReadOnlyAdvancedMdSchedulingPort({
      reads: fakeReads(),
      callerNumber: "+19725550100",
    });

    await port.verifyPatientIdentity({
      stated_legal_name: "Ada Lovelace",
      stated_dob: "1815-12-10",
    });
    const result = await port.requestReschedule(
      { patient_id: "patient-1", appointment_id: "appt-1" },
      {
        approvedBy: "gautam",
        approvedAt: "2026-06-01T00:00:00.000Z",
        scope: "verified-patient-reschedule",
      },
    );

    expect(result.action).toBe("approval_required");
    expect(result.data).toMatchObject({ reason: "write_gateway_not_configured" });
  });

  test("extracts the next non-canceled appointment from AMD history rows", () => {
    const next = findNextAppointment(
      {
        rows: [
          {
            id: "old",
            date: "01/01/2026 10:00 AM",
            apptstatus: "Seen",
            columnheading: "Dr. Old",
          },
          {
            id: "canceled",
            date: "06/02/2026 9:00 AM",
            apptstatus: "Cancelled",
            columnheading: "Dr. Skip",
          },
          {
            id: "next",
            date: "06/03/2026 2:15 PM",
            apptstatus: "Scheduled",
            columnheading: "Dr. Next",
          },
        ],
      },
      new Date("2026-06-01T12:00:00.000Z"),
    );

    expect(next).toMatchObject({
      appointmentId: "next",
      providerName: "Dr. Next",
      spokenDateTime: "Wednesday, June 3 at 2:15 PM",
    });
  });
});

function fakeReads(): AdvancedMdReadGateway & { historyLookups: string[] } {
  const historyLookups: string[] = [];
  return {
    historyLookups,
    async authStatus() {
      return {};
    },
    async searchPatients() {
      return [
        {
          patientId: "patient-1",
          legalName: "Ada Lovelace",
          dob: "1815-12-10",
          phones: ["972-555-0100"],
        },
      ];
    },
    async getAppointmentHistory(patientId: string) {
      historyLookups.push(patientId);
      return {
        rows: [
          {
            id: "appt-1",
            date: "01/15/2099 9:30 AM",
            apptstatus: "Scheduled",
            columnheading: "Dr. Turing",
          },
        ],
      };
    },
    async getVisitInfoByDate() {
      return {};
    },
    async getFieldsetInfo() {
      return {};
    },
  };
}
