import type {
  AppointmentRef,
  ApprovalToken,
  RescheduleSlot,
  VerifiedPatient,
} from "./types.ts";

export interface RescheduleRequest {
  patient: VerifiedPatient;
  currentAppointment: AppointmentRef;
  requestedSlot: RescheduleSlot;
  approval?: ApprovalToken;
}

export interface RescheduleResult {
  ok: boolean;
  disposition:
    | "rescheduled"
    | "no_valid_slot"
    | "approval_required"
    | "amd_write_failed";
  message: string;
}

export interface AdvancedMdWritePort {
  createReplacementAppointment(request: RescheduleRequest): Promise<{ appointmentId: string }>;
  deleteOldAppointment(request: RescheduleRequest): Promise<void>;
  verifyAppointment(appointmentId: string): Promise<boolean>;
  writeAuditNote(request: RescheduleRequest, newAppointmentId: string): Promise<void>;
  createUrgentCallback(request: RescheduleRequest, reason: string): Promise<void>;
}

export class SchedulingTransactionGate {
  private inFlight = 0;

  constructor(private readonly maxConcurrent: number) {}

  async run<T>(fn: () => Promise<T>): Promise<T> {
    if (this.inFlight >= this.maxConcurrent) {
      throw new Error("advancedmd transaction gate is busy");
    }
    this.inFlight += 1;
    try {
      return await fn();
    } finally {
      this.inFlight -= 1;
    }
  }
}

export class RescheduleGateway {
  constructor(
    private readonly writes: AdvancedMdWritePort,
    private readonly gate = new SchedulingTransactionGate(1),
  ) {}

  async reschedule(request: RescheduleRequest): Promise<RescheduleResult> {
    if (!request.approval) {
      return {
        ok: false,
        disposition: "approval_required",
        message: "AdvancedMD write approval is required before rescheduling.",
      };
    }

    return this.gate.run(async () => {
      let newAppointmentId: string | undefined;
      try {
        const created = await this.writes.createReplacementAppointment(request);
        newAppointmentId = created.appointmentId;
        const verifiedReplacement = await this.writes.verifyAppointment(newAppointmentId);
        if (!verifiedReplacement) {
          throw new Error("replacement appointment verification failed");
        }

        await this.writes.deleteOldAppointment(request);
        await this.writes.writeAuditNote(request, newAppointmentId);
        return {
          ok: true,
          disposition: "rescheduled",
          message: "Appointment changed and verified in AdvancedMD.",
        };
      } catch (err) {
        const reason = err instanceof Error ? err.message : String(err);
        await this.writes.createUrgentCallback(request, reason);
        return {
          ok: false,
          disposition: "amd_write_failed",
          message:
            "AdvancedMD write failed; caller must stay connected for live staff transfer.",
        };
      }
    });
  }
}
