import { describe, it, expect } from 'vitest';
import { calculateColumnLayout, eventsOverlap } from '../../src/v2/features/event/EventLayoutEngine';
import { ICalendarEvent } from '../../src/v2/types/CalendarTypes';
import { IGridConfig } from '../../src/v2/core/IGridConfig';

// Helper to create test events
function createEvent(id: string, startHour: number, startMin: number, endHour: number, endMin: number): ICalendarEvent {
  const baseDate = new Date('2025-10-06');
  const start = new Date(baseDate);
  start.setHours(startHour, startMin, 0, 0);
  const end = new Date(baseDate);
  end.setHours(endHour, endMin, 0, 0);

  return {
    id,
    title: `Event ${id}`,
    start,
    end,
    type: 'work',
    allDay: false
  };
}

const gridConfig: IGridConfig = {
  hourHeight: 60,
  dayStartHour: 8,
  dayEndHour: 20,
  snapInterval: 15,
  gridStartThresholdMinutes: 30  // Match calendar-config.json
};

describe('EventLayoutEngine', () => {

  describe('eventsOverlap', () => {
    it('should return true for overlapping events', () => {
      const a = createEvent('a', 10, 0, 11, 0);
      const b = createEvent('b', 10, 30, 11, 30);
      expect(eventsOverlap(a, b)).toBe(true);
    });

    it('should return false for edge-adjacent events (end === start)', () => {
      const a = createEvent('a', 10, 0, 11, 0);
      const b = createEvent('b', 11, 0, 12, 0);
      expect(eventsOverlap(a, b)).toBe(false);
    });

    it('should return false for non-overlapping events', () => {
      const a = createEvent('a', 10, 0, 11, 0);
      const b = createEvent('b', 12, 0, 13, 0);
      expect(eventsOverlap(a, b)).toBe(false);
    });
  });

  describe('Scenario 1: No Overlap', () => {
    it('should assign stackLevel=0 to all sequential non-overlapping events', () => {
      const events = [
        createEvent('S1A', 10, 0, 11, 0),
        createEvent('S1B', 11, 0, 12, 0),
        createEvent('S1C', 12, 0, 13, 0)
      ];

      const layout = calculateColumnLayout(events, gridConfig);

      expect(layout.grids).toHaveLength(0);
      expect(layout.stacked).toHaveLength(3);

      const levels = layout.stacked.map(s => ({ id: s.event.id, level: s.stackLevel }));
      expect(levels).toContainEqual({ id: 'S1A', level: 0 });
      expect(levels).toContainEqual({ id: 'S1B', level: 0 });
      expect(levels).toContainEqual({ id: 'S1C', level: 0 });
    });
  });

  describe('Scenario 2: Column Sharing (Grid)', () => {
    it('should create grid with 2 columns for simultaneous events', () => {
      const events = [
        createEvent('S2A', 10, 0, 11, 0),
        createEvent('S2B', 10, 0, 11, 0)
      ];

      const layout = calculateColumnLayout(events, gridConfig);

      expect(layout.stacked).toHaveLength(0);
      expect(layout.grids).toHaveLength(1);

      const grid = layout.grids[0];
      expect(grid.columns).toHaveLength(2);
      expect(grid.stackLevel).toBe(0);
    });
  });

  describe('Scenario 3: Nested Stacking', () => {
    it('should calculate progressive stack levels for nested events', () => {
      // A: 09:00-15:00, B: 10:00-13:00, C: 11:00-12:00, D: 12:30-13:30
      const events = [
        createEvent('S3A', 9, 0, 15, 0),
        createEvent('S3B', 10, 0, 13, 0),
        createEvent('S3C', 11, 0, 12, 0),
        createEvent('S3D', 12, 30, 13, 30)
      ];

      const layout = calculateColumnLayout(events, gridConfig);

      expect(layout.grids).toHaveLength(0);
      expect(layout.stacked).toHaveLength(4);

      const getLevel = (id: string) => layout.stacked.find(s => s.event.id === id)?.stackLevel;
      expect(getLevel('S3A')).toBe(0);
      expect(getLevel('S3B')).toBe(1);
      expect(getLevel('S3C')).toBe(2);
      expect(getLevel('S3D')).toBe(2);
    });
  });

  describe('Scenario 4: Complex Stacking', () => {
    it('should handle long event with multiple nested events at different times', () => {
      // A: 14:00-20:00, B: 15:00-17:00, C: 15:30-16:30, D: 18:00-19:00
      const events = [
        createEvent('S4A', 14, 0, 20, 0),
        createEvent('S4B', 15, 0, 17, 0),
        createEvent('S4C', 15, 30, 16, 30),
        createEvent('S4D', 18, 0, 19, 0)
      ];

      const layout = calculateColumnLayout(events, gridConfig);

      expect(layout.grids).toHaveLength(0);
      expect(layout.stacked).toHaveLength(4);

      const getLevel = (id: string) => layout.stacked.find(s => s.event.id === id)?.stackLevel;
      expect(getLevel('S4A')).toBe(0);
      expect(getLevel('S4B')).toBe(1);
      expect(getLevel('S4C')).toBe(2);
      expect(getLevel('S4D')).toBe(1);
    });
  });

  describe('Scenario 5: Three Column Share', () => {
    it('should create grid with 3 columns for 3 simultaneous events', () => {
      const events = [
        createEvent('S5A', 10, 0, 11, 0),
        createEvent('S5B', 10, 0, 11, 0),
        createEvent('S5C', 10, 0, 11, 0)
      ];

      const layout = calculateColumnLayout(events, gridConfig);

      expect(layout.stacked).toHaveLength(0);
      expect(layout.grids).toHaveLength(1);

      const grid = layout.grids[0];
      expect(grid.columns).toHaveLength(3);
      expect(grid.stackLevel).toBe(0);
    });
  });

  describe('Scenario 6: Overlapping Pairs', () => {
    it('should handle two independent pairs of overlapping events', () => {
      // Pair 1: A (10:00-12:00), B (11:00-12:00)
      // Pair 2: C (13:00-15:00), D (14:00-15:00)
      const events = [
        createEvent('S6A', 10, 0, 12, 0),
        createEvent('S6B', 11, 0, 12, 0),
        createEvent('S6C', 13, 0, 15, 0),
        createEvent('S6D', 14, 0, 15, 0)
      ];

      const layout = calculateColumnLayout(events, gridConfig);

      expect(layout.grids).toHaveLength(0);
      expect(layout.stacked).toHaveLength(4);

      const getLevel = (id: string) => layout.stacked.find(s => s.event.id === id)?.stackLevel;
      expect(getLevel('S6A')).toBe(0);
      expect(getLevel('S6B')).toBe(1);
      expect(getLevel('S6C')).toBe(0);
      expect(getLevel('S6D')).toBe(1);
    });
  });

  describe('Scenario 7: Long Event Container', () => {
    it('should assign same level to non-overlapping events inside container', () => {
      // A: 09:00-15:00 (container), B: 10:00-11:00, C: 12:00-13:00
      const events = [
        createEvent('S7A', 9, 0, 15, 0),
        createEvent('S7B', 10, 0, 11, 0),
        createEvent('S7C', 12, 0, 13, 0)
      ];

      const layout = calculateColumnLayout(events, gridConfig);

      expect(layout.grids).toHaveLength(0);
      expect(layout.stacked).toHaveLength(3);

      const getLevel = (id: string) => layout.stacked.find(s => s.event.id === id)?.stackLevel;
      expect(getLevel('S7A')).toBe(0);
      expect(getLevel('S7B')).toBe(1);
      expect(getLevel('S7C')).toBe(1);
    });
  });

  describe('Scenario 8: Edge-Adjacent Events', () => {
    it('should not stack events that touch at boundary (end === start)', () => {
      const events = [
        createEvent('S8A', 10, 0, 11, 0),
        createEvent('S8B', 11, 0, 12, 0)
      ];

      const layout = calculateColumnLayout(events, gridConfig);

      expect(layout.grids).toHaveLength(0);
      expect(layout.stacked).toHaveLength(2);

      const getLevel = (id: string) => layout.stacked.find(s => s.event.id === id)?.stackLevel;
      expect(getLevel('S8A')).toBe(0);
      expect(getLevel('S8B')).toBe(0);
    });
  });

  describe('Scenario 9: End-to-Start Chain', () => {
    it('should create grid for events connected through conflict chain', () => {
      // A: 12:00-13:00, B: 12:30-13:30, C: 13:15-15:00
      // A overlaps B, B overlaps C, but they're within threshold -> GRID
      const events = [
        createEvent('S9A', 12, 0, 13, 0),
        createEvent('S9B', 12, 30, 13, 30),
        createEvent('S9C', 13, 15, 15, 0)
      ];

      const layout = calculateColumnLayout(events, gridConfig);

      // This should create a grid because events are within threshold
      expect(layout.grids).toHaveLength(1);
      expect(layout.grids[0].columns).toHaveLength(2);
      expect(layout.grids[0].events).toHaveLength(3);
    });
  });

  describe('Scenario 10: Four Column Grid', () => {
    it('should create grid with 4 columns for 4 simultaneous events', () => {
      const events = [
        createEvent('S10A', 14, 0, 15, 0),
        createEvent('S10B', 14, 0, 15, 0),
        createEvent('S10C', 14, 0, 15, 0),
        createEvent('S10D', 14, 0, 15, 0)
      ];

      const layout = calculateColumnLayout(events, gridConfig);

      expect(layout.stacked).toHaveLength(0);
      expect(layout.grids).toHaveLength(1);

      const grid = layout.grids[0];
      expect(grid.columns).toHaveLength(4);
      expect(grid.stackLevel).toBe(0);
    });
  });
});
