/**
 * Unit tests for Voice Agent extended routes:
 * - /api/voice-agent/conversations (GET)
 * - /api/voice-agent/phone (GET, POST, DELETE)
 */

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

jest.mock("@/lib/db");

jest.mock("@/lib/generated/prisma", () => ({
  Prisma: {
    JsonNull: "DbNull",
  },
}));

jest.mock("@/lib/auth", () => ({
  auth: jest.fn(),
}));

jest.mock("@/lib/utils/auth-helpers.util", () => ({
  getAuthenticatedUser: jest.fn(),
}));

jest.mock("@/lib/validators/voice-agent-phone.validator", () => ({
  phoneConfigSchema: {
    parse: jest.fn(),
  },
}));

jest.mock("@/lib/services/elevenlabs-phone.service", () => ({
  elevenLabsPhoneService: {
    configurePhone: jest.fn(),
    removePhone: jest.fn(),
  },
}));

jest.mock("next/server", () => ({
  NextRequest: jest.fn(),
  NextResponse: {
    json: jest.fn((data, options) => ({
      status: options?.status || 200,
      headers: new Map(),
      json: async () => data,
    })),
  },
}));

import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/db";
import { getAuthenticatedUser } from "@/lib/utils/auth-helpers.util";
import { auth } from "@/lib/auth";
import { phoneConfigSchema } from "@/lib/validators/voice-agent-phone.validator";
import { elevenLabsPhoneService } from "@/lib/services/elevenlabs-phone.service";
import { z } from "zod";

// ─── Conversations ────────────────────────────────────────────────────────

// Voice agent routes not yet implemented
describe.skip("GET /api/voice-agent/conversations", () => {
  let convModule: any;

  beforeAll(async () => {
    convModule = await import("@/app/api/voice-agent/conversations/route");
  });

  beforeEach(() => {
    jest.clearAllMocks();
    (NextResponse.json as jest.Mock).mockImplementation((data, options) => ({
      status: options?.status || 200,
      headers: new Map(),
      json: async () => data,
    }));
  });

  const mockRequest = (params?: Record<string, string>) =>
    ({
      headers: { get: jest.fn(() => null) },
      nextUrl: {
        searchParams: new URLSearchParams(params || {}),
      },
    }) as unknown as NextRequest;

  it("should return 401 if not authenticated", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(null);

    const response = await convModule.GET(mockRequest());
    const body = await response.json();
    expect(response.status).toBe(401);
    expect(body.error).toBe("Unauthorized");
  });

  it("should return paginated conversations", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue({
      userId: "user-1",
    });
    (prisma.conversationLog.findMany as jest.Mock).mockResolvedValue([
      {
        id: "conv-1",
        customerPhone: "+97412345678",
        customerName: "Test",
        status: "resolved",
        metadata: { durationSeconds: 120, sentiment: "positive" },
        messages: [],
        createdAt: new Date(),
      },
    ]);
    (prisma.conversationLog.count as jest.Mock).mockResolvedValue(1);

    const response = await convModule.GET(
      mockRequest({ page: "1", pageSize: "10" }),
    );
    const body = await response.json();
    expect(body.success).toBe(true);
    expect(body.conversations).toHaveLength(1);
    expect(body.pagination.total).toBe(1);
  });

  it("should return 500 on error", async () => {
    (getAuthenticatedUser as jest.Mock).mockRejectedValue(
      new Error("DB error"),
    );

    const response = await convModule.GET(mockRequest());
    const body = await response.json();
    expect(response.status).toBe(500);
  });
});

// ─── Phone Config ─────────────────────────────────────────────────────────

// Voice agent routes not yet implemented
describe.skip("GET /api/voice-agent/phone", () => {
  let phoneModule: any;

  beforeAll(async () => {
    phoneModule = await import("@/app/api/voice-agent/phone/route");
  });

  beforeEach(() => {
    jest.clearAllMocks();
    (NextResponse.json as jest.Mock).mockImplementation((data, options) => ({
      status: options?.status || 200,
      headers: new Map(),
      json: async () => data,
    }));
  });

  const mockRequest = () =>
    ({
      headers: { get: jest.fn(() => null) },
    }) as unknown as NextRequest;

  it("should return 401 if not authenticated", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(null);

    const response = await phoneModule.GET(mockRequest());
    const body = await response.json();
    expect(response.status).toBe(401);
  });

  it("should return null phoneConfig when none exists", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue({
      userId: "user-1",
    });
    (prisma.voiceAgentPhoneConfig.findUnique as jest.Mock).mockResolvedValue(
      null,
    );

    const response = await phoneModule.GET(mockRequest());
    const body = await response.json();
    expect(body.success).toBe(true);
    expect(body.phoneConfig).toBeNull();
  });

  it("should return phone config when exists", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue({
      userId: "user-1",
    });
    (prisma.voiceAgentPhoneConfig.findUnique as jest.Mock).mockResolvedValue({
      connectionType: "twilio_native",
      phoneNumber: "+97412345678",
      isActive: true,
      elevenlabsPhoneId: "ph-1",
      twilioAccountSid: "AC123",
      sipUri: null,
      createdAt: new Date(),
      updatedAt: new Date(),
    });

    const response = await phoneModule.GET(mockRequest());
    const body = await response.json();
    expect(body.success).toBe(true);
    expect(body.phoneConfig.connectionType).toBe("twilio_native");
    expect(body.phoneConfig.hasTwilio).toBe(true);
  });
});

// Voice agent routes not yet implemented
describe.skip("POST /api/voice-agent/phone", () => {
  let phoneModule: any;

  beforeAll(async () => {
    phoneModule = await import("@/app/api/voice-agent/phone/route");
  });

  beforeEach(() => {
    jest.clearAllMocks();
    (NextResponse.json as jest.Mock).mockImplementation((data, options) => ({
      status: options?.status || 200,
      headers: new Map(),
      json: async () => data,
    }));
  });

  const mockRequest = (body?: any) =>
    ({
      headers: { get: jest.fn(() => null) },
      json: jest.fn().mockResolvedValue(body || {}),
    }) as unknown as NextRequest;

  it("should return 401 if not authenticated", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(null);

    const response = await phoneModule.POST(mockRequest());
    const body = await response.json();
    expect(response.status).toBe(401);
  });

  it("should return 400 if no voice agent configured", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue({
      userId: "user-1",
    });
    (phoneConfigSchema.parse as jest.Mock).mockReturnValue({
      connectionType: "twilio_native",
      phoneNumber: "+97412345678",
    });
    (prisma.users.findUnique as jest.Mock).mockResolvedValue({
      voiceAgentId: null,
      voiceAgentConfig: null,
    });

    const response = await phoneModule.POST(mockRequest({}));
    const body = await response.json();
    expect(response.status).toBe(400);
    expect(body.error).toBe("Voice agent must be configured first");
  });

  it("should return 400 if validation fails", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue({
      userId: "user-1",
    });
    (phoneConfigSchema.parse as jest.Mock).mockImplementation(() => {
      throw new z.ZodError([
        { code: "custom", message: "Invalid", path: ["connectionType"] },
      ]);
    });

    const response = await phoneModule.POST(mockRequest({}));
    const body = await response.json();
    expect(response.status).toBe(400);
    expect(body.error).toBe("Validation error");
  });

  it("should create phone config successfully", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue({
      userId: "user-1",
    });
    (phoneConfigSchema.parse as jest.Mock).mockReturnValue({
      connectionType: "twilio_native",
      phoneNumber: "+97412345678",
      twilio: { twilioPhoneNumber: "+97412345678" },
    });
    (prisma.users.findUnique as jest.Mock).mockResolvedValue({
      voiceAgentId: "agent-1",
      voiceAgentConfig: {},
    });
    (elevenLabsPhoneService.configurePhone as jest.Mock).mockResolvedValue({
      success: true,
      phoneId: "ph-1",
    });
    (prisma.voiceAgentPhoneConfig.upsert as jest.Mock).mockResolvedValue({
      connectionType: "twilio_native",
      phoneNumber: "+97412345678",
      isActive: true,
    });
    (prisma.users.update as jest.Mock).mockResolvedValue({});

    const response = await phoneModule.POST(mockRequest({}));
    const body = await response.json();
    expect(body.success).toBe(true);
    expect(body.phoneConfig.connectionType).toBe("twilio_native");
  });
});

// Voice agent routes not yet implemented
describe.skip("DELETE /api/voice-agent/phone", () => {
  let phoneModule: any;

  beforeAll(async () => {
    phoneModule = await import("@/app/api/voice-agent/phone/route");
  });

  beforeEach(() => {
    jest.clearAllMocks();
    (NextResponse.json as jest.Mock).mockImplementation((data, options) => ({
      status: options?.status || 200,
      headers: new Map(),
      json: async () => data,
    }));
  });

  const mockRequest = () =>
    ({
      headers: { get: jest.fn(() => null) },
    }) as unknown as NextRequest;

  it("should return 401 if not authenticated", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue(null);

    const response = await phoneModule.DELETE(mockRequest());
    const body = await response.json();
    expect(response.status).toBe(401);
  });

  it("should delete phone config successfully", async () => {
    (getAuthenticatedUser as jest.Mock).mockResolvedValue({
      userId: "user-1",
    });
    (prisma.users.findUnique as jest.Mock)
      .mockResolvedValueOnce({ voiceAgentId: "agent-1" })
      .mockResolvedValueOnce({ voiceAgentConfig: { phoneConfig: {} } });
    (elevenLabsPhoneService.removePhone as jest.Mock).mockResolvedValue(
      undefined,
    );
    (prisma.voiceAgentPhoneConfig.deleteMany as jest.Mock).mockResolvedValue({
      count: 1,
    });
    (prisma.users.update as jest.Mock).mockResolvedValue({});

    const response = await phoneModule.DELETE(mockRequest());
    const body = await response.json();
    expect(body.success).toBe(true);
  });
});
