import type {
  RingCentralInvite,
  RingCentralMediaCall,
  RingCentralMediaClient,
} from "./ringcentral-bridge.ts";
import type { AudioFrame } from "./types.ts";

export class SimulatedRingCentralClient implements RingCentralMediaClient {
  private handler?: (invite: RingCentralInvite, call: RingCentralMediaCall) => Promise<void>;

  onInvite(handler: (invite: RingCentralInvite, call: RingCentralMediaCall) => Promise<void>): void {
    this.handler = handler;
  }

  async receiveCall(invite: RingCentralInvite): Promise<SimulatedRingCentralCall> {
    if (!this.handler) throw new Error("invite handler not registered");
    const call = new SimulatedRingCentralCall();
    await this.handler(invite, call);
    return call;
  }
}

export class SimulatedRingCentralCall implements RingCentralMediaCall {
  answered = false;
  transferredTo?: string;
  hungup = false;
  outboundAudio: AudioFrame[] = [];
  private audioHandlers: Array<(frame: AudioFrame) => void | Promise<void>> = [];
  private dtmfHandlers: Array<(digit: string) => void | Promise<void>> = [];
  private closeHandlers: Array<() => void | Promise<void>> = [];

  async answer(): Promise<void> {
    this.answered = true;
  }

  async sendAudio(frame: AudioFrame): Promise<void> {
    this.outboundAudio.push(frame);
  }

  async transfer(destination: string): Promise<void> {
    this.transferredTo = destination;
  }

  async hangup(): Promise<void> {
    this.hungup = true;
    await this.close();
  }

  onAudio(handler: (frame: AudioFrame) => void | Promise<void>): void {
    this.audioHandlers.push(handler);
  }

  onDtmf(handler: (digit: string) => void | Promise<void>): void {
    this.dtmfHandlers.push(handler);
  }

  onClose(handler: () => void | Promise<void>): void {
    this.closeHandlers.push(handler);
  }

  async callerSays(text: string): Promise<void> {
    const data = new TextEncoder().encode(text);
    for (const handler of this.audioHandlers) {
      await handler({
        data,
        format: { codec: "pcm16", sampleRateHz: 16000, channels: 1 },
      });
    }
  }

  async pressDtmf(digit: string): Promise<void> {
    for (const handler of this.dtmfHandlers) await handler(digit);
  }

  async close(): Promise<void> {
    for (const handler of this.closeHandlers) await handler();
  }
}
