import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import type { HubScheduleItem } from '../../lib/hubConfig';
import {
  findScheduleIndexForToday,
  formatScheduleDateParts,
  isSchedulePast,
  isScheduleToday,
  scheduleWindowStart,
  sortScheduleItems,
} from '../../lib/hubSchedule';

const VISIBLE_COUNT = 3;
const ROW_STEP_PX = 46;
const WHEEL_THRESHOLD = 18;
const WHEEL_COOLDOWN_MS = 140;

interface HubScheduleCarouselProps {
  items: HubScheduleItem[];
  focusDate?: Date;
}

function slotOpacity(slot: number): number {
  if (slot === 0) return 0.4;
  if (slot === 1) return 1;
  if (slot === 2) return 0.88;
  return 0.28;
}

export function HubScheduleCarousel({ items, focusDate }: HubScheduleCarouselProps) {
  const viewportRef = useRef<HTMLDivElement | null>(null);
  const wheelAccRef = useRef(0);
  const wheelCooldownRef = useRef(false);
  const dragRef = useRef<{ y: number; scroll: number } | null>(null);

  const focus = focusDate ?? new Date();
  const sorted = useMemo(
    () => sortScheduleItems(items.filter((i) => i.date && i.text.trim())),
    [items],
  );

  const focusIndex = useMemo(() => findScheduleIndexForToday(sorted, focus), [sorted, focus]);
  const initialStart = useMemo(
    () => scheduleWindowStart(sorted.length, focusIndex, VISIBLE_COUNT),
    [sorted.length, focusIndex],
  );

  const maxStart = Math.max(0, sorted.length - VISIBLE_COUNT);
  const scrollable = sorted.length > VISIBLE_COUNT;

  const clampScrollPx = useCallback(
    (px: number) => Math.min(maxStart * ROW_STEP_PX, Math.max(0, px)),
    [maxStart],
  );

  const snapScrollPx = useCallback(
    (px: number) => {
      const idx = Math.round(px / ROW_STEP_PX);
      return clampScrollPx(idx * ROW_STEP_PX);
    },
    [clampScrollPx],
  );

  const [scrollPx, setScrollPx] = useState(() => initialStart * ROW_STEP_PX);
  const [isDragging, setIsDragging] = useState(false);

  useEffect(() => {
    setScrollPx(initialStart * ROW_STEP_PX);
  }, [initialStart]);

  const startIndex = Math.round(scrollPx / ROW_STEP_PX);

  const stepBy = useCallback(
    (delta: number) => {
      if (delta === 0) return;
      setScrollPx((prev) => snapScrollPx(prev + delta * ROW_STEP_PX));
    },
    [snapScrollPx],
  );

  useEffect(() => {
    const viewport = viewportRef.current;
    if (!viewport || !scrollable) return;

    const onWheel = (event: WheelEvent) => {
      if (Math.abs(event.deltaY) < 1) return;
      event.preventDefault();
      if (wheelCooldownRef.current || dragRef.current) return;

      wheelAccRef.current += event.deltaY;
      if (Math.abs(wheelAccRef.current) < WHEEL_THRESHOLD) return;

      const dir = wheelAccRef.current > 0 ? 1 : -1;
      wheelAccRef.current = 0;
      wheelCooldownRef.current = true;
      stepBy(dir);
      window.setTimeout(() => {
        wheelCooldownRef.current = false;
      }, WHEEL_COOLDOWN_MS);
    };

    viewport.addEventListener('wheel', onWheel, { passive: false });
    return () => viewport.removeEventListener('wheel', onWheel);
  }, [scrollable, stepBy]);

  const onPointerDown = useCallback(
    (event: React.PointerEvent<HTMLDivElement>) => {
      if (event.button !== 0 || !scrollable) return;
      dragRef.current = { y: event.clientY, scroll: scrollPx };
      setIsDragging(true);
      event.currentTarget.setPointerCapture(event.pointerId);
      event.preventDefault();
    },
    [scrollPx, scrollable],
  );

  const onPointerMove = useCallback(
    (event: React.PointerEvent<HTMLDivElement>) => {
      if (!dragRef.current) return;
      const deltaY = event.clientY - dragRef.current.y;
      setScrollPx(clampScrollPx(dragRef.current.scroll - deltaY));
    },
    [clampScrollPx],
  );

  const endDrag = useCallback(
    (event: React.PointerEvent<HTMLDivElement>) => {
      if (!dragRef.current) return;
      dragRef.current = null;
      setIsDragging(false);
      setScrollPx((prev) => snapScrollPx(prev));
      if (event.currentTarget.hasPointerCapture(event.pointerId)) {
        event.currentTarget.releasePointerCapture(event.pointerId);
      }
    },
    [snapScrollPx],
  );

  if (sorted.length === 0) {
    return (
      <div className="hub-schedule-wheel-viewport hub-schedule-wheel-viewport--empty">
        <p className="board-project-desc hub-schedule-empty">등록된 일정 없음</p>
      </div>
    );
  }

  return (
    <div className="hub-schedule-wheel-wrap">
      <div
        ref={viewportRef}
        className={`hub-schedule-wheel-viewport${isDragging ? ' is-dragging' : ''}${scrollable ? ' is-scrollable' : ''}`}
        onPointerDown={onPointerDown}
        onPointerMove={onPointerMove}
        onPointerUp={endDrag}
        onPointerCancel={endDrag}
      >
        <div
          className={`hub-schedule-wheel-track${isDragging ? ' is-dragging' : ''}`}
          style={{ transform: `translateY(-${scrollPx}px)` }}
        >
          {sorted.map((item, index) => {
            const past = isSchedulePast(item.date, focus);
            const today = isScheduleToday(item.date, focus);
            const dateParts = formatScheduleDateParts(item.date);
            const inWindow = index >= startIndex && index < startIndex + VISIBLE_COUNT;
            const slot = inWindow ? index - startIndex : -1;

            return (
              <div
                key={item.id}
                className={`hub-schedule-wheel-row${past ? ' is-past' : ''}${today ? ' is-today' : ''}${inWindow ? ` is-slot-${slot}` : ''}`}
                style={inWindow ? { opacity: slotOpacity(slot) } : undefined}
              >
                <span className="hub-schedule-wheel-date">
                  {dateParts ? (
                    <>
                      <span className="hub-schedule-date-month">{dateParts.month}</span>
                      <span className="hub-schedule-date-day">{dateParts.day}</span>
                    </>
                  ) : (
                    item.date
                  )}
                </span>
                <span className="hub-schedule-wheel-text">{item.text}</span>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}
