/* planner-board.jsx — the week board with day columns, blocks, drag & drop */

const { useState, useRef, useEffect, useMemo, useCallback } = React;

function PlannerBoard({ state, settings, currentWeekStart, weekStartDate, activeId, onSelect, onCreate, onUpdate, onPlaceSticker, motion }) {
  const { DAY_NAMES, DAY_SHORT, fmtDate, fmtHour, fmtTime, computeLayouts, snapMin, clamp, blockDays } = window.PLANNER_DATA;

  const blocks = state.blocks;
  const visibleDays = settings.includeWeekend ? 7 : 5;
  const dayColors = ["var(--c-mon)","var(--c-tue)","var(--c-wed)","var(--c-thu)","var(--c-fri)","var(--c-sat)","var(--c-sun)"];

  const startMin = settings.startHour * 60;
  const endMin = settings.endHour * 60;
  const totalMin = Math.max(endMin - startMin, 1);
  const hours = settings.endHour - settings.startHour;
  const HOUR_H = 72;
  const shelfHeight = hours * HOUR_H;

  const layouts = useMemo(() => computeLayouts(blocks), [blocks]);
  const blockInstances = useMemo(() => {
    const instances = [];
    blocks.forEach(block => {
      blockDays(block).forEach(day => {
        if (day < visibleDays) {
          instances.push({ block, day });
        }
      });
    });
    return instances;
  }, [blocks, visibleDays, blockDays]);

  /* ---------- Drag state ---------- */
  const [drag, setDrag] = useState(null); // { id, mode: "move"|"resize", offsetMin, originDay, originStart, originEnd, hoverDay, hoverStart }
  const dragRef = useRef(null);
  const dragFrame = useRef(null);
  const shelfRefs = useRef({});

  /* ---------- Inline title edit ---------- */
  const [editingTitleId, setEditingTitleId] = useState(null);
  const [titleDraft, setTitleDraft] = useState("");
  const titleEditRef = useRef(null);

  useEffect(() => {
    if (editingTitleId && titleEditRef.current) {
      titleEditRef.current.focus();
      titleEditRef.current.select();
    }
  }, [editingTitleId]);

  const startTitleEdit = (e, block) => {
    e.stopPropagation();
    e.preventDefault();
    setEditingTitleId(block.id);
    setTitleDraft(block.title || "");
    onSelect(block.id);
  };

  const commitTitleEdit = (block) => {
    const next = (titleDraft || "").trim();
    if (next && next !== block.title) onUpdate(block.id, { title: next });
    setEditingTitleId(null);
  };

  useEffect(() => {
    dragRef.current = drag;
  }, [drag]);

  const onPointerDownBlock = (e, block, instanceDay, mode="move") => {
    if (e.button != null && e.button !== 0) return;
    e.stopPropagation();
    e.currentTarget.setPointerCapture && e.currentTarget.setPointerCapture(e.pointerId);
    const rect = e.currentTarget.getBoundingClientRect();
    const clickMin = startMin + ((e.clientY - rect.top) / rect.height) * (block.end - block.start);
    const nextDrag = {
      id: block.id,
      mode,
      offsetMin: mode === "move" ? (clickMin - block.start) : 0,
      originDay: instanceDay,
      originStart: block.start,
      originEnd: block.end,
      hoverDay: instanceDay,
      hoverStart: block.start,
      hoverEnd: block.end,
      repeatDays: blockDays(block)
    };
    dragRef.current = nextDrag;
    setDrag(nextDrag);
    onSelect(block.id);
  };

  useEffect(() => {
    if (!drag) return;
    function getDayFromX(x, y) {
      for (let d = 0; d < visibleDays; d++) {
        const el = shelfRefs.current[d];
        if (!el) continue;
        const r = el.getBoundingClientRect();
        if (x >= r.left && x <= r.right && y >= r.top - 20 && y <= r.bottom + 20) {
          return { day: d, rect: r };
        }
      }
      return null;
    }
    function onMove(e) {
      const current = dragRef.current;
      if (!current) return;
      const hit = getDayFromX(e.clientX, e.clientY);
      if (!hit) return;
      const dur = current.originEnd - current.originStart;
      const y = clamp(e.clientY - hit.rect.top, 0, hit.rect.height);
      const minutes = startMin + (y / hit.rect.height) * totalMin;
      const isRepeating = current.repeatDays && current.repeatDays.length > 1;
      const hoverDay = isRepeating ? current.originDay : hit.day;

      let next;
      if (current.mode === "move") {
        const rawStart = minutes - current.offsetMin;
        const clampedStart = clamp(rawStart, startMin, endMin - dur);
        next = { ...current, hoverDay, hoverStart: clampedStart, hoverEnd: clampedStart + dur };
      } else {
        const clampedEnd = clamp(minutes, current.originStart + 30, endMin);
        next = { ...current, hoverDay, hoverStart: current.originStart, hoverEnd: clampedEnd };
      }

      dragRef.current = next;
      if (dragFrame.current) return;
      dragFrame.current = requestAnimationFrame(() => {
        dragFrame.current = null;
        setDrag(dragRef.current);
      });
    }
    function onUp() {
      const current = dragRef.current;
      if (!current) return;
      const snappedStart = clamp(snapMin(current.hoverStart), startMin, endMin - 15);
      const snappedEnd = clamp(snapMin(current.hoverEnd), snappedStart + 15, endMin);
      const isRepeating = current.repeatDays && current.repeatDays.length > 1;
      if (current.hoverDay !== current.originDay || snappedStart !== current.originStart || snappedEnd !== current.originEnd) {
        const patch = { start: snappedStart, end: snappedEnd };
        if (!isRepeating) {
          patch.dayIndex = current.hoverDay;
        }
        onUpdate(current.id, patch);
      }
      if (dragFrame.current) {
        cancelAnimationFrame(dragFrame.current);
        dragFrame.current = null;
      }
      dragRef.current = null;
      setDrag(null);
    }
    window.addEventListener("pointermove", onMove);
    window.addEventListener("pointerup", onUp);
    window.addEventListener("pointercancel", onUp);
    return () => {
      window.removeEventListener("pointermove", onMove);
      window.removeEventListener("pointerup", onUp);
      window.removeEventListener("pointercancel", onUp);
    };
  }, [Boolean(drag), visibleDays, startMin, endMin, totalMin, snapMin, clamp, onUpdate]);

  /* ---------- Sticker drop targets ---------- */
  const [stickerHover, setStickerHover] = useState(null);

  useEffect(() => {
    function onStickerMove(e) {
      const d = window.__draggingSticker;
      if (!d) return;
      const el = document.elementFromPoint(e.clientX, e.clientY);
      const block = el && el.closest && el.closest(".block");
      setStickerHover(block ? block.dataset.id : null);
    }
    function onStickerUp(e) {
      const d = window.__draggingSticker;
      if (!d) return;
      const el = document.elementFromPoint(e.clientX, e.clientY);
      const block = el && el.closest && el.closest(".block");
      if (block) {
        const r = block.getBoundingClientRect();
        onPlaceSticker(block.dataset.id, {
          kind: d.kind,
          x: ((e.clientX - r.left) / r.width) * 100,
          y: ((e.clientY - r.top) / r.height) * 100,
          rot: Math.random() * 30 - 15
        });
      }
      setStickerHover(null);
    }
    window.addEventListener("mousemove", onStickerMove);
    window.addEventListener("mouseup", onStickerUp);
    return () => {
      window.removeEventListener("mousemove", onStickerMove);
      window.removeEventListener("mouseup", onStickerUp);
    };
  }, [onPlaceSticker]);

  /* ---------- "Now" line ---------- */
  const [now, setNow] = useState(new Date());
  useEffect(() => {
    const t = setInterval(() => setNow(new Date()), 60000);
    return () => clearInterval(t);
  }, []);
  const todayDay = ((now.getDay() + 6) % 7);
  const nowMin = now.getHours() * 60 + now.getMinutes();
  const showNowOn = (dayIdx) => {
    if (dayIdx !== todayDay) return null;
    if (nowMin < startMin || nowMin > endMin) return null;
    const ws = window.PLANNER_DATA.parseISODate(currentWeekStart);
    const todayDate = window.PLANNER_DATA.addDays(ws, todayDay);
    const realToday = new Date(now.getFullYear(), now.getMonth(), now.getDate());
    if (todayDate.getTime() !== realToday.getTime()) return null;
    return ((nowMin - startMin) / totalMin) * 100;
  };

  /* ---------- Double-click empty slot to add ---------- */
  const onShelfDoubleClick = (e, dayIndex) => {
    if (e.target.closest(".block") || e.target.closest(".add-here")) return;
    if (drag) return;
    const r = e.currentTarget.getBoundingClientRect();
    const y = clamp(e.clientY - r.top, 0, r.height);
    const mins = snapMin(startMin + (y / r.height) * totalMin);
    onCreate({ dayIndex, start: mins, end: Math.min(mins + 45, endMin) });
  };

  return (
    <div className="board-wrap">
      <div className="board" style={{ "--day-count": visibleDays, "--hours": hours }}>
        {/* Time axis */}
        <div className="time-axis">
          {Array.from({ length: hours + 1 }).map((_, i) => {
            const h = settings.startHour + i;
            return (
              <div key={i} className="time-tick" style={{ top: `calc(110px + 6px + ${(i / hours) * 100}% * ${hours} / ${hours + 1})` }}>
                {fmtHour(h)}
              </div>
            );
          })}
        </div>

        {/* Day columns */}
        {Array.from({ length: visibleDays }).map((_, day) => {
          const date = window.PLANNER_DATA.addDays(weekStartDate, day);
          const dayColor = dayColors[day];
          const tilt = (day % 2 === 0 ? -1 : 1) * 1.2;
          const dayBlocks = blockInstances
            .filter(instance => instance.day === day && instance.block.end > startMin && instance.block.start < endMin)
            .map(instance => instance.block);
          const Mascot = window.DAY_MASCOTS[day];
          const nowTop = showNowOn(day);
          const isDropTarget = drag && drag.hoverDay === day && drag.hoverDay !== drag.originDay;

          return (
            <div key={day} className="day-col" style={{ "--day-color": dayColor }}>
              <div className="day-head" style={{ "--head-tilt": `${tilt}deg` }}>
                <span className="tape tl"/>
                <span className="tape tr"/>
                <div>
                  <div className="day-name">{DAY_SHORT[day]}</div>
                  <div className="day-date">{fmtDate(date, { month: "short", day: "numeric" })}</div>
                </div>
                <div className="mascot"><Mascot/></div>
              </div>

              <div
                className={"day-shelf" + (isDropTarget ? " drop-target" : "")}
                ref={el => (shelfRefs.current[day] = el)}
                style={{ minHeight: shelfHeight + "px" }}
                onDoubleClick={(e) => onShelfDoubleClick(e, day)}
              >
                {nowTop != null && <div className="now-line" style={{ top: nowTop + "%" }}/>}

                {/* Drop ghost */}
                {drag && drag.hoverDay === day && (() => {
                  const top = ((drag.hoverStart - startMin) / totalMin) * 100;
                  const ht = ((drag.hoverEnd - drag.hoverStart) / totalMin) * 100;
                  return <div className="drop-ghost" style={{ top: top + "%", height: `max(${ht}%, 48px)` }}/>;
                })()}

                {/* Blocks */}
                {dayBlocks.map(block => {
                  const isDragging = drag && drag.id === block.id;
                  const vStart = clamp(block.start, startMin, endMin);
                  const vEnd = clamp(block.end, startMin, endMin);
                  const top = ((vStart - startMin) / totalMin) * 100;
                  const height = Math.max(((vEnd - vStart) / totalMin) * 100, 6);
                  const lay = layouts[block.id + ":" + day] || { col: 0, cols: 1, nextStart: null };
                  const left = `calc(${(100 / lay.cols) * lay.col}% + 6px)`;
                  const width = `calc(${100 / lay.cols}% - 12px)`;
                  const gapToNext = lay.nextStart != null
                    ? ((Math.min(lay.nextStart, endMin) - vStart) / totalMin) * 100
                    : null;
                  const heightStyle = gapToNext != null
                    ? `${Math.min(height, gapToNext)}%`
                    : `max(${height}%, 48px)`;
                  const typeMeta = window.PLANNER_DATA.BLOCK_TYPES.find(t => t.id === block.typeId) || window.PLANNER_DATA.BLOCK_TYPES[0];
                  const TypeIcon = window.Icon[typeMeta.icon] || window.Icon.book;
                  const completion = window.PLANNER_DATA.planCompletion(block);
                  const rot = ((parseInt(block.id.slice(-2), 36) % 5) - 2) * 0.3;
                  const isHoverSticker = stickerHover === block.id;

                  return (
                    <button
                      key={block.id}
                      data-id={block.id}
                      data-day={day}
                      className={"block" + (block.id === activeId ? " active" : "") + (isDragging ? " dragging" : "")}
                      style={{
                        top: isDragging ? `calc(${((drag.hoverStart - startMin) / totalMin) * 100}% )` : top + "%",
                        height: isDragging ? `max(${((drag.hoverEnd - drag.hoverStart) / totalMin) * 100}%, 48px)` : heightStyle,
                        left, width,
                        "--block-color": block.color,
                        "--rot": rot + "deg",
                        outline: isHoverSticker ? "3px dashed #2a2438" : "none",
                        outlineOffset: isHoverSticker ? "3px" : 0
                      }}
                      onPointerDown={(e) => onPointerDownBlock(e, block, day, "move")}
                      onClick={(e) => { e.stopPropagation(); onSelect(block.id); }}
                      onDoubleClick={(e) => { e.stopPropagation(); onSelect(block.id, true); }}
                    >
                      <span className="b-time">{fmtTime(block.start)} – {fmtTime(block.end)}</span>
                      {editingTitleId === block.id ? (
                        <input
                          ref={titleEditRef}
                          className="b-title b-title-edit"
                          value={titleDraft}
                          maxLength={80}
                          onChange={(e) => setTitleDraft(e.target.value)}
                          onBlur={() => commitTitleEdit(block)}
                          onKeyDown={(e) => {
                            if (e.key === "Enter") { e.preventDefault(); commitTitleEdit(block); }
                            else if (e.key === "Escape") { e.preventDefault(); setEditingTitleId(null); }
                          }}
                          onPointerDown={(e) => e.stopPropagation()}
                          onMouseDown={(e) => e.stopPropagation()}
                          onClick={(e) => e.stopPropagation()}
                          onDoubleClick={(e) => e.stopPropagation()}
                        />
                      ) : (
                        <div
                          className="b-title"
                          title="Double-click to rename"
                          onDoubleClick={(e) => startTitleEdit(e, block)}
                        >
                          {block.title || "Untitled"}
                        </div>
                      )}
                      <div className="b-meta">
                        <span className="b-type-icon"><TypeIcon/></span>
                        <span>{block.group || typeMeta.label}</span>
                      </div>
                      {/* Plan progress dots */}
                      <div className="b-progress">
                        {[0,1,2,3].map(i => (
                          <span key={i} className={"dot" + (completion >= (i+1)*25 ? " on" : "")}/>
                        ))}
                      </div>
                      {/* Stickers — auto-stacked top-right; dragged ones keep their coords */}
                      {block.stickers && (() => {
                        let autoIdx = 0;
                        return block.stickers.map((s, i) => {
                          const kind = typeof s === "string" ? s : s.kind;
                          const Cmp = window.Sticker[kind];
                          if (!Cmp) return null;
                          const isAuto = typeof s === "string" || s.x == null;
                          if (isAuto) {
                            const k = autoIdx++;
                            return (
                              <span key={i} className="placed-sticker"
                                style={{
                                  right: `${4 + k * 16}px`,
                                  top: `4px`,
                                  "--rot": `${(k%2?1:-1)*10}deg`,
                                }}>
                                <Cmp/>
                              </span>
                            );
                          }
                          return (
                            <span key={i} className="placed-sticker"
                              style={{
                                left: `calc(${s.x}% - 13px)`,
                                top: `calc(${s.y}% - 13px)`,
                                "--rot": (s.rot || 0) + "deg",
                              }}>
                              <Cmp/>
                            </span>
                          );
                        });
                      })()}
                      {/* Resize handle */}
                      <span
                        className="resize-handle"
                        onPointerDown={(e) => onPointerDownBlock(e, block, day, "resize")}
                        style={{
                          position: "absolute", left: 0, right: 0, bottom: 0, height: 10,
                          cursor: "ns-resize"
                        }}
                      />
                    </button>
                  );
                })}

                {/* Add hint */}
                <button className="add-here" onDoubleClick={(e) => {
                  e.stopPropagation();
                  onCreate({ dayIndex: day, start: startMin + 60, end: startMin + 105 });
                }} title="Double-click to add block">
                  <svg viewBox="0 0 24 24" fill="none" stroke="#2a2438" strokeWidth="3" strokeLinecap="round" style={{width:18,height:18}}>
                    <path d="M12 5v14M5 12h14"/>
                  </svg>
                </button>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

window.PlannerBoard = PlannerBoard;
