import type { Tables } from "@xtablo/shared-types";
import { beforeEach, describe, expect, it } from "vitest";
import {
  type EventTypeConfig,
  type Exception,
  generateTimeSlots,
  getDateStringCET,
  getDayOfWeek,
  type WeeklyAvailability,
} from "../../helpers/slots.js";

// Mock the current date for consistent testing

describe("generateTimeSlots", () => {
  let basicEventTypeConfig: EventTypeConfig;
  let basicAvailability: WeeklyAvailability[0];
  let testDate: Date;
  let existingEvents: Tables<"events">[];

  beforeEach(() => {
    // Mock system time for consistent testing

    basicEventTypeConfig = {
      name: "Test Meeting",
      description: "Test meeting description",
      duration: 30, // 30 minutes
      requiresApproval: false,
    };

    basicAvailability = {
      enabled: true,
      timeRanges: [{ start: "09:00", end: "17:00" }],
    };

    testDate = new Date("2024-01-16T00:00:00Z"); // Tuesday
    existingEvents = [];
  });

  describe("Basic functionality (no events already booked)", () => {
    it("should generate time slots for a basic availability window", () => {
      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        basicEventTypeConfig,
        [],
        existingEvents
      );

      expect(slots.length).toBe(16); // 8 hours * 2 slots per hour (30min each)
      expect(slots[0]).toEqual({
        date: "2024-01-16",
        time: "09:00",
        available: true,
      });
      expect(slots[1]).toEqual({
        date: "2024-01-16",
        time: "09:30",
        available: true,
      });
      expect(slots[slots.length - 1]).toEqual({
        date: "2024-01-16",
        time: "16:30",
        available: true,
      });
    });

    it("should handle multiple time ranges in a day", () => {
      const availability = {
        enabled: true,
        timeRanges: [
          { start: "09:00", end: "12:00" },
          { start: "14:00", end: "17:00" },
        ],
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        availability,
        basicEventTypeConfig,
        [],
        existingEvents
      );

      expect(slots.length).toBe(12); // 6 slots morning + 6 slots afternoon

      // Check morning slots
      expect(slots[0].time).toBe("09:00");
      expect(slots[5].time).toBe("11:30");

      // Check afternoon slots
      expect(slots[6].time).toBe("14:00");
      expect(slots[11].time).toBe("16:30");
    });

    it("should return empty array when day is not enabled", () => {
      const disabledAvailability = {
        enabled: false,
        timeRanges: [{ start: "09:00", end: "17:00" }],
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        disabledAvailability,
        basicEventTypeConfig,
        [],
        existingEvents
      );

      expect(slots.length).toBe(0);
    });

    it("should handle 15-minute duration slots", () => {
      const shortEventConfig = {
        ...basicEventTypeConfig,
        duration: 15, // 15 minutes
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        shortEventConfig,
        [],
        existingEvents
      );

      expect(slots.length).toBe(32); // 8 hours * 4 slots per hour
      expect(slots[0].time).toBe("09:00");
      expect(slots[1].time).toBe("09:15");
      expect(slots[2].time).toBe("09:30");
      expect(slots[3].time).toBe("09:45");
      expect(slots[4].time).toBe("10:00");
      expect(slots[31].time).toBe("16:45");
    });

    it("should handle different event durations", () => {
      const longEventConfig = {
        ...basicEventTypeConfig,
        duration: 60, // 1 hour
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        longEventConfig,
        [],
        existingEvents
      );

      expect(slots.length).toBe(8); // 8 hours, 1 slot per hour
      expect(slots[0].time).toBe("09:00");
      expect(slots[1].time).toBe("10:00");
      expect(slots[7].time).toBe("16:00");
    });

    it("should generate slots for the next day correctly", () => {
      const currentTime = new Date("2025-09-30T19:39:30.000Z"); // Current time
      const nextDay = new Date("2025-10-01T19:39:30.000Z"); // Next day

      const slots = generateTimeSlots(
        currentTime, // currentTime (2025-09-30)
        nextDay, // date (2025-10-01)
        basicAvailability,
        basicEventTypeConfig,
        [],
        existingEvents
      );

      expect(slots.length).toBe(16); // 8 hours * 2 slots per hour
      expect(slots[0].time).toBe("09:00");
      expect(slots[0].date).toBe("2025-10-01");
      expect(slots[15].time).toBe("16:30");
      expect(slots[15].date).toBe("2025-10-01");

      // All slots should be available since it's a future day
      slots.forEach((slot) => {
        expect(slot.available).toBe(true);
      });
    });
  });

  describe("Exception handling", () => {
    it("should return empty array for day exception", () => {
      const dayException: Exception = {
        date: "2024-01-16",
        type: "day",
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        basicEventTypeConfig,
        [dayException],
        existingEvents
      );

      expect(slots.length).toBe(0);
    });

    it("should handle exception with date with minutes and seconds", () => {
      const dayExceptionWithTime: Exception = {
        date: "2024-01-16T10:30:45.123Z", // Date with time components
        type: "day",
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        basicEventTypeConfig,
        [dayExceptionWithTime],
        existingEvents
      );

      expect(slots.length).toBe(0);
    });

    it("should handle hours exception with date containing time components", () => {
      const hoursExceptionWithTime: Exception = {
        date: "2024-01-16T10:30:45.123Z", // Date with time components
        type: "hours",
        hours: [{ start: "10:00", end: "12:00" }],
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        basicEventTypeConfig,
        [hoursExceptionWithTime],
        existingEvents
      );

      expect(slots.length).toBe(4); // 2 hours * 2 slots per hour
      expect(slots[0].time).toBe("10:00");
      expect(slots[3].time).toBe("11:30");
    });

    it("should use exception hours instead of regular availability", () => {
      const hoursException: Exception = {
        date: "2024-01-16",
        type: "hours",
        hours: [{ start: "10:00", end: "12:00" }],
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        basicEventTypeConfig,
        [hoursException],
        existingEvents
      );

      expect(slots.length).toBe(4); // 2 hours * 2 slots per hour
      expect(slots[0].time).toBe("10:00");
      expect(slots[3].time).toBe("11:30");
    });

    it("should handle multiple time ranges in hours exception", () => {
      const multipleHoursException: Exception = {
        date: "2024-01-16",
        type: "hours",
        hours: [
          { start: "09:00", end: "10:00" },
          { start: "14:00", end: "16:00" },
        ],
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        basicEventTypeConfig,
        [multipleHoursException],
        existingEvents
      );

      expect(slots.length).toBe(6); // 1 hour + 2 hours = 3 hours * 2 slots per hour

      // First range: 09:00-10:00
      expect(slots[0].time).toBe("09:00");
      expect(slots[1].time).toBe("09:30");

      // Second range: 14:00-16:00
      expect(slots[2].time).toBe("14:00");
      expect(slots[3].time).toBe("14:30");
      expect(slots[4].time).toBe("15:00");
      expect(slots[5].time).toBe("15:30");
    });

    it("should handle overlapping time ranges in hours exception", () => {
      const overlappingHoursException: Exception = {
        date: "2024-01-16",
        type: "hours",
        hours: [
          { start: "09:00", end: "11:00" },
          { start: "10:00", end: "12:00" },
        ],
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        basicEventTypeConfig,
        [overlappingHoursException],
        existingEvents
      );

      // Should generate slots for the combined range 09:00-12:00
      expect(slots.length).toBe(6); // 3 hours * 2 slots per hour
      expect(slots[0].time).toBe("09:00");
      expect(slots[1].time).toBe("09:30");
      expect(slots[2].time).toBe("10:00");
      expect(slots[3].time).toBe("10:30");
      expect(slots[4].time).toBe("11:00");
      expect(slots[5].time).toBe("11:30");
    });

    it("should handle multiple overlapping time ranges", () => {
      const multipleOverlappingException: Exception = {
        date: "2024-01-16",
        type: "hours",
        hours: [
          { start: "09:00", end: "10:30" },
          { start: "10:00", end: "11:30" },
          { start: "11:00", end: "12:00" },
          { start: "14:00", end: "15:00" }, // Separate range
        ],
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        basicEventTypeConfig,
        [multipleOverlappingException],
        existingEvents
      );

      // Should generate slots for merged range 09:00-12:00 and separate range 14:00-15:00
      expect(slots.length).toBe(8); // 3 hours + 1 hour = 4 hours * 2 slots per hour

      // First merged range: 09:00-12:00
      expect(slots[0].time).toBe("09:00");
      expect(slots[1].time).toBe("09:30");
      expect(slots[2].time).toBe("10:00");
      expect(slots[3].time).toBe("10:30");
      expect(slots[4].time).toBe("11:00");
      expect(slots[5].time).toBe("11:30");

      // Separate range: 14:00-15:00
      expect(slots[6].time).toBe("14:00");
      expect(slots[7].time).toBe("14:30");
    });

    it("should handle adjacent time ranges (touching but not overlapping)", () => {
      const adjacentRangesException: Exception = {
        date: "2024-01-16",
        type: "hours",
        hours: [
          { start: "09:00", end: "10:00" },
          { start: "10:00", end: "11:00" }, // Touches at 10:00
        ],
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        basicEventTypeConfig,
        [adjacentRangesException],
        existingEvents
      );

      // Should merge into one continuous range 09:00-11:00
      expect(slots.length).toBe(4); // 2 hours * 2 slots per hour
      expect(slots[0].time).toBe("09:00");
      expect(slots[1].time).toBe("09:30");
      expect(slots[2].time).toBe("10:00");
      expect(slots[3].time).toBe("10:30");
    });

    it("should ignore exceptions for different dates", () => {
      const differentDateException: Exception = {
        date: "2024-01-17",
        type: "day",
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        basicEventTypeConfig,
        [differentDateException],
        existingEvents
      );

      expect(slots.length).toBe(16); // Normal availability should apply
    });
  });

  describe("Existing event conflicts", () => {
    it("should mark slots as unavailable when conflicting with existing events", () => {
      const conflictingEvent: Tables<"events"> = {
        id: "1",
        start_date: "2024-01-16",
        start_time: "10:00",
        end_time: "11:00",
        created_by: "user1",
        deleted_at: null,
        // Add other required fields with default values
        title: "Existing Meeting",
        description: null,
        created_at: "2024-01-15T00:00:00Z",
        tablo_id: "",
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        basicEventTypeConfig,
        [],
        [conflictingEvent]
      );

      // Find slots that should be affected
      const slot09_30 = slots.find((s) => s.time === "09:30");
      const slot10_00 = slots.find((s) => s.time === "10:00");
      const slot10_30 = slots.find((s) => s.time === "10:30");
      const slot11_00 = slots.find((s) => s.time === "11:00");

      expect(slot10_00?.available).toBe(false); // Starts during event
      expect(slot10_30?.available).toBe(false); // Starts during event
      expect(slot09_30?.available).toBe(true); // Ends right when the event starts
      expect(slot11_00?.available).toBe(true); // Starts after event ends
    });

    it("should ignore deleted events", () => {
      const deletedEvent: Tables<"events"> = {
        id: "1",
        start_date: "2024-01-16",
        start_time: "10:00",
        end_time: "11:00",
        created_by: "user1",
        deleted_at: "2024-01-15T12:00:00Z",
        title: "Deleted Meeting",
        description: null,
        created_at: "2024-01-15T00:00:00Z",
        tablo_id: "",
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        basicEventTypeConfig,
        [],
        [deletedEvent]
      );

      // All slots should be available since the event is deleted
      const slot10_00 = slots.find((s) => s.time === "10:00");
      expect(slot10_00?.available).toBe(true);
    });

    it("should handle events without end_time using event duration", () => {
      const eventWithoutEndTime: Tables<"events"> = {
        id: "1",
        start_date: "2024-01-16",
        start_time: "10:00",
        end_time: null,
        created_by: "user1",
        deleted_at: null,
        title: "Meeting without end time",
        description: null,
        created_at: "2024-01-15T00:00:00Z",
        tablo_id: "",
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        basicEventTypeConfig,
        [],
        [eventWithoutEndTime]
      );

      const slot10_00 = slots.find((s) => s.time === "10:00");
      const slot10_30 = slots.find((s) => s.time === "10:30");

      expect(slot10_00?.available).toBe(false); // Event uses duration (30 min)
      expect(slot10_30?.available).toBe(true); // Should be available after 30 min
    });
  });

  describe("Minimum advance booking", () => {
    it("should mark past slots as unavailable", () => {
      // Test date is same as current date
      const todayDate = new Date("2024-01-15T00:00:00Z"); // Date for slot generation

      // Mock current time in CET (11:00 CET = 10:00 UTC in winter)
      const currentTimeCET = new Date("2024-01-15T11:00:00"); // 11:00 CET

      const slots = generateTimeSlots(
        currentTimeCET, // currentTime
        todayDate, // date
        basicAvailability,
        basicEventTypeConfig,
        [],
        []
      );

      // Slots before current time (11:00 CET) should be unavailable
      const slot09_00 = slots.find((s) => s.time === "09:00");
      const slot09_30 = slots.find((s) => s.time === "09:30");
      const slot10_00 = slots.find((s) => s.time === "10:00");
      const slot10_30 = slots.find((s) => s.time === "10:30");
      const slot11_00 = slots.find((s) => s.time === "11:00");
      const slot11_30 = slots.find((s) => s.time === "11:30");

      expect(slot09_00?.available).toBe(false);
      expect(slot09_30?.available).toBe(false);
      expect(slot10_00?.available).toBe(false);
      expect(slot10_30?.available).toBe(false);
      expect(slot11_00?.available).toBe(true);
      expect(slot11_30?.available).toBe(true);
    });

    it("should respect minimum advance booking in hours", () => {
      const configWithAdvanceBooking = {
        ...basicEventTypeConfig,
        minAdvanceBooking: {
          value: 2,
          unit: "hours" as const,
        },
      };

      // Mock current time in CET (10:00 CET)
      const currentTimeCET = new Date("2024-01-15T10:00:00");
      const todayDate = new Date("2024-01-15T00:00:00Z");

      const slots = generateTimeSlots(
        currentTimeCET, // currentTime
        todayDate, // date
        basicAvailability,
        configWithAdvanceBooking,
        [],
        existingEvents
      );

      // Slots before 12:00 (10:00 + 2 hours) should be unavailable
      const slot11_30 = slots.find((s) => s.time === "11:30");
      const slot12_00 = slots.find((s) => s.time === "12:00");

      expect(slot11_30?.available).toBe(false);
      expect(slot12_00?.available).toBe(true);
    });

    it("should respect minimum advance booking in days", () => {
      const configWithAdvanceBooking = {
        ...basicEventTypeConfig,
        minAdvanceBooking: {
          value: 1,
          unit: "days" as const,
        },
      };

      const todayDate = new Date("2024-01-15T00:00:00Z");

      // Mock current time in CET (10:00 CET)
      const currentTimeCET = new Date("2024-01-15T10:00:00");

      const slots = generateTimeSlots(
        currentTimeCET, // currentTime
        todayDate, // date
        basicAvailability,
        configWithAdvanceBooking,
        [],
        existingEvents
      );

      // All slots today should be unavailable due to 1-day advance booking
      slots.forEach((slot) => {
        expect(slot.available).toBe(false);
      });
    });

    it("should ignore minimum advance booking if the date is in the future", () => {
      const configWithAdvanceBooking = {
        ...basicEventTypeConfig,
        minAdvanceBooking: {
          value: 2,
          unit: "hours" as const,
        },
      };

      // Mock current time in CET (10:00 CET on Jan 15)
      const currentTimeCET = new Date("2024-01-15T10:00:00");
      // Test date is Jan 16 (future date)
      const futureDate = new Date("2024-01-16T00:00:00Z");

      const slots = generateTimeSlots(
        currentTimeCET, // currentTime
        futureDate, // date
        basicAvailability,
        configWithAdvanceBooking,
        [],
        existingEvents
      );

      // All slots should be available since we're checking a future date
      const slot09_00 = slots.find((s) => s.time === "09:00");
      const slot09_30 = slots.find((s) => s.time === "09:30");

      expect(slot09_00?.available).toBe(true);
      expect(slot09_30?.available).toBe(true);
    });
  });

  describe("Buffer time", () => {
    it("should generate all possible slots regardless of buffer time", () => {
      const configWithBuffer = {
        ...basicEventTypeConfig,
        duration: 30,
        bufferTime: 15,
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        configWithBuffer,
        [],
        existingEvents
      );

      // Should generate slots every 30 minutes (duration), not considering buffer time for slot generation
      expect(slots.length).toBe(16); // Same as without buffer time
      expect(slots[0].time).toBe("09:00");
      expect(slots[1].time).toBe("09:30");
      expect(slots[2].time).toBe("10:00");
      expect(slots[3].time).toBe("10:30");
    });

    it("should apply buffer time around existing events to disable slots", () => {
      const configWithBuffer = {
        ...basicEventTypeConfig,
        duration: 30,
        bufferTime: 15, // 15 minutes buffer before and after events
      };

      const existingEvent: Tables<"events"> = {
        id: "1",
        start_date: "2024-01-16",
        start_time: "10:00",
        end_time: "10:30",
        created_by: "user1",
        deleted_at: null,
        title: "Existing Meeting",
        description: null,
        created_at: "2024-01-15T00:00:00Z",
        tablo_id: "",
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        configWithBuffer,
        [],
        [existingEvent]
      );

      // Find slots around the existing event (10:00-10:30)
      // Buffer time should disable slots that ends after 09:45 or starts before 10:45
      const slot09_00 = slots.find((s) => s.time === "09:00");
      const slot09_30 = slots.find((s) => s.time === "09:30");
      const slot10_00 = slots.find((s) => s.time === "10:00");
      const slot10_30 = slots.find((s) => s.time === "10:30");
      const slot11_00 = slots.find((s) => s.time === "11:00");

      expect(slot09_00?.available).toBe(true); // Ends before buffer starts
      expect(slot09_30?.available).toBe(false); // Ends at 10:00, overlaps with buffer (09:45-10:45)
      expect(slot10_00?.available).toBe(false); // Overlaps with buffered event
      expect(slot10_30?.available).toBe(false); // Starts at 10:30, overlaps with buffer until 10:45
      expect(slot11_00?.available).toBe(true); // Starts after buffer ends
    });

    it("should handle buffer time with events without end_time", () => {
      const configWithBuffer = {
        ...basicEventTypeConfig,
        duration: 30,
        bufferTime: 15,
      };

      const eventWithoutEndTime: Tables<"events"> = {
        id: "1",
        start_date: "2024-01-16",
        start_time: "10:00",
        end_time: null, // Will use event duration (30 min)
        created_by: "user1",
        deleted_at: null,
        title: "Meeting without end time",
        description: null,
        created_at: "2024-01-15T00:00:00Z",
        tablo_id: "",
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        configWithBuffer,
        [],
        [eventWithoutEndTime]
      );

      // Event is 10:00-10:30, with 15min buffer: 09:45-10:45
      const slot09_00 = slots.find((s) => s.time === "09:00");
      const slot09_30 = slots.find((s) => s.time === "09:30");
      const slot10_00 = slots.find((s) => s.time === "10:00");
      const slot10_30 = slots.find((s) => s.time === "10:30");
      const slot11_00 = slots.find((s) => s.time === "11:00");

      expect(slot09_00?.available).toBe(true);
      expect(slot09_30?.available).toBe(false);
      expect(slot10_00?.available).toBe(false); // Within buffered time
      expect(slot10_30?.available).toBe(false); // Within buffered time
      expect(slot11_00?.available).toBe(true); // After buffered time
    });

    it("should handle large buffer time that affects multiple slots", () => {
      const configWithLargeBuffer = {
        ...basicEventTypeConfig,
        duration: 30,
        bufferTime: 60, // 1 hour buffer
      };

      const existingEvent: Tables<"events"> = {
        id: "1",
        start_date: "2024-01-16",
        start_time: "12:00",
        end_time: "12:30",
        created_by: "user1",
        deleted_at: null,
        title: "Existing Meeting",
        description: null,
        created_at: "2024-01-15T00:00:00Z",
        tablo_id: "",
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        configWithLargeBuffer,
        [],
        [existingEvent]
      );

      // Event is 12:00-12:30, with 60min buffer: 11:00-13:30
      const slot10_30 = slots.find((s) => s.time === "10:30");
      const slot11_00 = slots.find((s) => s.time === "11:00");
      const slot11_30 = slots.find((s) => s.time === "11:30");
      const slot12_00 = slots.find((s) => s.time === "12:00");
      const slot12_30 = slots.find((s) => s.time === "12:30");
      const slot13_00 = slots.find((s) => s.time === "13:00");
      const slot13_30 = slots.find((s) => s.time === "13:30");
      const slot14_00 = slots.find((s) => s.time === "14:00");

      expect(slot10_30?.available).toBe(true); // Before buffer
      expect(slot11_00?.available).toBe(false); // Within buffer
      expect(slot11_30?.available).toBe(false); // Within buffer
      expect(slot12_00?.available).toBe(false); // Within buffer
      expect(slot12_30?.available).toBe(false); // Within buffer
      expect(slot13_00?.available).toBe(false); // Within buffer (ends at 13:30)
      expect(slot13_30?.available).toBe(true); // After buffer
      expect(slot14_00?.available).toBe(true); // After buffer
    });

    it("should handle multiple events with overlapping buffer times", () => {
      const configWithBuffer = {
        ...basicEventTypeConfig,
        duration: 30,
        bufferTime: 30,
      };

      const existingEvents: Tables<"events">[] = [
        {
          id: "1",
          start_date: "2024-01-16",
          start_time: "10:00",
          end_time: "10:30",
          created_by: "user1",
          deleted_at: null,
          title: "First Meeting",
          description: null,
          created_at: "2024-01-15T00:00:00Z",
          tablo_id: "",
        },
        {
          id: "2",
          start_date: "2024-01-16",
          start_time: "11:30",
          end_time: "12:00",
          created_by: "user1",
          deleted_at: null,
          title: "Second Meeting",
          description: null,
          created_at: "2024-01-15T00:00:00Z",
          tablo_id: "",
        },
      ];

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        configWithBuffer,
        [],
        existingEvents
      );

      // First event: 10:00-10:30, buffer: 09:30-11:00
      // Second event: 11:30-12:00, buffer: 11:00-12:30
      // Combined buffer coverage: 09:30-12:30

      const slot09_00 = slots.find((s) => s.time === "09:00");
      const slot09_30 = slots.find((s) => s.time === "09:30");
      const slot10_00 = slots.find((s) => s.time === "10:00");
      const slot11_00 = slots.find((s) => s.time === "11:00");
      const slot12_00 = slots.find((s) => s.time === "12:00");
      const slot12_30 = slots.find((s) => s.time === "12:30");
      const slot13_00 = slots.find((s) => s.time === "13:00");

      expect(slot09_00?.available).toBe(true); // Before any buffer
      expect(slot09_30?.available).toBe(false); // Within first event's buffer
      expect(slot10_00?.available).toBe(false); // Within first event's buffer
      expect(slot11_00?.available).toBe(false); // Within second event's buffer
      expect(slot12_00?.available).toBe(false); // Within second event's buffer
      expect(slot12_30?.available).toBe(true); // After all buffers
      expect(slot13_00?.available).toBe(true); // After all buffers
    });

    it("should not affect slot generation in short availability windows", () => {
      const configWithBuffer = {
        ...basicEventTypeConfig,
        duration: 30,
        bufferTime: 60, // Large buffer
      };

      const shortAvailability = {
        enabled: true,
        timeRanges: [{ start: "09:00", end: "10:30" }], // Only 1.5 hours
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        shortAvailability,
        configWithBuffer,
        [],
        existingEvents
      );

      // Should generate all possible slots within the availability window
      expect(slots.length).toBe(3); // 09:00, 09:30, 10:00
      expect(slots[0].time).toBe("09:00");
      expect(slots[1].time).toBe("09:30");
      expect(slots[2].time).toBe("10:00");
      // All should be available since there are no existing events
      expect(slots.every((slot) => slot.available)).toBe(true);
    });

    it("should handle buffer time that extends before start of day", () => {
      const configWithBuffer = {
        ...basicEventTypeConfig,
        duration: 30,
        bufferTime: 60, // 1 hour buffer
      };

      const earlyMorningAvailability = {
        enabled: true,
        timeRanges: [{ start: "08:00", end: "12:00" }],
      };

      const earlyEvent: Tables<"events"> = {
        id: "1",
        start_date: "2024-01-16",
        start_time: "08:30",
        end_time: "09:00",
        created_by: "user1",
        deleted_at: null,
        title: "Early Meeting",
        description: null,
        created_at: "2024-01-15T00:00:00Z",
        tablo_id: "",
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        earlyMorningAvailability,
        configWithBuffer,
        [],
        [earlyEvent]
      );

      // Event is 08:30-09:00, with 60min buffer: 07:30-10:00
      // Since 07:30 is before availability starts, buffer should start from 08:00
      const slot08_00 = slots.find((s) => s.time === "08:00");
      const slot08_30 = slots.find((s) => s.time === "08:30");
      const slot09_00 = slots.find((s) => s.time === "09:00");
      const slot09_30 = slots.find((s) => s.time === "09:30");
      const slot10_00 = slots.find((s) => s.time === "10:00");

      expect(slot08_00?.available).toBe(false); // Within buffer
      expect(slot08_30?.available).toBe(false); // Within buffer
      expect(slot09_00?.available).toBe(false); // Within buffer
      expect(slot09_30?.available).toBe(false); // Within buffer (ends at 10:00)
      expect(slot10_00?.available).toBe(true); // After buffer
    });
  });

  describe("Maximum bookings per day", () => {
    it("should mark all slots as unavailable when max bookings reached", () => {
      const configWithMaxBookings = {
        ...basicEventTypeConfig,
        maxBookingsPerDay: 2,
      };

      const existingBookings: Tables<"events">[] = [
        {
          id: "1",
          start_date: "2024-01-16",
          start_time: "10:00",
          end_time: "10:30",
          created_by: "user1",
          deleted_at: null,
          title: "Booking 1",
          description: null,
          created_at: "2024-01-15T00:00:00Z",
          tablo_id: "",
        },
        {
          id: "2",
          start_date: "2024-01-16",
          start_time: "14:00",
          end_time: "14:30",
          created_by: "user1",
          deleted_at: null,
          title: "Booking 2",
          description: null,
          created_at: "2024-01-15T00:00:00Z",
          tablo_id: "",
        },
      ];

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        configWithMaxBookings,
        [],
        existingBookings
      );

      // All slots should be unavailable
      slots.forEach((slot) => {
        expect(slot.available).toBe(false);
      });
    });

    it("should not count deleted bookings towards max limit", () => {
      const configWithMaxBookings = {
        ...basicEventTypeConfig,
        maxBookingsPerDay: 2,
      };

      const existingBookings: Tables<"events">[] = [
        {
          id: "1",
          start_date: "2024-01-16",
          start_time: "10:00",
          end_time: "10:30",
          created_by: "user1",
          deleted_at: null,
          title: "Active Booking",
          description: null,
          created_at: "2024-01-15T00:00:00Z",
          tablo_id: "",
        },
        {
          id: "2",
          start_date: "2024-01-16",
          start_time: "14:00",
          end_time: "14:30",
          created_by: "user1",
          deleted_at: "2024-01-15T12:00:00Z", // Deleted
          title: "Deleted Booking",
          description: null,
          created_at: "2024-01-15T00:00:00Z",
          tablo_id: "",
        },
      ];

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        configWithMaxBookings,
        [],
        existingBookings
      );

      // Should have available slots since only 1 active booking exists
      const availableSlots = slots.filter((slot) => slot.available);
      expect(availableSlots.length).toBeGreaterThan(0);
    });
  });

  describe("Edge cases", () => {
    it("should handle empty time ranges", () => {
      const emptyAvailability = {
        enabled: true,
        timeRanges: [],
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        emptyAvailability,
        basicEventTypeConfig,
        [],
        existingEvents
      );

      expect(slots.length).toBe(0);
    });

    it("should handle time ranges where duration does not fit", () => {
      const shortRange = {
        enabled: true,
        timeRanges: [{ start: "09:00", end: "09:15" }], // Only 15 minutes
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        shortRange,
        basicEventTypeConfig, // 30 minute duration
        [],
        existingEvents
      );

      expect(slots.length).toBe(0);
    });

    it("should handle midnight crossing time ranges", () => {
      const lateNightAvailability = {
        enabled: true,
        timeRanges: [{ start: "23:00", end: "23:59" }],
      };

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        lateNightAvailability,
        basicEventTypeConfig,
        [],
        existingEvents
      );

      expect(slots.length).toBe(1);
      expect(slots[0].time).toBe("23:00");
    });

    it("should handle complex overlapping event scenarios", () => {
      const overlappingEvents: Tables<"events">[] = [
        {
          id: "1",
          start_date: "2024-01-16",
          start_time: "10:00",
          end_time: "11:30",
          created_by: "user1",
          deleted_at: null,
          title: "Long Meeting",
          description: null,
          created_at: "2024-01-15T00:00:00Z",
          tablo_id: "",
        },
        {
          id: "2",
          start_date: "2024-01-16",
          start_time: "11:00",
          end_time: "12:00",
          created_by: "user1",
          deleted_at: null,
          title: "Overlapping Meeting",
          description: null,
          created_at: "2024-01-15T00:00:00Z",
          tablo_id: "",
        },
      ];

      const slots = generateTimeSlots(
        testDate, // currentTime
        testDate, // date
        basicAvailability,
        basicEventTypeConfig,
        [],
        overlappingEvents
      );

      // Check that all conflicting slots are marked unavailable
      const conflictingTimes = ["10:00", "10:30", "11:00", "11:30"];
      conflictingTimes.forEach((time) => {
        const slot = slots.find((s) => s.time === time);
        expect(slot?.available).toBe(false);
      });

      // Check that non-conflicting slots are available
      const slot12_00 = slots.find((s) => s.time === "12:00");
      expect(slot12_00?.available).toBe(true);
    });
  });

  describe("Helper functions", () => {
    it("should correctly convert day of week", () => {
      expect(getDayOfWeek(new Date("2024-01-15"))).toBe(0); // Monday
      expect(getDayOfWeek(new Date("2024-01-16"))).toBe(1); // Tuesday
      expect(getDayOfWeek(new Date("2024-01-21"))).toBe(6); // Sunday
    });

    it("should format date strings correctly", () => {
      expect(getDateStringCET(new Date("2024-01-15T10:30:00Z"))).toBe("2024-01-15");
      expect(getDateStringCET(new Date("2024-12-31T23:59:59Z"))).toBe("2025-01-01");
    });
  });
});
