import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
  DAY_LABELS,
  formatHourLabel,
  getMeetingSegments,
  getOverlapLevel,
  HOURS_PER_DAY,
  isLaterOverlappingMeeting,
  meetingCoversSlot,
  meetingStartLinear,
  normalizeLinearSelection,
  normalizeRectangle,
  shiftMeetingRange,
  shiftSlot,
  slotKey,
} from '../../data/availabilityUtils';

/** Every (dayIndex, hour) cell in the display rectangle, converted to native slots. */
function rectCellsNative(a, b, shiftHours) {
  const { start, end } = normalizeRectangle(a, b);
  const cells = [];
  for (let d = start.dayIndex; d <= end.dayIndex; d += 1) {
    for (let h = start.hour; h <= end.hour; h += 1) {
      cells.push(shiftSlot(d, h, -shiftHours));
    }
  }
  return cells;
}

function RangeOutline({ startHour, endHour, className, style, title }) {
  const topPct = (startHour / HOURS_PER_DAY) * 100;
  const heightPct = ((endHour - startHour) / HOURS_PER_DAY) * 100;

  return (
    <div
      className={className}
      style={{ top: `${topPct}%`, height: `${heightPct}%`, ...style }}
      title={title}
      aria-hidden={!title}
    />
  );
}

export function AvailabilityGrid({
  mode,
  weekNumber,
  availableSlots,
  overlapCount,
  teamSize = 0,
  onApplyCells,
  onHoverSlot,
  onSelectionComplete,
  onSelectionStart,
  onMeetingClick,
  selection,
  meetings = [],
  activeMeetingId,
  shiftHours = 0,
  sectionTitle,
  sectionSub,
}) {
  const [dragging, setDragging] = useState(false);
  const [dragStart, setDragStart] = useState(null);
  const [dragEnd, setDragEnd] = useState(null);
  const [toggleTarget, setToggleTarget] = useState(null);
  const gridRef = useRef(null);

  const isPersonal = mode === 'personal';
  const weekMeetings = useMemo(
    () => meetings.filter((m) => m.weekNumber === weekNumber),
    [meetings, weekNumber]
  );

  const clearDrag = useCallback(() => {
    setDragging(false);
    setDragStart(null);
    setDragEnd(null);
    setToggleTarget(null);
  }, []);

  useEffect(() => {
    if (!dragging || isPersonal) return undefined;

    const onUp = () => {
      if (dragStart && dragEnd) {
        const isClick =
          dragStart.dayIndex === dragEnd.dayIndex && dragStart.hour === dragEnd.hour;
        if (isClick) {
          const native = shiftSlot(dragStart.dayIndex, dragStart.hour, -shiftHours);
          const meeting = weekMeetings.find((m) =>
            meetingCoversSlot(m, native.dayIndex, native.hour)
          );
          if (meeting) {
            onMeetingClick?.(meeting);
            clearDrag();
            return;
          }
        }
        onSelectionComplete?.({
          start: shiftSlot(dragStart.dayIndex, dragStart.hour, -shiftHours),
          end: shiftSlot(dragEnd.dayIndex, dragEnd.hour, -shiftHours),
        });
      }
      clearDrag();
    };

    document.addEventListener('mouseup', onUp);
    return () => document.removeEventListener('mouseup', onUp);
  }, [
    dragging,
    dragStart,
    dragEnd,
    isPersonal,
    onSelectionComplete,
    onMeetingClick,
    weekMeetings,
    clearDrag,
    shiftHours,
  ]);

  useEffect(() => {
    if (!dragging || !isPersonal) return undefined;

    const onUp = () => clearDrag();
    document.addEventListener('mouseup', onUp);
    return () => document.removeEventListener('mouseup', onUp);
  }, [dragging, isPersonal, clearDrag]);

  const applyPersonalRect = useCallback(
    (start, end, enabled) => {
      if (!start || !end) return;
      onApplyCells?.(rectCellsNative(start, end, shiftHours), enabled);
    },
    [onApplyCells, shiftHours]
  );

  const handleCellMouseDown = (dayIndex, hour, e) => {
    e.preventDefault();
    if (isPersonal) {
      const native = shiftSlot(dayIndex, hour, -shiftHours);
      const willEnable = !availableSlots.has(slotKey(native.dayIndex, native.hour));
      const start = { dayIndex, hour };
      setToggleTarget(willEnable);
      setDragging(true);
      setDragStart(start);
      setDragEnd(start);
      applyPersonalRect(start, start, willEnable);
      onHoverSlot?.({ dayIndex, hour });
      return;
    }

    onSelectionStart?.();
    setDragging(true);
    setDragStart({ dayIndex, hour });
    setDragEnd({ dayIndex, hour });
    onHoverSlot?.({ dayIndex, hour });
  };

  const handleCellMouseEnter = (dayIndex, hour) => {
    onHoverSlot?.({ dayIndex, hour });

    if (!dragging) return;

    const end = { dayIndex, hour };
    setDragEnd(end);

    if (isPersonal && toggleTarget !== null && dragStart) {
      applyPersonalRect(dragStart, end, toggleTarget);
    }
  };

  const handleCellMouseLeave = () => {
    if (!dragging) onHoverSlot?.(null);
  };

  const activeSelection = useMemo(() => {
    if (isPersonal) return null;
    if (dragging && dragStart && dragEnd) {
      return normalizeLinearSelection(dragStart, dragEnd);
    }
    if (selection) {
      // `selection` is in native (server) coordinates; shift to display.
      const displayStart = shiftSlot(selection.start.dayIndex, selection.start.hour, shiftHours);
      const displayEnd = shiftSlot(selection.end.dayIndex, selection.end.hour, shiftHours);
      return normalizeLinearSelection(displayStart, displayEnd);
    }
    return null;
  }, [isPersonal, dragging, dragStart, dragEnd, selection, shiftHours]);

  const selectionSegments = useMemo(() => {
    if (!activeSelection || isPersonal) return [];
    const { start, end } = activeSelection;
    return getMeetingSegments({
      startDay: start.dayIndex,
      startHour: start.hour,
      endDay: end.dayIndex,
      endHour: end.hour + 1,
    });
  }, [activeSelection, isPersonal]);

  const meetingOverlays = useMemo(() => {
    if (isPersonal) return [];
    // Shift every meeting into display coordinates first so overlap/ordering
    // comparisons below stay internally consistent.
    const displayMeetings = weekMeetings.map((m) => shiftMeetingRange(m, shiftHours));
    const sorted = [...displayMeetings].sort(
      (a, b) => meetingStartLinear(a) - meetingStartLinear(b)
    );
    const items = [];
    sorted.forEach((meeting, paintOrder) => {
      getMeetingSegments(meeting).forEach((segment) => {
        items.push({
          meeting,
          segment,
          paintOrder,
          isLater: isLaterOverlappingMeeting(segment, meeting, displayMeetings),
        });
      });
    });
    return items;
  }, [isPersonal, weekMeetings, shiftHours]);

  const getCellLevel = (dayIndex, hour) => {
    if (isPersonal) return 0;
    const native = shiftSlot(dayIndex, hour, -shiftHours);
    const count = overlapCount?.(native.dayIndex, native.hour) ?? 0;
    return getOverlapLevel(count, teamSize);
  };

  return (
    <div className="availability-section-block">
      <header className="availability-section-header">
        <h3 className="availability-section-title">{sectionTitle}</h3>
        <p className="availability-section-sub">{sectionSub}</p>
      </header>
      <div className="availability-grid-wrap" ref={gridRef}>
        <div className="availability-hour-labels">
          {Array.from({ length: HOURS_PER_DAY }, (_, hour) => (
            <span key={hour} className="availability-hour-label">
              {hour % 3 === 0 ? formatHourLabel(hour) : ''}
            </span>
          ))}
        </div>
        <div className="availability-grid-main">
          <div className="availability-day-headers">
            {DAY_LABELS.map((label) => (
              <span key={label} className="availability-day-header">
                {label}
              </span>
            ))}
          </div>
          <div className="availability-columns">
            {DAY_LABELS.map((_, dayIndex) => (
              <div key={dayIndex} className="availability-column">
                {Array.from({ length: HOURS_PER_DAY }, (_, hour) => {
                  const key = slotKey(dayIndex, hour);
                  const native = shiftSlot(dayIndex, hour, -shiftHours);
                  const available = isPersonal
                    ? availableSlots.has(slotKey(native.dayIndex, native.hour))
                    : false;
                  const level = isPersonal ? (available ? 4 : 0) : getCellLevel(dayIndex, hour);

                  return (
                    <div
                      key={key}
                      data-slot={key}
                      className={[
                        'availability-cell',
                        `availability-cell--level-${level}`,
                        isPersonal && available ? 'availability-cell--available' : '',
                      ]
                        .filter(Boolean)
                        .join(' ')}
                      onMouseDown={(e) => handleCellMouseDown(dayIndex, hour, e)}
                      onMouseEnter={() => handleCellMouseEnter(dayIndex, hour)}
                      onMouseLeave={handleCellMouseLeave}
                    />
                  );
                })}

                {!isPersonal &&
                  meetingOverlays
                    .filter(({ segment }) => segment.dayIndex === dayIndex)
                    .map(({ meeting, segment, paintOrder, isLater }) => (
                      <RangeOutline
                        key={`${meeting.id}-${segment.startHour}`}
                        startHour={segment.startHour}
                        endHour={segment.endHour}
                        className={[
                          'availability-range-outline',
                          'availability-range-outline--meeting',
                          isLater ? 'availability-range-outline--later' : '',
                          meeting.id === activeMeetingId ? 'availability-range-outline--active' : '',
                        ]
                          .filter(Boolean)
                          .join(' ')}
                        style={{ zIndex: 10 + paintOrder }}
                        title={meeting.subject}
                      />
                    ))}

                {!isPersonal &&
                  selectionSegments
                    .filter((segment) => segment.dayIndex === dayIndex)
                    .map((segment) => (
                      <RangeOutline
                        key={`sel-${segment.startHour}`}
                        startHour={segment.startHour}
                        endHour={segment.endHour}
                        className="availability-range-outline availability-range-outline--selection"
                        style={{ zIndex: 30 }}
                      />
                    ))}
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}
