import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
import { EventContentArg, EventClickArg, CalendarOptions } from '@fullcalendar/core';
import FullCalendar from '@fullcalendar/react';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import interactionPlugin from '@fullcalendar/interaction';
import multiMonthPlugin from '@fullcalendar/multimonth';  // Import the multiMonth plugin for year view
import listPlugin from '@fullcalendar/list';
import { useAuth } from '../../contexts/AuthContext';
import { useNeoUser } from '../../contexts/NeoUserContext';
import { FaEllipsisV } from 'react-icons/fa';
import { logger } from '../../debugConfig';
import { TimetableNeoDBService } from '../../services/graph/timetableNeoDBService';

interface Event {
  id: string;
  title: string;
  start: string;
  end: string;
  groupId?: string;
  extendedProps?: {
    subjectClass: string;
    color: string;
    periodCode: string;
    tldraw_snapshot?: string;
  };
}

function lightenColor(color: string, amount: number): string {
  // Remove the '#' if it exists
  color = color.replace(/^#/, '');

  // Parse the color
  let r = parseInt(color.slice(0, 2), 16);
  let g = parseInt(color.slice(2, 4), 16);
  let b = parseInt(color.slice(4, 6), 16);

  // Convert to HSL
  const [h, s, l] = rgbToHsl(r, g, b);

  // Adjust the lightness based on the current lightness
  const newL = l < 0.5 ? l + (1 - l) * amount : l + (1 - l) * amount * 0.5;

  // Convert back to RGB
  [r, g, b] = hslToRgb(h, s, newL);

  // Convert to hex and return
  return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
}

function rgbToHsl(r: number, g: number, b: number): [number, number, number] {
  r /= 255;
  g /= 255;
  b /= 255;
  const max = Math.max(r, g, b);
  const min = Math.min(r, g, b);
  let h = 0, s, l = (max + min) / 2;

  if (max === min) {
    h = s = 0; // achromatic
  } else {
    const d = max - min;
    s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
    switch (max) {
      case r: h = (g - b) / d + (g < b ? 6 : 0); break;
      case g: h = (b - r) / d + 2; break;
      case b: h = (r - g) / d + 4; break;
    }
    h /= 6;
  }

  return [h, s, l];
}

function hslToRgb(h: number, s: number, l: number): [number, number, number] {
  let r, g, b;

  if (s === 0) {
    r = g = b = l; // achromatic
  } else {
    const hue2rgb = (p: number, q: number, t: number) => {
      if (t < 0) {
        t += 1;
      }
      if (t > 1) {
        t -= 1;
      }
      if (t < 1/6) {
        return p + (q - p) * 6 * t;
      }
      if (t < 1/2) {
        return q;
      }
      if (t < 2/3) {
        return p + (q - p) * (2/3 - t) * 6;
      }
      return p;
    };

    const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
    const p = 2 * l - q;
    r = hue2rgb(p, q, h + 1/3);
    g = hue2rgb(p, q, h);
    b = hue2rgb(p, q, h - 1/3);
  }

  return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}

const CalendarPage: React.FC = () => {
  const [events, setEvents] = useState<Event[]>([]);
  const [selectedClasses, setSelectedClasses] = useState<string[]>([]);
  const { user } = useAuth();
  const calendarRef = useRef<FullCalendar>(null);
  const [openDropdownId, setOpenDropdownId] = useState<string | null>(null);
  const [hiddenSubjectClassDivs, setHiddenSubjectClassDivs] = useState<string[]>([]);
  const [hiddenPeriodCodeDivs, setHiddenPeriodCodeDivs] = useState<string[]>([]);
  const [hiddenTimeDivs, setHiddenTimeDivs] = useState<string[]>([]);
  const [eventRange, setEventRange] = useState<{ start: Date | null; end: Date | null }>({ start: null, end: null });
  const { workerNode, isLoading, error, workerDbName } = useNeoUser();

  const getEventRange = useCallback((events: Event[]) => {
    if (events.length === 0) {
      return { start: null, end: null };
    }

    let start = new Date(events[0].start);
    let end = new Date(events[0].end);

    events.forEach(event => {
      const eventStart = new Date(event.start);
      const eventEnd = new Date(event.end);
      if (eventStart < start) {
        start = eventStart;
      }
      if (eventEnd > end) {
        end = eventEnd;
      }
    });

    // Adjust start to the beginning of its month and end to the end of its month
    start.setDate(1);
    end.setMonth(end.getMonth() + 1, 0);

    return { start, end };
  }, []);

  const fetchEvents = useCallback(async () => {
    if (!user || isLoading || error || !workerNode?.nodeData) {
      if (error) {
        logger.error('calendar', 'NeoUser context error', { error });
      }
      return;
    }

    try {
      logger.debug('calendar', 'Fetching events', {
        unique_id: workerNode.nodeData.unique_id,
        school_db_name: workerDbName
      });

      const events = await TimetableNeoDBService.fetchTeacherTimetableEvents(
        workerNode.nodeData.unique_id,
        workerDbName || ''
      );

      const transformedEvents = events.map(event => ({
        ...event,
        extendedProps: {
          ...event.extendedProps,
          tldraw_snapshot: workerNode?.nodeData?.tldraw_snapshot
        }
      }));

      setEvents(transformedEvents);
      
      const classes: string[] = [];
      transformedEvents.forEach((event: Event) => {
        if (event.extendedProps?.subjectClass && !classes.includes(event.extendedProps.subjectClass)) {
          classes.push(event.extendedProps.subjectClass);
        }
      });
      
      setSelectedClasses(classes);

      const range = getEventRange(transformedEvents);
      setEventRange(range);
      
    } catch (error) {
      logger.error('calendar', 'Error fetching events', { error });
    }
  }, [user, workerNode, workerDbName, isLoading, error, getEventRange]);

  useEffect(() => {
    fetchEvents();
  }, [fetchEvents]);

  const handleEventClick = useCallback((clickInfo: EventClickArg) => {
    const tldraw_snapshot = clickInfo.event.extendedProps?.tldraw_snapshot;
    if (tldraw_snapshot) {
      // TODO: Implement tldraw_snapshot retrieval from storage API
      // For now, we'll just log it
      console.log('TLDraw snapshot:', tldraw_snapshot);
    }
  }, []);

  const filteredEvents = useMemo(() => 
    events.filter(event => 
      selectedClasses.includes(event.extendedProps?.subjectClass || '')
    ), [events, selectedClasses]
  );

  const handleResize = useCallback(() => {
    if (calendarRef.current) {
      calendarRef.current.getApi().updateSize();
    }
  }, []);

  useEffect(() => {
    window.addEventListener('resize', handleResize);
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, [handleResize]);

  const toggleDropdown = useCallback((eventId: string) => {
    setOpenDropdownId(openDropdownId === eventId ? null : eventId);
  }, [openDropdownId]);

  const toggleSubjectClassDivVisibility = useCallback((subjectClass: string) => {
    setHiddenSubjectClassDivs(prev => 
      prev.includes(subjectClass)
        ? prev.filter(c => c !== subjectClass)
        : [...prev, subjectClass]
    );
  }, []);

  const togglePeriodCodeDivVisibility = useCallback((subjectClass: string) => {
    setHiddenPeriodCodeDivs(prev => 
      prev.includes(subjectClass)
        ? prev.filter(c => c !== subjectClass)
        : [...prev, subjectClass]
    );
  }, []);

  const toggleTimeDivVisibility = useCallback((subjectClass: string) => {
    setHiddenTimeDivs(prev => 
      prev.includes(subjectClass)
        ? prev.filter(c => c !== subjectClass)
        : [...prev, subjectClass]
    );
  }, []);

  const hideSubjectClassFromView = useCallback((subjectClass: string) => {
    setSelectedClasses(prev => prev.filter(c => c !== subjectClass));
  }, []);

  const toggleAllDivs = useCallback((subjectClass: string, hide: boolean) => {
    const updateHiddenDivs = (prev: string[]) => 
      hide ? [...prev, subjectClass] : prev.filter(c => c !== subjectClass);

    setHiddenSubjectClassDivs(updateHiddenDivs);
    setHiddenPeriodCodeDivs(updateHiddenDivs);
    setHiddenTimeDivs(updateHiddenDivs);
  }, []);

  const areAllDivsHidden = useCallback((subjectClass: string) => {
    return hiddenSubjectClassDivs.includes(subjectClass) &&
           hiddenPeriodCodeDivs.includes(subjectClass) &&
           hiddenTimeDivs.includes(subjectClass);
  }, [hiddenSubjectClassDivs, hiddenPeriodCodeDivs, hiddenTimeDivs]);

  const renderEventContent = useCallback((eventInfo: EventContentArg) => {
    const { event } = eventInfo;
    const subjectClass = event.extendedProps?.subjectClass || 'Subject Class';
    const originalColor = event.extendedProps?.color || '#ffffff';
    const lightenedColor = lightenColor(originalColor, 0.9);

    const eventStyle = {
      backgroundColor: lightenedColor,
      color: '#000',
      padding: '4px 6px',
      borderRadius: '6px',
      fontSize: '1.0em',
      overflow: 'visible',
      display: 'flex',
      flexDirection: 'column' as const,
      height: '100%',
      boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
      border: `2px solid ${originalColor}`,
      position: 'relative' as const,
    };

    const titleStyle = {
      fontWeight: 'bold' as const,
      whiteSpace: 'nowrap' as const,
      overflow: 'hidden',
      textOverflow: 'ellipsis',
      paddingRight: '20px',
    };

    const contentStyle = {
      fontSize: '0.8em',
      whiteSpace: 'nowrap' as const,
      overflow: 'hidden',
      textOverflow: 'ellipsis'
    };

    const ellipsisStyle = {
      position: 'absolute' as const,
      top: '4px',
      right: '4px',
      cursor: 'pointer',
      zIndex: 10,
    };

    return (
      <div className={`custom-event-content ${openDropdownId === event.id ? 'event-with-dropdown' : ''}`} style={eventStyle}>
        <div style={titleStyle}>{event.title}</div>
        <div style={ellipsisStyle}>
          <FaEllipsisV onClick={(e) => {
            e.stopPropagation();
            toggleDropdown(event.id);
          }} />
        </div>
        {openDropdownId === event.id && (
          <div className="event-dropdown" style={{ position: 'absolute'}}>
            <div onClick={(e) => {
                e.stopPropagation();
                hideSubjectClassFromView(subjectClass);
                setOpenDropdownId(null);
            }}>
                Hide this class from view
            </div>
            <div onClick={(e) => {
              e.stopPropagation();
              toggleAllDivs(subjectClass, !areAllDivsHidden(subjectClass));
              setOpenDropdownId(null);
            }}>
              {areAllDivsHidden(subjectClass) ? 'Show' : 'Hide'} all divs
            </div>
            <div onClick={(e) => {
              e.stopPropagation();
              toggleSubjectClassDivVisibility(subjectClass);
              setOpenDropdownId(null);
            }}>
              {hiddenSubjectClassDivs.includes(subjectClass) ? 'Show' : 'Hide'} subject class
            </div>
            <div onClick={(e) => {
              e.stopPropagation();
              togglePeriodCodeDivVisibility(subjectClass);
              setOpenDropdownId(null);
            }}>
              {hiddenPeriodCodeDivs.includes(subjectClass) ? 'Show' : 'Hide'} period code
            </div>
            <div onClick={(e) => {
              e.stopPropagation();
              toggleTimeDivVisibility(subjectClass);
              setOpenDropdownId(null);
            }}>
              {hiddenTimeDivs.includes(subjectClass) ? 'Show' : 'Hide'} time
            </div>
          </div>
        )}
        {!hiddenSubjectClassDivs.includes(subjectClass) && (
          <div style={contentStyle} className="event-subject-class">{subjectClass}</div>
        )}
        {!hiddenPeriodCodeDivs.includes(subjectClass) && (
          <div style={contentStyle} className="event-period">{event.extendedProps?.periodCode || 'Period Code'}</div>
        )}
        {!hiddenTimeDivs.includes(subjectClass) && (
          <div style={contentStyle} className="event-time">
            {event.start?.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})} - 
            {event.end?.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}
          </div>
        )}
      </div>
    );
  }, [openDropdownId, hiddenSubjectClassDivs, hiddenPeriodCodeDivs, hiddenTimeDivs, toggleDropdown, hideSubjectClassFromView, toggleAllDivs, areAllDivsHidden, toggleSubjectClassDivVisibility, togglePeriodCodeDivVisibility, toggleTimeDivVisibility]);

  const calendarOptions: CalendarOptions = useMemo(() => ({
    plugins: [dayGridPlugin, timeGridPlugin, interactionPlugin, multiMonthPlugin, listPlugin],
    initialView: "timeGridWeek",
    headerToolbar: {
      left: 'prev,next today',
      center: 'title',
      right: 'viewToggle filterClassesButton'
    },
    customButtons: {
      filterClassesButton: {
        text: 'Filter Classes',
        click: () => {} // We'll implement this differently later
      },
      viewToggle: {
        text: 'Change View',
        click: () => {} // We'll implement this differently later
      }
    },
    views: {
      dayGridYear: {
        type: 'dayGrid',
        duration: { years: 1 },
        buttonText: 'Year Grid',
        visibleRange: (currentDate: Date) => ({
          start: eventRange.start || currentDate,
          end: eventRange.end || currentDate
        }),
      },
      dayGridMonth: {
        buttonText: 'Month',
        visibleRange: (currentDate: Date) => ({
          start: eventRange.start || currentDate,
          end: eventRange.end || currentDate
        }),
      },
      timeGridWeek: {
        buttonText: 'Week',
        visibleRange: (currentDate: Date) => ({
          start: eventRange.start || currentDate,
          end: eventRange.end || currentDate
        }),
      },
      timeGridDay: {
        buttonText: 'Day',
        visibleRange: (currentDate: Date) => ({
          start: eventRange.start || currentDate,
          end: eventRange.end || currentDate
        }),
      },
      listYear: {
        buttonText: 'List Year',
        visibleRange: (currentDate: Date) => ({
          start: eventRange.start || currentDate,
          end: eventRange.end || currentDate
        }),
      },
      listMonth: {
        buttonText: 'List Month',
        visibleRange: (currentDate: Date) => ({
          start: eventRange.start || currentDate,
          end: eventRange.end || currentDate
        }),
      },
      listWeek: {
        buttonText: 'List Week',
        visibleRange: (currentDate: Date) => ({
          start: eventRange.start || currentDate,
          end: eventRange.end || currentDate
        }),
      },
      listDay: {
        buttonText: 'List Day',
        visibleRange: (currentDate: Date) => ({
          start: eventRange.start || currentDate,
          end: eventRange.end || currentDate
        }),
      },
    },
    validRange: eventRange.start && eventRange.end ? {
      start: eventRange.start,
      end: eventRange.end
    } : undefined,
    events: filteredEvents,
    height: "100%",
    slotMinTime: "08:00:00",
    slotMaxTime: "17:00:00",
    allDaySlot: false,
    expandRows: true,
    slotEventOverlap: false,
    slotDuration: "00:30:00",
    slotLabelInterval: "01:00",
    eventContent: renderEventContent,
    eventClassNames: (arg: { event: { extendedProps?: { subjectClass?: string } } }) => 
      [arg.event.extendedProps?.subjectClass || ''],
    eventDidMount: (arg: { event: { extendedProps?: { color?: string }; id: string }; el: HTMLElement }) => {
      if (arg.event.extendedProps?.color) {
        const originalColor = arg.event.extendedProps.color;
        const lightenedColor = lightenColor(originalColor, 0.4);
        arg.el.style.backgroundColor = lightenedColor;
        arg.el.style.borderColor = originalColor;
      }
      
      const updateEventContent = () => {
        const height = arg.el.offsetHeight;
        const contentElements = arg.el.querySelectorAll('.custom-event-content > div:not(.event-dropdown)');
        
        contentElements.forEach((el, index) => {
          const element = el as HTMLElement;
          if (index === 0 || index === 1) {
            element.style.display = 'block';
          } else if (height >= 40 && index === 2) {
            element.style.display = 'block';
          } else if (height >= 60 && index === 3) {
            element.style.display = 'block';
          } else if (height >= 80 && index === 4) {
            element.style.display = 'block';
          } else {
            element.style.display = 'none';
          }
        });
      };

      updateEventContent();
      
      const resizeObserver = new ResizeObserver(updateEventContent);
      resizeObserver.observe(arg.el);

      return () => resizeObserver.disconnect();
    },
    eventClick: handleEventClick,
  }), [eventRange.start, eventRange.end, filteredEvents, renderEventContent, handleEventClick]);

  if (!user) {
    console.log('User not logged in');
    return <div>Please log in to view your calendar.</div>;
  }

  return (
    <div className="calendar-page">
      <div className="calendar-container" style={{ height: '100vh', position: 'relative' }}>
        <FullCalendar
          {...calendarOptions}
          ref={calendarRef}
        />
      </div>
    </div>
  );
};

export default CalendarPage;