// frontend/components/calendar/EvBlock.tsx — 주/일 그리드의 이벤트 블록(드래그 이동·리사이즈)
"use client";
import { useEffect, useRef, useState, type MouseEvent as ReactMouseEvent } from "react";
import { GRID_END_MIN, HSTART, PXH, fromMin, toMin, topOf } from "@/lib/calendar/time";
import type { CalEvent, Calendar } from "@/lib/calendar/types";

export type EvCommit = (
  ev: CalEvent,
  patch: { date: string; start: string; end: string },
) => void;

export function EvBlock({
  ev,
  cal,
  onClick,
  idx = 0,
  onCommit,
}: {
  ev: CalEvent;
  cal: Calendar;
  onClick: (ev: CalEvent, e: ReactMouseEvent) => void;
  idx?: number; // 종일 일정 세로 스택 인덱스
  onCommit?: EvCommit; // 드래그 이동/리사이즈 확정(없으면 드래그 비활성)
}) {
  // 종일(시작 시각 없음) 일정은 그리드 상단에 22px 간격으로 쌓는다(NaN 위치 방지).
  const allDay = !ev.start;
  const tone = cal?.tone ?? "ink"; // 캘린더 누락 시에도 안전
  const baseTop = allDay ? idx * 22 : topOf(ev.start);
  const baseH = allDay ? 20 : ((toMin(ev.end) - toMin(ev.start)) / 60) * PXH;
  const short = allDay || baseH < 38;
  const canDrag = !allDay && !!onCommit;

  const [active, setActive] = useState(false);
  const [vis, setVis] = useState<{ mode: "move" | "resize"; dy: number }>({ mode: "move", dy: 0 });
  const dragRef = useRef<{ mode: "move" | "resize"; startX: number; startY: number; moved: boolean } | null>(null);
  const suppressClick = useRef(false);

  useEffect(() => {
    if (!active) return;
    const onMove = (e: MouseEvent) => {
      const d = dragRef.current;
      if (!d) return;
      const dy = e.clientY - d.startY;
      if (Math.abs(dy) > 3 || Math.abs(e.clientX - d.startX) > 3) d.moved = true;
      setVis({ mode: d.mode, dy });
    };
    const onUp = (e: MouseEvent) => {
      const d = dragRef.current;
      dragRef.current = null;
      setActive(false);
      if (!d || !d.moved || !onCommit) return;
      suppressClick.current = true; // 드래그 직후의 click 으로 팝오버가 열리지 않도록
      const dy = e.clientY - d.startY;
      const deltaMin = Math.round((dy / PXH) * 60 / 15) * 15; // 15분 스냅
      const startMin = toMin(ev.start);
      const endMin = toMin(ev.end);
      const dur = endMin - startMin;
      if (d.mode === "move") {
        let ns = startMin + deltaMin;
        ns = Math.max(HSTART * 60, Math.min(ns, GRID_END_MIN - dur));
        // 날짜 변경(주 뷰): 포인터 아래 컬럼의 data-date 로 이동
        let date = ev.date;
        const under = document.elementFromPoint(e.clientX, e.clientY) as HTMLElement | null;
        const col = under?.closest("[data-date]") as HTMLElement | null;
        if (col?.dataset.date) date = col.dataset.date;
        if (ns === startMin && date === ev.date) return;
        onCommit(ev, { date, start: fromMin(ns), end: fromMin(ns + dur) });
      } else {
        let ne = endMin + deltaMin;
        ne = Math.max(startMin + 15, Math.min(ne, GRID_END_MIN));
        if (ne === endMin) return;
        onCommit(ev, { date: ev.date, start: ev.start, end: fromMin(ne) });
      }
    };
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseup", onUp);
    return () => {
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseup", onUp);
    };
  }, [active, ev, onCommit]);

  const beginDrag = (mode: "move" | "resize", e: ReactMouseEvent) => {
    if (!canDrag) return;
    e.stopPropagation();
    e.preventDefault();
    dragRef.current = { mode, startX: e.clientX, startY: e.clientY, moved: false };
    setVis({ mode, dy: 0 });
    setActive(true);
  };

  let top = baseTop;
  let h = baseH;
  if (active && vis.mode === "move") top = baseTop + vis.dy;
  if (active && vis.mode === "resize") h = Math.max(baseH + vis.dy, 20);

  const handleClick = (e: ReactMouseEvent) => {
    e.stopPropagation();
    if (suppressClick.current) {
      suppressClick.current = false;
      return;
    }
    onClick(ev, e);
  };

  return (
    <div
      className={
        "ev tint-" +
        tone +
        (allDay ? " allday" : "") +
        (ev.soon ? " soon" : "") +
        (short ? " short" : "") +
        (canDrag ? " draggable" : "") +
        (active ? " dragging" : "")
      }
      style={{ top: top + "px", height: Math.max(h - 3, 20) + "px" }}
      onMouseDown={canDrag ? (e) => beginDrag("move", e) : undefined}
      onClick={handleClick}
    >
      <div className="et">{ev.title}</div>
      {!allDay && (
        <div className="etm">
          {ev.start}–{ev.end}
        </div>
      )}
      {canDrag && (
        <div className="ev-resize" onMouseDown={(e) => beginDrag("resize", e)} aria-hidden />
      )}
    </div>
  );
}
