/**
 * Unit tests for WhatsApp AI Agent E2E Flow
 *
 * Tests the complete user journey through mocked route handlers:
 * identify → language → query → availability → booking
 *
 * This replaces the integration test that required a running server + database.
 */

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

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

jest.mock("@/lib/services/system-settings.service", () => ({
  systemSettingsService: {
    validateN8NToken: jest.fn().mockResolvedValue(true),
  },
}));

jest.mock("@/lib/services/whatsapp-company-identification.service", () => ({
  identifyCompanyByPhone: jest.fn(),
}));

jest.mock("@/lib/services/whatsapp-language.service", () => ({
  getLanguagePreference: jest.fn(),
  setLanguagePreference: jest.fn(),
}));

jest.mock("@/lib/services/whatsapp-knowledge-base.service", () => ({
  queryKnowledgeBase: jest.fn(),
}));

jest.mock("@/lib/services/whatsapp-availability.service", () => ({
  getAvailableSlots: jest.fn(),
  isSlotAvailable: jest.fn(),
}));

jest.mock("@/lib/services/whatsapp-feature-access.service", () => ({
  requireFeatureAccess: jest.fn(),
  getFeatureAccess: jest.fn(),
  getFeatureUsage: jest.fn(),
  isWithinLimits: jest.fn(),
}));

jest.mock("@/lib/validators/whatsapp-ai.validator", () => ({
  CompanyIdentificationQuerySchema: { safeParse: jest.fn() },
  LanguagePreferenceGetSchema: { safeParse: jest.fn() },
  LanguagePreferenceSetSchema: { safeParse: jest.fn() },
  KnowledgeBaseQuerySchema: { safeParse: jest.fn() },
  AvailabilityQuerySchema: { safeParse: jest.fn() },
  BookingCreateSchema: { safeParse: jest.fn() },
  FeatureAccessQuerySchema: { safeParse: jest.fn() },
  OrganizationIdSchema: { safeParse: jest.fn() },
  PhoneNumberSchema: { safeParse: jest.fn() },
}));

jest.mock("next/server", () => {
  const NextResponseMock = function (
    body: string | null,
    init?: Record<string, unknown>,
  ) {
    return {
      status: (init?.status as number) || 200,
      headers: new Map(
        Object.entries((init?.headers as Record<string, string>) || {}),
      ),
      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 { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/db";
import { identifyCompanyByPhone } from "@/lib/services/whatsapp-company-identification.service";
import {
  getLanguagePreference,
  setLanguagePreference,
} from "@/lib/services/whatsapp-language.service";
import { queryKnowledgeBase } from "@/lib/services/whatsapp-knowledge-base.service";
import {
  getAvailableSlots,
  isSlotAvailable,
} from "@/lib/services/whatsapp-availability.service";
import { requireFeatureAccess } from "@/lib/services/whatsapp-feature-access.service";
import {
  CompanyIdentificationQuerySchema,
  LanguagePreferenceGetSchema,
  LanguagePreferenceSetSchema,
  KnowledgeBaseQuerySchema,
  AvailabilityQuerySchema,
  BookingCreateSchema,
} from "@/lib/validators/whatsapp-ai.validator";

// Routes not yet implemented
describe.skip("WhatsApp AI Agent - E2E Flow (Unit)", () => {
  let identifyModule: Record<
    string,
    (req: unknown) => Promise<{
      status: number;
      json: () => Promise<Record<string, unknown>>;
    }>
  >;
  let langModule: Record<
    string,
    (req: unknown) => Promise<{
      status: number;
      json: () => Promise<Record<string, unknown>>;
    }>
  >;
  let kbModule: Record<
    string,
    (req: unknown) => Promise<{
      status: number;
      json: () => Promise<Record<string, unknown>>;
    }>
  >;
  let availModule: Record<
    string,
    (req: unknown) => Promise<{
      status: number;
      json: () => Promise<Record<string, unknown>>;
    }>
  >;
  let bookingModule: Record<
    string,
    (req: unknown) => Promise<{
      status: number;
      json: () => Promise<Record<string, unknown>>;
    }>
  >;

  beforeAll(async () => {
    identifyModule = await import("@/app/api/whatsapp/identify-company/route");
    langModule = await import("@/app/api/whatsapp/language-preference/route");
    kbModule = await import("@/app/api/whatsapp/knowledge-base/query/route");
    availModule = await import("@/app/api/whatsapp/availability/route");
    bookingModule = await import("@/app/api/whatsapp/bookings/route");
  });

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

  const mockGetRequest = (params?: Record<string, string>) =>
    ({
      headers: {
        get: jest.fn((key: string) => {
          if (key === "authorization") return "Bearer valid-token";
          return null;
        }),
      },
      nextUrl: { searchParams: new URLSearchParams(params || {}) },
    }) as unknown as NextRequest;

  const mockPostRequest = (body?: Record<string, unknown>) =>
    ({
      headers: {
        get: jest.fn((key: string) => {
          if (key === "authorization") return "Bearer valid-token";
          return null;
        }),
      },
      json: jest.fn().mockResolvedValue(body || {}),
    }) as unknown as NextRequest;

  it("should complete full journey: identify → language → query → availability → booking", async () => {
    // Step 1: Identify company
    (CompanyIdentificationQuerySchema.safeParse as jest.Mock).mockReturnValue({
      success: true,
      data: { phone: "1234567890" },
    });
    (identifyCompanyByPhone as jest.Mock).mockResolvedValue({
      companyId: "org-e2e",
      companyName: "E2E Medical Center",
      subscriptionTier: "professional",
    });

    const identifyResponse = await identifyModule.GET(
      mockGetRequest({ phone: "1234567890" }),
    );
    const identifyData = await identifyResponse.json();
    expect(identifyData.data.companyId).toBe("org-e2e");

    // Step 2: Check language preference (first-time user)
    (LanguagePreferenceGetSchema.safeParse as jest.Mock).mockReturnValue({
      success: true,
      data: { organizationId: "org-e2e", phone: "+966501234567" },
    });
    (getLanguagePreference as jest.Mock).mockResolvedValue(null);

    const langCheckResponse = await langModule.GET(
      mockGetRequest({
        organizationId: "org-e2e",
        phone: "+966501234567",
      }),
    );
    const langCheckData = await langCheckResponse.json();
    expect(langCheckData.data.language).toBeNull();

    // Step 3: Save language preference
    (LanguagePreferenceSetSchema.safeParse as jest.Mock).mockReturnValue({
      success: true,
      data: {
        organizationId: "org-e2e",
        phone: "+966501234567",
        language: "en",
      },
    });
    (setLanguagePreference as jest.Mock).mockResolvedValue({
      organizationId: "org-e2e",
      userId: "+966501234567",
      language: "en",
      selectedAt: new Date(),
    });

    const langSaveResponse = await langModule.POST(
      mockPostRequest({
        organizationId: "org-e2e",
        phone: "+966501234567",
        language: "en",
      }),
    );
    const langSaveData = await langSaveResponse.json();
    expect(langSaveData.data.language).toBe("en");

    // Step 4: Query knowledge base
    (KnowledgeBaseQuerySchema.safeParse as jest.Mock).mockReturnValue({
      success: true,
      data: {
        organizationId: "org-e2e",
        query: "operating hours",
        language: "en",
        limit: 3,
      },
    });
    (queryKnowledgeBase as jest.Mock).mockResolvedValue([
      {
        question: "What are your operating hours?",
        answer: "We are open Monday to Friday 9 AM - 5 PM.",
        relevanceScore: 95,
        language: "en",
      },
    ]);

    const kbResponse = await kbModule.POST(mockPostRequest());
    const kbData = await kbResponse.json();
    expect(kbData.data.results[0].answer).toContain("9 AM - 5 PM");

    // Step 5: Check availability
    (AvailabilityQuerySchema.safeParse as jest.Mock).mockReturnValue({
      success: true,
      data: {
        organizationId: "org-e2e",
        date: "2026-03-02",
        duration: 30,
      },
    });
    (getAvailableSlots as jest.Mock).mockResolvedValue({
      date: "2026-03-02",
      timezone: "Asia/Riyadh",
      slots: [
        { startTime: "09:00", endTime: "09:30", available: true },
        { startTime: "09:30", endTime: "10:00", available: true },
        { startTime: "10:00", endTime: "10:30", available: false },
      ],
    });

    const availResponse = await availModule.GET(mockGetRequest());
    const availData = await availResponse.json();
    const slots = (availData.data as Record<string, unknown>).slots as Array<
      Record<string, unknown>
    >;
    const availableSlot = slots.find((s) => s.available);
    expect(availableSlot).toBeDefined();

    // Step 6: Create booking
    (BookingCreateSchema.safeParse as jest.Mock).mockReturnValue({
      success: true,
      data: {
        organizationId: "org-e2e",
        customerName: "Ahmed Ali",
        customerPhone: "+966501234567",
        customerEmail: "ahmed@example.com",
        appointmentDate: "2026-03-02T09:00:00Z",
        duration: 30,
        language: "en",
      },
    });
    (requireFeatureAccess as jest.Mock).mockResolvedValue(undefined);
    (isSlotAvailable as jest.Mock).mockResolvedValue(true);
    (prisma.organizations.findUnique as jest.Mock).mockResolvedValue({
      ownerId: "owner-1",
    });
    (prisma.bookings.create as jest.Mock).mockResolvedValue({
      id: "wa_e2e_booking",
      status: "pending",
    });

    const bookingResponse = await bookingModule.POST(mockPostRequest());
    const bookingData = await bookingResponse.json();
    expect(bookingData.success).toBe(true);
    expect(bookingData.data.bookingId).toBe("wa_e2e_booking");
    expect(bookingData.data.confirmationMessage).toContain("confirmed");
  });

  it("should handle Arabic language flow", async () => {
    // Save Arabic preference
    (LanguagePreferenceSetSchema.safeParse as jest.Mock).mockReturnValue({
      success: true,
      data: {
        organizationId: "org-e2e",
        phone: "+966507777777",
        language: "ar",
      },
    });
    (setLanguagePreference as jest.Mock).mockResolvedValue({
      language: "ar",
      selectedAt: new Date(),
    });

    const langResponse = await langModule.POST(mockPostRequest());
    const langData = await langResponse.json();
    expect(langData.data.language).toBe("ar");

    // Create booking with Arabic confirmation
    (BookingCreateSchema.safeParse as jest.Mock).mockReturnValue({
      success: true,
      data: {
        organizationId: "org-e2e",
        customerName: "محمد علي",
        customerPhone: "+966507777777",
        appointmentDate: "2026-03-02T10:00:00Z",
        duration: 30,
        language: "ar",
      },
    });
    (requireFeatureAccess as jest.Mock).mockResolvedValue(undefined);
    (isSlotAvailable as jest.Mock).mockResolvedValue(true);
    (prisma.organizations.findUnique as jest.Mock).mockResolvedValue({
      ownerId: "owner-1",
    });
    (prisma.bookings.create as jest.Mock).mockResolvedValue({
      id: "wa_arabic_booking",
      status: "pending",
    });

    const bookingResponse = await bookingModule.POST(mockPostRequest());
    const bookingData = await bookingResponse.json();
    expect(bookingData.data.confirmationMessage).toContain("تم تأكيد");
    expect(bookingData.data.confirmationMessage).toContain("التاريخ:");
  });

  it("should enforce feature access based on subscription tier", async () => {
    (BookingCreateSchema.safeParse as jest.Mock).mockReturnValue({
      success: true,
      data: {
        organizationId: "starter-org",
        customerName: "Test",
        customerPhone: "+966501234567",
        appointmentDate: "2026-03-02T10:00:00Z",
        duration: 30,
        language: "en",
      },
    });
    (requireFeatureAccess as jest.Mock).mockRejectedValue(
      new Error("Appointments not available on your plan"),
    );

    const response = await bookingModule.POST(mockPostRequest());
    expect(response.status).toBe(403);
    const body = await response.json();
    expect(body.error).toBe("Feature not available");
  });
});
