/**
 * Tests for Twilio Inbound Call Webhook
 * POST /api/voice/twilio/inbound
 *
 * NOTE: Twilio webhooks always return 200 with TwiML to prevent retries.
 */

// Mock dependencies BEFORE imports
jest.mock("@/lib/logger", () => ({
  logger: {
    warn: jest.fn(),
    error: jest.fn(),
    info: jest.fn(),
    debug: jest.fn(),
  },
}));

jest.mock("@/lib/services/twilio-voice.service", () => ({
  twilioVoiceService: {
    buildErrorTwiml: jest.fn(
      (msg: string) => `<Response><Say>${msg}</Say><Hangup/></Response>`,
    ),
  },
  twilioVoiceDb: {
    getPhoneNumberByNumber: jest.fn(),
    createVoiceCall: jest.fn(),
    updateVoiceCallStatus: jest.fn(),
  },
}));

jest.mock("@/lib/services/elevenlabs.service", () => ({
  elevenLabsService: {
    registerTwilioCall: jest.fn(),
  },
}));

jest.mock("@/lib/validators/twilio-voice.validator", () => ({
  twilioIncomingCallWebhookSchema: {
    safeParse: jest.fn(() => ({ success: true, data: {} })),
  },
}));

jest.mock("next/server", () => {
  const NextResponseMock = function (
    body: string | null,
    init?: Record<string, unknown>,
  ) {
    const status = (init?.status as number) || 200;
    const headers = new Map(
      Object.entries((init?.headers as Record<string, string>) || {}),
    );
    return {
      status,
      headers: { get: (k: string) => headers.get(k) || null },
      text: async () => body || "",
      json: async () => (body ? JSON.parse(body) : null),
    };
  };
  NextResponseMock.json = jest.fn(
    (data: unknown, options?: { status?: number }) => ({
      status: options?.status || 200,
      headers: new Map(),
      json: async () => data,
    }),
  );
  return { NextRequest: jest.fn(), NextResponse: NextResponseMock };
});

import { twilioVoiceDb } from "@/lib/services/twilio-voice.service";
import { elevenLabsService } from "@/lib/services/elevenlabs.service";

// Voice agent routes not yet implemented
describe.skip("POST /api/voice/twilio/inbound", () => {
  let inboundModule: {
    POST: (req: unknown) => Promise<{
      status: number;
      headers: { get: (k: string) => string | null };
      text: () => Promise<string>;
    }>;
  };

  beforeAll(async () => {
    inboundModule = await import("@/app/api/voice/twilio/inbound/route");
  });

  beforeEach(() => {
    jest.clearAllMocks();
  });

  // The route uses request.formData() — mock it
  const mockRequest = (params: Record<string, string>) => ({
    formData: jest.fn().mockResolvedValue(new Map(Object.entries(params))),
  });

  it("should return 200 with error TwiML when phone not found", async () => {
    (twilioVoiceDb.getPhoneNumberByNumber as jest.Mock).mockResolvedValue(null);

    const response = await inboundModule.POST(
      mockRequest({
        CallSid: "CA123",
        From: "+1234567890",
        To: "+0987654321",
        AccountSid: "AC123",
      }),
    );

    // Twilio always gets 200
    expect(response.status).toBe(200);
    expect(response.headers.get("Content-Type")).toBe("text/xml");
    const text = await response.text();
    expect(text).toContain("Sorry");
    expect(text).toContain("not configured");
  });

  it("should return 200 with error TwiML when phone is inactive", async () => {
    (twilioVoiceDb.getPhoneNumberByNumber as jest.Mock).mockResolvedValue({
      id: "phone-1",
      userId: "user-1",
      status: "SUSPENDED",
      elevenLabsAgentId: "agent-1",
    });

    const response = await inboundModule.POST(
      mockRequest({
        CallSid: "CA123",
        From: "+1234567890",
        To: "+0987654321",
        AccountSid: "AC123",
      }),
    );

    expect(response.status).toBe(200);
    const text = await response.text();
    expect(text).toContain("temporarily unavailable");
  });

  it("should return 200 with error TwiML when no agent configured", async () => {
    (twilioVoiceDb.getPhoneNumberByNumber as jest.Mock).mockResolvedValue({
      id: "phone-1",
      userId: "user-1",
      status: "ACTIVE",
      elevenLabsAgentId: null,
    });

    const response = await inboundModule.POST(
      mockRequest({
        CallSid: "CA123",
        From: "+1234567890",
        To: "+0987654321",
        AccountSid: "AC123",
      }),
    );

    expect(response.status).toBe(200);
    const text = await response.text();
    expect(text).toContain("voice assistant is not available");
  });

  it("should register call with ElevenLabs and return TwiML", async () => {
    (twilioVoiceDb.getPhoneNumberByNumber as jest.Mock).mockResolvedValue({
      id: "phone-1",
      userId: "user-1",
      status: "ACTIVE",
      elevenLabsAgentId: "agent-123",
    });
    (twilioVoiceDb.createVoiceCall as jest.Mock).mockResolvedValue({
      id: "call-1",
    });
    (elevenLabsService.registerTwilioCall as jest.Mock).mockResolvedValue({
      conversationId: "conv-123",
      twiml:
        '<Response><Connect><Stream url="wss://api.elevenlabs.io" /></Connect></Response>',
    });
    (twilioVoiceDb.updateVoiceCallStatus as jest.Mock).mockResolvedValue({});

    const response = await inboundModule.POST(
      mockRequest({
        CallSid: "CA123",
        From: "+1234567890",
        To: "+0987654321",
        AccountSid: "AC123",
        FromCity: "Doha",
        FromCountry: "QA",
      }),
    );

    expect(response.status).toBe(200);
    expect(response.headers.get("Content-Type")).toBe("text/xml");
    const text = await response.text();
    expect(text).toContain("<Response>");
    expect(text).toContain("<Connect>");

    expect(elevenLabsService.registerTwilioCall).toHaveBeenCalledWith(
      "agent-123",
      "+1234567890",
      "+0987654321",
      "inbound",
      expect.objectContaining({
        dynamicVariables: expect.objectContaining({
          caller_number: "+1234567890",
          caller_city: "Doha",
          caller_country: "QA",
        }),
      }),
    );

    expect(twilioVoiceDb.createVoiceCall).toHaveBeenCalled();
    expect(twilioVoiceDb.updateVoiceCallStatus).toHaveBeenCalledWith(
      "CA123",
      "RINGING",
      expect.objectContaining({ elevenLabsConversationId: "conv-123" }),
    );
  });

  it("should return 200 with error TwiML when ElevenLabs fails", async () => {
    (twilioVoiceDb.getPhoneNumberByNumber as jest.Mock).mockResolvedValue({
      id: "phone-1",
      userId: "user-1",
      status: "ACTIVE",
      elevenLabsAgentId: "agent-123",
    });
    (twilioVoiceDb.createVoiceCall as jest.Mock).mockResolvedValue({
      id: "call-1",
    });
    (elevenLabsService.registerTwilioCall as jest.Mock).mockRejectedValue(
      new Error("ElevenLabs API error"),
    );

    const response = await inboundModule.POST(
      mockRequest({
        CallSid: "CA123",
        From: "+1234567890",
        To: "+0987654321",
        AccountSid: "AC123",
      }),
    );

    // Twilio always gets 200, not 503
    expect(response.status).toBe(200);
    const text = await response.text();
    expect(text).toContain("technical difficulties");
  });
});
