"use client";

import { MILLISECONDS_PER_DAY } from "@capakraken/shared";
import { clsx } from "clsx";
import React from "react";
import type { TimelineAssignmentEntry } from "./TimelineContext.js";
import { applyPointerOffsetPreviewRect, getDragPointerOffset } from "./allocationVisualState.js";
import { heatmapBgColor } from "./heatmapUtils.js";
import { getPlanningEntryMutationId } from "~/lib/planningEntryIds.js";
import { SUB_LANE_HEIGHT } from "./timelineConstants.js";
import { getProjectColor } from "~/lib/project-colors.js";
import type { DragState, AllocDragState, MultiSelectState } from "~/hooks/useTimelineDrag.js";
import type { HeatmapColorScheme } from "~/hooks/useAppPreferences.js";
import type { ResourceCapacitySeries } from "./timelineCapacity.js";
import {
  buildAllocationWorkingDaySegments,
  isAllocationScheduledOnDate,
  toLocalDateKey,
} from "./timelineAvailability.js";
// ─── Shared interaction types ────────────────────────────────────────────────

export interface AllocMouseDownInfo {
  mode: "move" | "resize-start" | "resize-end";
  allocationId: string;
  mutationAllocationId: string;
  projectId: string;
  projectName: string;
  resourceId: string | null;
  startDate: Date;
  endDate: Date;
  allocationStartDate?: Date;
  allocationEndDate?: Date;
  scope?: "allocation" | "segment";
}

// ─── Helper types ───────────────────────────────────────────────────────────

export interface AllocBlockData {
  alloc: TimelineAssignmentEntry;
  lane: number;
}

// ─── Pure render functions (no hooks, extracted from TimelineResourcePanel) ───

export function renderAllocBlocksFromData(
  blockData: AllocBlockData[],
  _allocs: TimelineAssignmentEntry[],
  dragState: DragState,
  allocDragState: AllocDragState,
  toLeft: (d: Date) => number,
  toWidth: (s: Date, e: Date) => number,
  CELL_WIDTH: number,
  totalCanvasWidth: number,
  onAllocMouseDown: (e: React.MouseEvent, info: AllocMouseDownInfo) => void,
  onAllocTouchStart: (e: React.TouchEvent, info: AllocMouseDownInfo) => void,
  onAllocationContextMenu: (
    info: { allocationId: string; projectId: string; contextDate?: Date },
    anchorX: number,
    anchorY: number,
  ) => void,
  multiSelectState: MultiSelectState,
  suppressHoverInteractions: boolean,
  onInlineEdit?: (
    allocationId: string,
    initialValues: { startDate: Date; endDate: Date; hoursPerDay: number },
    barRect: DOMRect,
  ) => void,
  scrollLeft = 0,
  containerWidth = 1200,
  pendingMutationIds?: ReadonlySet<string>,
) {
  const OVERSCAN_PX = 10 * CELL_WIDTH;
  const visibleLeft = scrollLeft - OVERSCAN_PX;
  const visibleRight = scrollLeft + containerWidth + OVERSCAN_PX;
  const anyDragActive = dragState.isDragging || allocDragState.isActive;
  const selectedAllocationSet = new Set(multiSelectState.selectedAllocationIds);

  function toUtcDay(value: Date): Date {
    return new Date(Date.UTC(value.getFullYear(), value.getMonth(), value.getDate()));
  }

  function addUtcDays(value: Date, days: number): Date {
    const next = new Date(value);
    next.setUTCDate(next.getUTCDate() + days);
    return next;
  }

  function resolveSegmentContextDate(
    clientX: number,
    rect: DOMRect,
    segmentStart: Date,
    segmentEnd: Date,
  ): Date {
    const start = toUtcDay(segmentStart);
    const end = toUtcDay(segmentEnd);
    const rawIndex = Math.floor((clientX - rect.left) / CELL_WIDTH);
    const maxIndex = Math.max(
      0,
      Math.round((end.getTime() - start.getTime()) / MILLISECONDS_PER_DAY),
    );
    const dayIndex = Math.min(Math.max(rawIndex, 0), maxIndex);
    return addUtcDays(start, dayIndex);
  }

  function sameDate(a: Date | null, b: Date | null) {
    return Boolean(a && b) && a!.getTime() === b!.getTime();
  }

  return blockData.flatMap(({ alloc, lane }) => {
    const allocStart = toUtcDay(new Date(alloc.startDate));
    const allocEnd = toUtcDay(new Date(alloc.endDate));

    const isProjectShifted = dragState.isDragging && dragState.projectId === alloc.projectId;

    let dispStart = allocStart;
    let dispEnd = allocEnd;
    if (isProjectShifted && dragState.currentStartDate && dragState.currentEndDate) {
      dispStart = dragState.currentStartDate;
      dispEnd = dragState.currentEndDate;
    }

    // Multi-drag offset: shift selected allocations visually during multi-drag
    const isMultiDragTarget =
      multiSelectState.isMultiDragging && selectedAllocationSet.has(alloc.id);
    const multiDragPx = isMultiDragTarget ? multiSelectState.multiDragDaysDelta * CELL_WIDTH : 0;
    const multiDragMode = multiSelectState.multiDragMode;

    const blockTop = 8 + lane * SUB_LANE_HEIGHT;
    const blockHeight = SUB_LANE_HEIGHT - 8;

    const customColor = (alloc.project as { color?: string | null }).color;
    const projectColor = getProjectColor(alloc.projectId);
    const blockBgColor = customColor ?? projectColor.hex + "B3";
    const hasRecurrence = !!(alloc.metadata as Record<string, unknown> | null)?.recurrence;

    const allocInfo: AllocMouseDownInfo = {
      mode: "move",
      allocationId: alloc.id,
      mutationAllocationId: getPlanningEntryMutationId(alloc),
      projectId: alloc.projectId,
      projectName: alloc.project.name,
      resourceId: alloc.resourceId,
      startDate: allocStart,
      endDate: allocEnd,
      allocationStartDate: allocStart,
      allocationEndDate: allocEnd,
      scope: "allocation",
    };

    const segments = buildAllocationWorkingDaySegments(
      { ...alloc, startDate: dispStart, endDate: dispEnd },
      dispStart,
      dispEnd,
    );

    if (segments.length === 0) {
      return [];
    }

    return segments.flatMap((segment, segmentIndex) => {
      const isFirstSegment = segmentIndex === 0;
      const segmentKey = `${alloc.id}-${segmentIndex}`;
      const draggedSegmentActive =
        allocDragState.isActive &&
        allocDragState.allocationId === alloc.id &&
        sameDate(allocDragState.originalStartDate, toUtcDay(segment.start)) &&
        sameDate(allocDragState.originalEndDate, toUtcDay(segment.end));
      const isBeingDragged = isProjectShifted || draggedSegmentActive;
      const isOtherDragged = anyDragActive && !isBeingDragged;

      let segmentLeft = toLeft(segment.start);
      let segmentWidth = Math.max(CELL_WIDTH, toWidth(segment.start, segment.end));
      let dragTransform: string | undefined;

      if (isProjectShifted) {
        const preview = applyPointerOffsetPreviewRect({
          left: segmentLeft,
          width: segmentWidth,
          mode: "move",
          pointerOffsetX: getDragPointerOffset(
            dragState.pointerDeltaX,
            dragState.daysDelta,
            CELL_WIDTH,
          ),
          minWidth: CELL_WIDTH,
        });
        segmentLeft = preview.left;
        segmentWidth = preview.width;
        dragTransform = preview.transform;
      } else if (draggedSegmentActive) {
        const preview = applyPointerOffsetPreviewRect({
          left: segmentLeft,
          width: segmentWidth,
          mode: allocDragState.mode,
          pointerOffsetX: getDragPointerOffset(
            allocDragState.pointerDeltaX,
            allocDragState.daysDelta,
            CELL_WIDTH,
          ),
          minWidth: CELL_WIDTH,
        });
        segmentLeft = preview.left;
        segmentWidth = preview.width;
        dragTransform = preview.transform;
      }

      if (isMultiDragTarget && multiDragMode === "resize-start") {
        segmentLeft += multiDragPx;
        segmentWidth = Math.max(CELL_WIDTH, segmentWidth - multiDragPx);
      } else if (isMultiDragTarget && multiDragMode === "resize-end") {
        segmentWidth = Math.max(CELL_WIDTH, segmentWidth + multiDragPx);
      }

      if (segmentWidth <= 0 || segmentLeft >= totalCanvasWidth) {
        return [];
      }

      // Horizontal virtualization: skip bars entirely outside the visible window
      const effectiveRight = segmentLeft + segmentWidth;
      if (effectiveRight < visibleLeft || segmentLeft > visibleRight) {
        return [];
      }

      const handleWidth = segmentWidth >= 48 ? 10 : 6;
      const dragInset = Math.min(handleWidth, Math.max(2, Math.floor(segmentWidth / 4)));
      const segmentInfo: AllocMouseDownInfo = {
        ...allocInfo,
        startDate: toUtcDay(segment.start),
        endDate: toUtcDay(segment.end),
        scope: "segment",
      };

      return (
        <div
          key={segmentKey}
          data-allocation-id={alloc.id}
          data-timeline-entry-type="allocation"
          data-timeline-drag-preview="project-shift allocation"
          data-timeline-project-id={alloc.projectId}
          data-allocation-segment-index={segmentIndex}
          data-allocation-segment-start={toLocalDateKey(segment.start)}
          data-allocation-segment-end={toLocalDateKey(segment.end)}
          className={clsx(
            "absolute text-white group/block",
            hasRecurrence && "opacity-80",
            isBeingDragged
              ? "opacity-90 z-20"
              : isOtherDragged
                ? "opacity-30 z-[10]"
                : "transition-[opacity] duration-75 z-[10]",
            selectedAllocationSet.has(alloc.id) && "z-20",
            pendingMutationIds?.has(alloc.id) && !isBeingDragged && "animate-pulse",
          )}
          style={{
            left: segmentLeft + 2,
            width: segmentWidth - 4,
            top: blockTop,
            height: blockHeight,
            ...((multiDragPx && multiDragMode === "move") || dragTransform
              ? {
                  transform: [
                    dragTransform,
                    multiDragPx && multiDragMode === "move" ? `translateX(${multiDragPx}px)` : null,
                  ]
                    .filter(Boolean)
                    .join(" "),
                }
              : {}),
          }}
          onMouseDown={(e) => {
            if (e.button === 2) e.stopPropagation();
          }}
          onDoubleClick={(e) => {
            if (suppressHoverInteractions || !onInlineEdit) return;
            e.stopPropagation();
            const toUtcDate = (v: Date | string) => {
              const d = new Date(v);
              return new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
            };
            onInlineEdit(
              alloc.id,
              {
                startDate: toUtcDate(alloc.startDate),
                endDate: toUtcDate(alloc.endDate),
                hoursPerDay: alloc.hoursPerDay,
              },
              e.currentTarget.getBoundingClientRect(),
            );
          }}
          onContextMenu={(e) => {
            e.preventDefault();
            e.stopPropagation();
            if (suppressHoverInteractions) return;
            onAllocationContextMenu(
              {
                allocationId: getPlanningEntryMutationId(alloc),
                projectId: alloc.projectId,
                contextDate: resolveSegmentContextDate(
                  e.clientX,
                  e.currentTarget.getBoundingClientRect(),
                  segment.start,
                  segment.end,
                ),
              },
              e.clientX,
              e.clientY,
            );
          }}
        >
          <div
            data-allocation-handle="start"
            className="absolute inset-y-0 left-0 z-30 flex items-center justify-center cursor-ew-resize rounded-l-md hover:bg-black/15 transition-colors"
            style={{ width: handleWidth }}
            onMouseDown={(e) => {
              e.stopPropagation();
              onAllocMouseDown(e, { ...segmentInfo, mode: "resize-start" });
            }}
            onTouchStart={(e) => {
              e.stopPropagation();
              onAllocTouchStart(e, { ...segmentInfo, mode: "resize-start" });
            }}
          >
            {handleWidth >= 10 && (
              <div className="flex flex-col gap-0.5 opacity-60 group-hover/block:opacity-100">
                <div className="w-px h-2.5 bg-white rounded" />
                <div className="w-px h-2.5 bg-white rounded" />
              </div>
            )}
          </div>

          <div
            aria-hidden="true"
            className={clsx(
              "pointer-events-none absolute inset-0 z-0 rounded-md overflow-hidden",
              hasRecurrence && "border-2 border-dashed border-white/60",
              isBeingDragged
                ? "shadow-2xl ring-2 ring-white ring-offset-1 scale-[1.01]"
                : "hover:ring-2 hover:ring-white hover:ring-offset-1",
              selectedAllocationSet.has(alloc.id) && "ring-2 ring-sky-500 ring-offset-1",
            )}
            style={{
              backgroundColor: blockBgColor,
            }}
          />

          <div
            data-allocation-interaction="body"
            className={clsx(
              "absolute inset-y-0 z-20 flex min-w-0 select-none items-center gap-1 px-1 pointer-events-auto",
              isBeingDragged ? "cursor-grabbing" : "cursor-grab",
            )}
            style={{ left: dragInset, right: dragInset }}
            onMouseDown={(e) => {
              e.stopPropagation();
              onAllocMouseDown(e, segmentInfo);
            }}
            onTouchStart={(e) => {
              e.stopPropagation();
              onAllocTouchStart(e, segmentInfo);
            }}
          >
            {hasRecurrence && segmentWidth > 28 && (
              <span className="text-[10px] opacity-80 flex-shrink-0">↻</span>
            )}
            {isFirstSegment && segmentWidth > 60 ? (
              <span className="text-xs font-semibold truncate">{alloc.project.name}</span>
            ) : isFirstSegment ? (
              <span className="text-[9px] font-bold truncate opacity-90">
                {alloc.project.shortCode}
              </span>
            ) : null}
            {isFirstSegment && segmentWidth > 130 && (
              <span className="text-[10px] opacity-75 truncate">{alloc.role}</span>
            )}
            {isFirstSegment && segmentWidth > 190 && (
              <span className="text-[10px] opacity-60 truncate">{alloc.hoursPerDay}h</span>
            )}
          </div>

          <div
            data-allocation-handle="end"
            className="absolute inset-y-0 right-0 z-30 flex items-center justify-center cursor-ew-resize rounded-r-md hover:bg-black/15 transition-colors"
            style={{ width: handleWidth }}
            onMouseDown={(e) => {
              e.stopPropagation();
              onAllocMouseDown(e, { ...segmentInfo, mode: "resize-end" });
            }}
            onTouchStart={(e) => {
              e.stopPropagation();
              onAllocTouchStart(e, { ...segmentInfo, mode: "resize-end" });
            }}
          >
            {handleWidth >= 10 && (
              <div className="flex flex-col gap-0.5 opacity-60 group-hover/block:opacity-100">
                <div className="w-px h-2.5 bg-white rounded" />
                <div className="w-px h-2.5 bg-white rounded" />
              </div>
            )}
          </div>
        </div>
      );
    });
  });
}

// ─── Strip-mode: daily load graph ────────────────────────────────────────────

export function renderLoadGraph(
  allocs: TimelineAssignmentEntry[],
  dates: Date[],
  CELL_WIDTH: number,
  capacity: ResourceCapacitySeries | undefined,
) {
  const GRAPH_H = 12;

  function hoursOnDay(list: TimelineAssignmentEntry[], t: number) {
    const currentDate = new Date(t);
    return list.reduce((sum, a) => {
      return isAllocationScheduledOnDate(a, currentDate) ? sum + a.hoursPerDay : sum;
    }, 0);
  }

  return (
    <div className="absolute inset-x-0 bottom-1 pointer-events-none" style={{ height: GRAPH_H }}>
      {dates.map((date, i) => {
        const t = date.getTime();
        const bookingFactor = capacity?.bookingFactorsByDay[i] ?? 1;
        const capacityHours = capacity?.capacityHoursByDay[i] ?? 8;
        const totalH = hoursOnDay(allocs, t) * bookingFactor;
        if (totalH === 0) return null;

        const totalBarH =
          capacityHours > 0
            ? Math.min(
                GRAPH_H,
                Math.round((Math.min(totalH, capacityHours) / capacityHours) * GRAPH_H),
              )
            : GRAPH_H;
        const utilizationPct = capacityHours > 0 ? (totalH / capacityHours) * 100 : 100;

        return (
          <div
            key={i}
            className={clsx(
              "absolute bottom-0 rounded-t-sm",
              utilizationPct > 100
                ? "bg-red-500 opacity-80"
                : utilizationPct > 75
                  ? "bg-amber-400 opacity-80"
                  : "bg-brand-500 opacity-80",
            )}
            style={{ left: i * CELL_WIDTH + 3, width: CELL_WIDTH - 6, height: totalBarH }}
          />
        );
      })}
    </div>
  );
}

// ─── Heatmap-mode: utilisation colour overlay ────────────────────────────────

export function renderHeatmapOverlay(
  allocs: TimelineAssignmentEntry[],
  dates: Date[],
  CELL_WIDTH: number,
  heatmapScheme: HeatmapColorScheme,
  capacity: ResourceCapacitySeries | undefined,
) {
  return dates.map((date, i) => {
    const bookingFactor = capacity?.bookingFactorsByDay[i] ?? 1;
    const capacityHours = capacity?.capacityHoursByDay[i] ?? 8;
    const totalH = allocs.reduce((sum, a) => {
      return isAllocationScheduledOnDate(a, date) ? sum + a.hoursPerDay * bookingFactor : sum;
    }, 0);
    const pct = capacityHours > 0 ? (totalH / capacityHours) * 100 : totalH > 0 ? 100 : 0;
    const bg = heatmapBgColor(pct, heatmapScheme);
    if (!bg) return null;
    return (
      <div
        key={`hm-${i}`}
        className="absolute top-0 bottom-0 pointer-events-none z-10"
        style={{ left: i * CELL_WIDTH, width: CELL_WIDTH, backgroundColor: bg }}
      />
    );
  });
}

// ─── Bar-mode: stacked daily bars ────────────────────────────────────────────

export function renderDailyBars(
  allocs: TimelineAssignmentEntry[],
  rowHeight: number,
  CELL_WIDTH: number,
  dates: Date[],
  allocDragState: AllocDragState,
  onAllocMouseDown: (e: React.MouseEvent, info: AllocMouseDownInfo) => void,
  onAllocTouchStart: (e: React.TouchEvent, info: AllocMouseDownInfo) => void,
  onAllocationContextMenu: (
    info: { allocationId: string; projectId: string; contextDate?: Date },
    anchorX: number,
    anchorY: number,
  ) => void,
  toLeft: (d: Date) => number,
  toWidth: (s: Date, e: Date) => number,
  totalCanvasWidth: number,
  capacity: ResourceCapacitySeries | undefined,
  multiSelectState: MultiSelectState,
  suppressHoverInteractions: boolean,
) {
  const BAR_AREA = rowHeight - 8;
  const selectedAllocationSet = new Set(multiSelectState.selectedAllocationIds);

  return dates.flatMap((date, i) => {
    const dayTimestamp = date.getTime();
    const covering = allocs.filter((a) => {
      const isDragged = allocDragState.isActive && allocDragState.allocationId === a.id;
      return isAllocationScheduledOnDate(
        {
          ...a,
          startDate:
            isDragged && allocDragState.currentStartDate
              ? allocDragState.currentStartDate
              : a.startDate,
          endDate:
            isDragged && allocDragState.currentEndDate ? allocDragState.currentEndDate : a.endDate,
        },
        date,
      );
    });

    if (covering.length === 0) return [];

    const bookingFactor = capacity?.bookingFactorsByDay[i] ?? 1;
    const capacityHours = capacity?.capacityHoursByDay[i] ?? 8;
    const totalH = covering.reduce((sum, a) => sum + a.hoursPerDay * bookingFactor, 0);
    const isOver = totalH > capacityHours;
    let stackedH = 0;

    const segs: React.ReactNode[] = covering.map((alloc) => {
      const customColor = (alloc.project as { color?: string | null }).color;
      const projectColor = getProjectColor(alloc.projectId);
      const segBgColor = customColor ?? projectColor.hex + "B3";
      const effectiveHours = alloc.hoursPerDay * bookingFactor;
      if (effectiveHours <= 0 || capacityHours <= 0) {
        return null;
      }
      const segH = Math.max(
        2,
        Math.min(
          BAR_AREA - stackedH,
          Math.round(
            (capacityHours > 0 ? effectiveHours / capacityHours : effectiveHours > 0 ? 1 : 0) *
              BAR_AREA,
          ),
        ),
      );
      const bottom = 4 + stackedH;
      stackedH += segH;
      const isBeingDragged = allocDragState.isActive && allocDragState.allocationId === alloc.id;

      const dispStart = new Date(
        isBeingDragged && allocDragState.currentStartDate
          ? allocDragState.currentStartDate
          : alloc.startDate,
      );
      const dispEnd = new Date(
        isBeingDragged && allocDragState.currentEndDate
          ? allocDragState.currentEndDate
          : alloc.endDate,
      );
      dispStart.setHours(0, 0, 0, 0);
      dispEnd.setHours(0, 0, 0, 0);
      const isFirstDay = dayTimestamp === dispStart.getTime();
      const isLastDay = dayTimestamp === dispEnd.getTime();
      const EDGE_W = CELL_WIDTH >= 16 ? 4 : 0;
      const dragPointerOffset = isBeingDragged
        ? getDragPointerOffset(allocDragState.pointerDeltaX, allocDragState.daysDelta, CELL_WIDTH)
        : 0;

      const allocInfo: AllocMouseDownInfo = {
        mode: "move",
        allocationId: alloc.id,
        mutationAllocationId: getPlanningEntryMutationId(alloc),
        projectId: alloc.projectId,
        projectName: alloc.project.name,
        resourceId: alloc.resourceId,
        startDate: new Date(alloc.startDate),
        endDate: new Date(alloc.endDate),
      };

      return (
        <div
          key={`bar-${i}-${alloc.id}`}
          data-allocation-id={alloc.id}
          data-timeline-entry-type="allocation"
          data-timeline-drag-preview="allocation"
          data-timeline-project-id={alloc.projectId}
          className={clsx(
            "absolute rounded-sm flex items-stretch overflow-hidden",
            isBeingDragged
              ? "opacity-90 ring-2 ring-white ring-offset-1 z-20"
              : "transition-opacity duration-75 hover:opacity-80 z-[10]",
            selectedAllocationSet.has(alloc.id) && "ring-2 ring-sky-500 ring-offset-1 z-20",
          )}
          style={{
            left: i * CELL_WIDTH + 2,
            width: CELL_WIDTH - 4,
            height: segH,
            bottom,
            backgroundColor: segBgColor,
            ...((multiSelectState.isMultiDragging && selectedAllocationSet.has(alloc.id)) ||
            dragPointerOffset
              ? {
                  transform: [
                    dragPointerOffset ? `translateX(${dragPointerOffset}px)` : null,
                    multiSelectState.isMultiDragging && selectedAllocationSet.has(alloc.id)
                      ? `translateX(${multiSelectState.multiDragDaysDelta * CELL_WIDTH}px)`
                      : null,
                  ]
                    .filter(Boolean)
                    .join(" "),
                }
              : {}),
          }}
          onMouseDown={(e) => {
            if (e.button === 2) e.stopPropagation();
          }}
          onContextMenu={(e) => {
            e.preventDefault();
            e.stopPropagation();
            if (suppressHoverInteractions) return;
            onAllocationContextMenu(
              {
                allocationId: getPlanningEntryMutationId(alloc),
                projectId: alloc.projectId,
                contextDate: new Date(
                  Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()),
                ),
              },
              e.clientX,
              e.clientY,
            );
          }}
        >
          {isFirstDay && EDGE_W > 0 && (
            <div
              className="flex-shrink-0 cursor-ew-resize hover:bg-black/20"
              style={{ width: EDGE_W }}
              onMouseDown={(e) => {
                e.stopPropagation();
                onAllocMouseDown(e, { ...allocInfo, mode: "resize-start" });
              }}
              onTouchStart={(e) => {
                e.stopPropagation();
                onAllocTouchStart(e, { ...allocInfo, mode: "resize-start" });
              }}
            />
          )}
          <div
            className={clsx("flex-1 min-w-0", "cursor-grab")}
            onMouseDown={(e) => {
              e.stopPropagation();
              onAllocMouseDown(e, allocInfo);
            }}
            onTouchStart={(e) => {
              e.stopPropagation();
              onAllocTouchStart(e, allocInfo);
            }}
          />
          {isLastDay && EDGE_W > 0 && (
            <div
              className="flex-shrink-0 cursor-ew-resize hover:bg-black/20"
              style={{ width: EDGE_W }}
              onMouseDown={(e) => {
                e.stopPropagation();
                onAllocMouseDown(e, { ...allocInfo, mode: "resize-end" });
              }}
              onTouchStart={(e) => {
                e.stopPropagation();
                onAllocTouchStart(e, { ...allocInfo, mode: "resize-end" });
              }}
            />
          )}
        </div>
      );
    });

    if (isOver) {
      segs.push(
        <div
          key={`bar-${i}-over`}
          className="absolute bg-red-500/70 rounded-t-sm pointer-events-none z-30"
          style={{ left: i * CELL_WIDTH + 2, width: CELL_WIDTH - 4, top: 4, height: 3 }}
        />,
      );
    }

    return segs.filter(Boolean);
  });
}
