import { describe, expect, test } from "bun:test";
import type { VoiceAgentConfig } from "./config.ts";
import { FakeVoiceProvider } from "./fake-voice-provider.ts";
import { SimulatedRingCentralClient } from "./simulated-media.ts";
import {
  EXULT_VOICE_AGENT_INSTRUCTIONS,
  EXULT_VOICE_AGENT_START_PROMPT,
  VoiceAgentRuntime,
} from "./voice-agent-runtime.ts";

describe("VoiceAgentRuntime", () => {
  test("starts with the approved Exult greeting", () => {
    expect(EXULT_VOICE_AGENT_START_PROMPT).toBe(
      'Say exactly: "Hi, this is Exult Healthcare. How can I help you?"',
    );
    expect(EXULT_VOICE_AGENT_INSTRUCTIONS).toContain(
      "When verification returns a next_appointment",
    );
  });

  test("confirms what it heard and retries before transferring to staff", () => {
    // Regression: live test call 4770bade-1d97-4022-ab58-780198477cb5
    // (2026-06-01T18:59:32Z) transferred on no_candidates without telling
    // the caller why, which felt like a crash. New UX: read back the
    // heard name + DOB, accept corrections, retry verify_patient_identity,
    // and only transfer after the retry also fails.
    expect(EXULT_VOICE_AGENT_INSTRUCTIONS).toContain("no_candidates");
    expect(EXULT_VOICE_AGENT_INSTRUCTIONS).toContain("name_mismatch");
    expect(EXULT_VOICE_AGENT_INSTRUCTIONS).toContain("dob_mismatch");
    expect(EXULT_VOICE_AGENT_INSTRUCTIONS).toContain("multiple_candidates");
    expect(EXULT_VOICE_AGENT_INSTRUCTIONS).toContain("Is that right");
    expect(EXULT_VOICE_AGENT_INSTRUCTIONS).toContain("DOB as [Month Day, Year]");
    expect(EXULT_VOICE_AGENT_INSTRUCTIONS).toContain(
      "re-run verify_patient_identity with the corrected values",
    );
    expect(EXULT_VOICE_AGENT_INSTRUCTIONS).toContain("2 total verify_patient_identity attempts");
    expect(EXULT_VOICE_AGENT_INSTRUCTIONS).toContain("Do not silently transfer");
  });

  test("wires provider, media, and per-call scheduling policy", async () => {
    const client = new SimulatedRingCentralClient();
    const toolCalls: string[] = [];
    const runtime = new VoiceAgentRuntime({
      config: config(),
      voiceProvider: new FakeVoiceProvider(),
      mediaClient: client,
      humanTransferDestination: "+15550999000",
      scheduling: () => ({
        async verifyPatientIdentity() {
          toolCalls.push("verify_patient_identity");
          return { ok: true, action: "continue", message: "verified" };
        },
        async findRescheduleOptions() {
          toolCalls.push("find_reschedule_options");
          return { ok: true, action: "offer_slots", message: "slots" };
        },
        async requestReschedule() {
          return { ok: false, action: "approval_required", message: "blocked" };
        },
        async createCallback() {
          return { ok: true, action: "callback_required", message: "callback" };
        },
        async transferToStaff() {
          return { ok: true, action: "transfer_to_staff", message: "transfer" };
        },
      }),
    });

    const started = await runtime.start();
    const call = await client.receiveCall({ callId: "call-1", callerNumber: "+19725550100" });
    await call.callerSays("I need to reschedule my appointment");
    await call.close();
    await runtime.stop();

    expect(started.provider).toBe("openai_realtime");
    expect(started.media).toBe("injected");
    expect(toolCalls).toEqual(["find_reschedule_options"]);
  });
});

function config(): VoiceAgentConfig {
  const endpoint = (name: string) => ({
    name,
    mcpUrl: `https://example.com/${name}/mcp`,
    bearerToken: "token",
    requiresPhiTransport: true,
  });
  return {
    defaultVoiceProvider: "openai_realtime",
    voiceProviderFailover: false,
    openai: {
      apiKey: "openai",
      model: "gpt-realtime",
      realtimeUrl: "wss://api.openai.com/v1/realtime",
      voice: "alloy",
    },
    xai: {
      apiKey: "xai",
      model: "grok-voice-think-fast-1.0",
      realtimeUrl: "wss://api.x.ai/v1/realtime",
      voice: "eve",
      phiAllowed: false,
    },
    audio: {
      ringCentralFormat: { codec: "opus", sampleRateHz: 16000, channels: 1 },
      providerFormat: { codec: "pcm16", sampleRateHz: 16000, channels: 1 },
    },
    mcp: {
      advancedmd: endpoint("advancedmd"),
      ringcentral: endpoint("ringcentral"),
      ringcentralAdmin: endpoint("ringcentral-admin"),
    },
    scheduling: {
      maxConcurrentTransactions: 1,
      amdLookupTimeoutMs: 6000,
    },
  };
}
