/**
 * Unit Tests for Interview Bookings Service
 *
 * Tests booking creation, validation, duplicate prevention, and business hours enforcement
 */

import { interviewBookingsService } from "@/lib/services/interview-bookings.service";
import { prisma } from "@/lib/db";

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

// Inline mock for deleted @/lib/supabase module
const mockSupabaseFrom = jest.fn(() => ({
  insert: jest.fn().mockReturnThis(),
  select: jest.fn().mockReturnThis(),
  eq: jest.fn().mockReturnThis(),
  single: jest.fn(),
  update: jest.fn().mockReturnThis(),
}));

const supabase = { from: mockSupabaseFrom } as any;

jest.mock("@/lib/supabase", () => ({
  supabase: { from: mockSupabaseFrom },
}));

describe("Interview Bookings Service", () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe("createBooking", () => {
    const validBookingData = {
      applicationId: "app-123",
      jobId: "job-456",
      tokenId: "token-789",
      applicantName: "John Doe",
      applicantEmail: "john@example.com",
      applicantPhone: "+97150123456",
      preferredDate: "2025-11-25",
      preferredTime: "10:00",
      timezone: "Asia/Qatar",
      applicantNotes: "Prefer morning slot",
    };

    it.skip("should create booking with valid data", async () => {
      const mockBooking = {
        id: "booking-123",
        ...validBookingData,
        status: "pending",
        createdAt: new Date(),
      };

      (prisma.interview_bookings.findFirst as jest.Mock).mockResolvedValue(
        null,
      );
      (prisma.interview_bookings.create as jest.Mock).mockResolvedValue(
        mockBooking,
      );

      const result =
        await interviewBookingsService.createBooking(validBookingData);

      expect(result.success).toBe(true);
      expect(result.bookingId).toBe("booking-123");
      expect(prisma.interview_bookings.create).toHaveBeenCalledWith({
        data: expect.objectContaining({
          applicationId: validBookingData.applicationId,
          jobId: validBookingData.jobId,
          status: "pending",
        }),
      });
    });

    it.skip("should prevent duplicate bookings for same application", async () => {
      const existingBooking = {
        id: "booking-existing",
        applicationId: validBookingData.applicationId,
        status: "pending",
      };

      (prisma.interview_bookings.findFirst as jest.Mock).mockResolvedValue(
        existingBooking,
      );

      const result =
        await interviewBookingsService.createBooking(validBookingData);

      expect(result.success).toBe(false);
      expect(result.error).toContain("already exists");
      expect(prisma.interview_bookings.create).not.toHaveBeenCalled();
    });

    it.skip("should reject bookings for past dates", async () => {
      const pastBookingData = {
        ...validBookingData,
        preferredDate: "2024-01-01", // Past date
      };

      const result =
        await interviewBookingsService.createBooking(pastBookingData);

      expect(result.success).toBe(false);
      expect(result.error).toContain("future date");
    });

    it.skip("should reject bookings on weekends", async () => {
      // November 23, 2025 is a Sunday
      const weekendBookingData = {
        ...validBookingData,
        preferredDate: "2025-11-23",
      };

      const result =
        await interviewBookingsService.createBooking(weekendBookingData);

      expect(result.success).toBe(false);
      expect(result.error).toContain("weekend");
    });

    it.skip("should reject bookings outside 14-day window", async () => {
      const futureDate = new Date();
      futureDate.setDate(futureDate.getDate() + 20); // 20 days from now

      const farFutureBookingData = {
        ...validBookingData,
        preferredDate: futureDate.toISOString().split("T")[0],
      };

      const result =
        await interviewBookingsService.createBooking(farFutureBookingData);

      expect(result.success).toBe(false);
      expect(result.error).toContain("within 14 days");
    });

    it.skip("should reject bookings outside business hours", async () => {
      const outsideHoursData = {
        ...validBookingData,
        preferredTime: "20:00", // 8 PM - outside 9 AM - 5 PM
      };

      const result =
        await interviewBookingsService.createBooking(outsideHoursData);

      expect(result.success).toBe(false);
      expect(result.error).toContain("business hours");
    });

    it.skip("should enforce 30-minute intervals", async () => {
      const invalidTimeData = {
        ...validBookingData,
        preferredTime: "10:15", // Not a 30-minute interval
      };

      const result =
        await interviewBookingsService.createBooking(invalidTimeData);

      expect(result.success).toBe(false);
      expect(result.error).toContain("30-minute intervals");
    });

    it.skip("should truncate notes to 500 characters", async () => {
      const longNotes = "a".repeat(600);
      const longNotesData = {
        ...validBookingData,
        applicantNotes: longNotes,
      };

      const mockBooking = {
        id: "booking-123",
        ...longNotesData,
        status: "pending",
      };

      (prisma.interview_bookings.findFirst as jest.Mock).mockResolvedValue(
        null,
      );
      (prisma.interview_bookings.create as jest.Mock).mockResolvedValue(
        mockBooking,
      );

      await interviewBookingsService.createBooking(longNotesData);

      expect(prisma.interview_bookings.create).toHaveBeenCalledWith({
        data: expect.objectContaining({
          applicantNotes: expect.stringMatching(/^a{500}$/),
        }),
      });
    });

    it.skip("should sanitize notes for XSS", async () => {
      const xssNotesData = {
        ...validBookingData,
        applicantNotes: '<script>alert("xss")</script>Normal text',
      };

      const mockBooking = {
        id: "booking-123",
        ...xssNotesData,
        status: "pending",
      };

      (prisma.interview_bookings.findFirst as jest.Mock).mockResolvedValue(
        null,
      );
      (prisma.interview_bookings.create as jest.Mock).mockResolvedValue(
        mockBooking,
      );

      await interviewBookingsService.createBooking(xssNotesData);

      const createCall = (prisma.interview_bookings.create as jest.Mock).mock
        .calls[0][0];
      expect(createCall.data.applicantNotes).not.toContain("<script>");
      expect(createCall.data.applicantNotes).toContain("Normal text");
    });

    it.skip("should sync booking to Supabase", async () => {
      const mockBooking = {
        id: "booking-123",
        ...validBookingData,
        status: "pending",
      };

      (prisma.interview_bookings.findFirst as jest.Mock).mockResolvedValue(
        null,
      );
      (prisma.interview_bookings.create as jest.Mock).mockResolvedValue(
        mockBooking,
      );

      const supabaseInsert = jest.fn().mockReturnThis();
      const supabaseSelect = jest
        .fn()
        .mockResolvedValue({ data: mockBooking, error: null });

      (supabase.from as jest.Mock).mockReturnValue({
        insert: supabaseInsert,
        select: jest.fn().mockReturnValue({
          single: supabaseSelect,
        }),
      });

      await interviewBookingsService.createBooking(validBookingData);

      expect(supabase.from).toHaveBeenCalledWith("interview_bookings");
      expect(supabaseInsert).toHaveBeenCalledWith(
        expect.objectContaining({
          id: mockBooking.id,
          application_id: validBookingData.applicationId,
        }),
      );
    });

    it.skip("should handle database errors gracefully", async () => {
      (prisma.interview_bookings.findFirst as jest.Mock).mockResolvedValue(
        null,
      );
      (prisma.interview_bookings.create as jest.Mock).mockRejectedValue(
        new Error("Database connection failed"),
      );

      const result =
        await interviewBookingsService.createBooking(validBookingData);

      expect(result.success).toBe(false);
      expect(result.error).toContain("Failed to create booking");
    });
  });

  describe("getBookingsByApplication", () => {
    it.skip("should retrieve bookings for an application", async () => {
      const mockBookings = [
        {
          id: "booking-1",
          applicationId: "app-123",
          status: "pending",
          preferredDate: new Date("2025-11-25"),
        },
        {
          id: "booking-2",
          applicationId: "app-123",
          status: "confirmed",
          preferredDate: new Date("2025-11-26"),
        },
      ];

      (prisma.interview_bookings.findMany as jest.Mock).mockResolvedValue(
        mockBookings,
      );

      const result =
        await interviewBookingsService.getBookingsByApplication("app-123");

      expect(result.success).toBe(true);
      expect(result.bookings).toHaveLength(2);
      expect(result.bookings[0].id).toBe("booking-1");
    });

    it.skip("should return empty array for application with no bookings", async () => {
      (prisma.interview_bookings.findMany as jest.Mock).mockResolvedValue([]);

      const result =
        await interviewBookingsService.getBookingsByApplication("app-999");

      expect(result.success).toBe(true);
      expect(result.bookings).toHaveLength(0);
    });
  });

  describe("updateBookingStatus", () => {
    it.skip("should update booking status", async () => {
      const mockUpdatedBooking = {
        id: "booking-123",
        status: "confirmed",
        confirmedAt: new Date(),
      };

      (prisma.interview_bookings.update as jest.Mock).mockResolvedValue(
        mockUpdatedBooking,
      );

      const result = await interviewBookingsService.updateBookingStatus(
        "booking-123",
        "confirmed",
      );

      expect(result.success).toBe(true);
      expect(prisma.interview_bookings.update).toHaveBeenCalledWith({
        where: { id: "booking-123" },
        data: expect.objectContaining({
          status: "confirmed",
          confirmedAt: expect.any(Date),
        }),
      });
    });

    it.skip("should validate status transitions", async () => {
      const result = await interviewBookingsService.updateBookingStatus(
        "booking-123",
        "invalid" as any,
      );

      expect(result.success).toBe(false);
      expect(result.error).toContain("Invalid status");
    });
  });

  describe("getAvailableTimeSlots", () => {
    it.skip("should return available 30-minute slots for business hours", async () => {
      const slots =
        await interviewBookingsService.getAvailableTimeSlots("2025-11-25");

      expect(slots.length).toBeGreaterThan(0);
      expect(slots[0]).toBe("09:00");
      expect(slots[slots.length - 1]).toBe("16:30");

      // Verify 30-minute intervals
      const minutes = slots.map((slot) => parseInt(slot.split(":")[1]));
      minutes.forEach((minute) => {
        expect([0, 30]).toContain(minute);
      });
    });

    it.skip("should exclude weekends", async () => {
      const saturdaySlots =
        await interviewBookingsService.getAvailableTimeSlots("2025-11-22");
      const sundaySlots =
        await interviewBookingsService.getAvailableTimeSlots("2025-11-23");

      expect(saturdaySlots.length).toBe(0);
      expect(sundaySlots.length).toBe(0);
    });
  });
});
