import { clsx } from "clsx";
import type { TimelineAssignmentEntry, TimelineDemandEntry } from "./TimelineContext.js";
import { applyPointerOffsetPreviewRect, getDragPointerOffset } from "./allocationVisualState.js";
import { heatmapColor } from "./heatmapUtils.js";
import { getPlanningEntryMutationId } from "~/lib/planningEntryIds.js";
import { ROW_HEIGHT, SUB_LANE_HEIGHT, LABEL_WIDTH } from "./timelineConstants.js";
import type { AllocDragState, MultiSelectState } from "~/hooks/useTimelineDrag.js";
import type { AllocMouseDownInfo } from "./TimelineResourcePanel.js";
import type { OpenDemandRowLayout } from "./timelineProjectRows.js";
import type { ProjectDayMetric } from "./timelineProjectMetrics.js";

const SVG_XMLNS = "http://www.w3.org/2000/svg";

// ─── Open-demand row ───────────────────────────────────────────────────────

export function renderOpenDemandRow(
  openDemandCount: number,
  layout: OpenDemandRowLayout,
  projectId: string,
  CELL_WIDTH: number,
  totalCanvasWidth: number,
  toLeft: (d: Date) => number,
  toWidth: (s: Date, e: Date) => number,
  rowGridLines: React.ReactNode,
  onOpenDemandClick: (demand: TimelineDemandEntry, anchorX: number, anchorY: number) => void,
  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,
  onDemandHoverMove: (e: React.MouseEvent, demand: TimelineDemandEntry) => void,
  onClearHoverTooltips: () => void,
  multiSelectState: MultiSelectState,
  allocDragState: AllocDragState,
  suppressHoverInteractions: boolean,
) {
  const selectedAllocationSet = new Set(multiSelectState.selectedAllocationIds);
  const { visibleOpenDemands, laneMap, rowHeight } = layout;
  if (visibleOpenDemands.length === 0) return null;

  return (
    <div
      data-project-demand-row="true"
      data-project-id={projectId}
      className="group relative isolate flex border-b border-dashed border-amber-200 bg-amber-50 dark:border-amber-800 dark:bg-[rgb(var(--surface-card))] hover:bg-amber-100/80 dark:hover:bg-[rgb(var(--surface-elevated))]"
      style={{ height: rowHeight }}
    >
      <div
        className="sticky left-0 z-30 flex flex-shrink-0 items-center gap-2 border-r border-amber-200 bg-amber-50 pl-8 pr-4 dark:border-amber-800 dark:bg-[rgb(var(--surface-card))]"
        style={{ width: LABEL_WIDTH, height: rowHeight }}
      >
        <div className="pointer-events-none absolute inset-0 bg-amber-50 dark:bg-[rgb(var(--surface-card))]" />
        <div className="relative z-10 flex items-center gap-2 min-w-0">
          <div className="w-6 h-6 rounded-full bg-amber-100 dark:bg-amber-900/50 flex items-center justify-center text-[10px] font-bold text-amber-600 dark:text-amber-400 flex-shrink-0 border border-dashed border-amber-400 dark:border-amber-600">
            ?
          </div>
          <div className="min-w-0">
            <div className="text-xs font-medium text-amber-700 dark:text-amber-400 truncate">
              Open demand
            </div>
            <div className="text-[10px] text-amber-500 dark:text-amber-600 truncate">
              {openDemandCount} open demand{openDemandCount > 1 ? "s" : ""}
            </div>
          </div>
        </div>
      </div>

      <div
        className="relative overflow-hidden bg-amber-50 touch-none dark:bg-[rgb(var(--surface-card))]"
        style={{ width: totalCanvasWidth, height: rowHeight }}
        onMouseLeave={onClearHoverTooltips}
      >
        {rowGridLines}
        <div className="pointer-events-none absolute inset-x-0 inset-y-0 border-y border-dashed border-amber-200/70 dark:border-amber-800/80" />
        <div className="pointer-events-none absolute inset-x-0 inset-y-1 rounded-md bg-amber-100/25 dark:bg-amber-950/35" />
        {visibleOpenDemands.map((alloc) => {
          const allocStart = new Date(alloc.startDate);
          const allocEnd = new Date(alloc.endDate);

          const isAllocDragged =
            allocDragState.isActive && allocDragState.allocationId === alloc.id;
          const dispStart =
            isAllocDragged && allocDragState.currentStartDate
              ? allocDragState.currentStartDate
              : allocStart;
          const dispEnd =
            isAllocDragged && allocDragState.currentEndDate
              ? allocDragState.currentEndDate
              : allocEnd;

          // Multi-drag visual offset
          const isMultiDragTarget =
            multiSelectState.isMultiDragging && selectedAllocationSet.has(alloc.id);
          const multiDragPx = isMultiDragTarget
            ? multiSelectState.multiDragDaysDelta * CELL_WIDTH
            : 0;
          const multiDragMode = multiSelectState.multiDragMode;

          let left = toLeft(dispStart);
          let width = Math.max(CELL_WIDTH, toWidth(dispStart, dispEnd));
          let dragTransform: string | undefined;

          if (isAllocDragged) {
            const preview = applyPointerOffsetPreviewRect({
              left,
              width,
              mode: allocDragState.mode,
              pointerOffsetX: getDragPointerOffset(
                allocDragState.pointerDeltaX,
                allocDragState.daysDelta,
                CELL_WIDTH,
              ),
              minWidth: CELL_WIDTH,
            });
            left = preview.left;
            width = preview.width;
            dragTransform = preview.transform;
          }

          // Clamp negative left (bar starts before view) to avoid extending outside canvas.
          if (left < 0) {
            width += left;
            left = 0;
          }
          if (width <= 0 || left >= totalCanvasWidth) return null;

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

          const roleEntity = (
            alloc as { roleEntity?: { id: string; name: string; color: string | null } | null }
          ).roleEntity;
          const roleName =
            roleEntity?.name ?? (alloc as { role?: string | null }).role ?? "Open demand";
          const roleColor = roleEntity?.color ?? "#f59e0b";
          const headcount = (alloc as { headcount?: number }).headcount ?? 1;
          const lane = laneMap.get(alloc.id) ?? 0;
          const top = 8 + lane * SUB_LANE_HEIGHT;
          const blockHeight = SUB_LANE_HEIGHT - 8;

          const HANDLE_W = width >= 48 ? 8 : 6;

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

          return (
            <div
              key={alloc.id}
              data-allocation-id={alloc.id}
              data-timeline-entry-type="demand"
              data-timeline-drag-preview="project-shift allocation"
              data-timeline-project-id={alloc.projectId}
              className={clsx(
                "absolute rounded-md flex items-stretch overflow-hidden z-[10] group/demand",
                isAllocDragged
                  ? "ring-2 ring-amber-500 z-20"
                  : "hover:ring-2 hover:ring-amber-400 hover:ring-offset-1",
                selectedAllocationSet.has(alloc.id) && "ring-2 ring-sky-500 ring-offset-1 z-20",
              )}
              style={{
                left: left + 2,
                width: width - 4,
                top,
                height: blockHeight,
                backgroundColor: `${roleColor}4D`,
                border: `2px dashed ${roleColor}B3`,
                ...((multiDragPx && multiDragMode === "move") || dragTransform
                  ? {
                      transform: [
                        dragTransform,
                        multiDragPx && multiDragMode === "move"
                          ? `translateX(${multiDragPx}px)`
                          : null,
                      ]
                        .filter(Boolean)
                        .join(" "),
                    }
                  : {}),
              }}
              onMouseDown={(e) => {
                if (e.button === 2) e.stopPropagation();
              }}
              onContextMenu={(e) => {
                e.preventDefault();
                e.stopPropagation();
                if (suppressHoverInteractions) return;
                onAllocationContextMenu(
                  { allocationId: alloc.id, projectId: alloc.projectId },
                  e.clientX,
                  e.clientY,
                );
              }}
              onMouseMove={(e) => {
                if (suppressHoverInteractions) return;
                onDemandHoverMove(e, alloc);
              }}
              onClick={(e) => {
                e.stopPropagation();
                if (suppressHoverInteractions) return;
                onOpenDemandClick(alloc, e.clientX, e.clientY);
              }}
              onKeyDown={(e) => {
                if (e.key !== "Enter" && e.key !== " ") {
                  return;
                }
                e.preventDefault();
                e.stopPropagation();
                if (suppressHoverInteractions) return;
                const rect = e.currentTarget.getBoundingClientRect();
                onOpenDemandClick(alloc, rect.left + rect.width / 2, rect.top + rect.height / 2);
              }}
              role="button"
              tabIndex={0}
              aria-label={`Open demand details for ${roleName} on ${alloc.project.name}`}
            >
              {/* Left resize handle */}
              <div
                className="flex-shrink-0 cursor-ew-resize hover:bg-black/10"
                style={{ width: HANDLE_W }}
                onMouseDown={(e) => onAllocMouseDown(e, { ...allocInfo, mode: "resize-start" })}
                onTouchStart={(e) => {
                  e.stopPropagation();
                  onAllocTouchStart(e, { ...allocInfo, mode: "resize-start" });
                }}
              />

              {/* Center — move + click */}
              <div
                className={clsx(
                  "flex-1 min-w-0 flex items-center px-1 gap-1",
                  isAllocDragged ? "cursor-grabbing" : "cursor-grab",
                )}
                onMouseDown={(e) => {
                  e.stopPropagation();
                  onAllocMouseDown(e, allocInfo);
                }}
                onTouchStart={(e) => {
                  e.stopPropagation();
                  onAllocTouchStart(e, allocInfo);
                }}
              >
                <span className="text-xs font-medium truncate" style={{ color: roleColor }}>
                  {roleName}
                  {headcount > 1 ? ` x${headcount}` : ""}
                </span>
              </div>

              {/* Right resize handle */}
              <div
                className="flex-shrink-0 cursor-ew-resize hover:bg-black/10"
                style={{ width: HANDLE_W }}
                onMouseDown={(e) => onAllocMouseDown(e, { ...allocInfo, mode: "resize-end" })}
                onTouchStart={(e) => {
                  e.stopPropagation();
                  onAllocTouchStart(e, { ...allocInfo, mode: "resize-end" });
                }}
              />
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ─── Project-view: per-resource utilisation band ────────────────────────────

export function renderProjectUtilOverlay(
  dayMetrics: ProjectDayMetric[],
  CELL_WIDTH: number,
  displayMode: string,
  heatmapScheme: string,
  totalCanvasWidth: number,
) {
  if (dayMetrics.length === 0 || totalCanvasWidth <= 0) return null;

  const BAND_H = 7;
  const BAR_H = ROW_HEIGHT - BAND_H - 11;
  const useHeatmapColors = displayMode === "bar";
  const svgParts: string[] = [
    `<svg xmlns="${SVG_XMLNS}" width="${totalCanvasWidth}" height="${ROW_HEIGHT}" viewBox="0 0 ${totalCanvasWidth} ${ROW_HEIGHT}" preserveAspectRatio="none" shape-rendering="crispEdges">`,
  ];

  dayMetrics.forEach(({ projH, totalH, capacityH }, i) => {
    if ((totalH === 0 && projH === 0) || capacityH <= 0) return;

    const isOver = totalH > capacityH;
    const totalBarH = Math.max(
      projH > 0 ? 2 : 0,
      Math.round((Math.min(totalH, capacityH) / capacityH) * BAR_H),
    );
    const projBarH =
      projH > 0 ? Math.min(totalBarH, Math.max(2, Math.round((projH / capacityH) * BAR_H))) : 0;
    const otherBarH = totalBarH - projBarH;
    const projPct = (projH / capacityH) * 100;
    const totalPct = (totalH / capacityH) * 100;
    const projColor = useHeatmapColors
      ? (heatmapColor(
          projPct,
          heatmapScheme as import("~/hooks/useAppPreferences.js").HeatmapColorScheme,
          "bar",
        ) ?? "rgba(59,130,246,0.8)")
      : "rgba(96,165,250,0.8)";
    const totalColor = useHeatmapColors
      ? (heatmapColor(
          totalPct,
          heatmapScheme as import("~/hooks/useAppPreferences.js").HeatmapColorScheme,
          "bar",
        ) ?? "rgba(156,163,175,0.5)")
      : isOver
        ? "rgba(252,211,77,0.8)"
        : "rgba(209,213,219,0.8)";
    const xBand = i * CELL_WIDTH + 1;
    const xBar = i * CELL_WIDTH + 3;
    const bandWidth = Math.max(CELL_WIDTH - 2, 0);
    const barWidth = Math.max(CELL_WIDTH - 6, 0);

    if (projH > 0 && bandWidth > 0) {
      svgParts.push(
        `<rect x="${xBand}" y="6" width="${bandWidth}" height="${BAND_H}" fill="${projColor}" />`,
      );
    }

    if (otherBarH > 0 && barWidth > 0) {
      svgParts.push(
        `<rect x="${xBar}" y="${ROW_HEIGHT - 4 - projBarH - otherBarH}" width="${barWidth}" height="${otherBarH}" fill="${totalColor}" />`,
      );
    }

    if (projBarH > 0 && barWidth > 0) {
      svgParts.push(
        `<rect x="${xBar}" y="${ROW_HEIGHT - 4 - projBarH}" width="${barWidth}" height="${projBarH}" fill="${projColor}" />`,
      );
    }

    if (isOver && totalBarH > 0 && barWidth > 0) {
      svgParts.push(
        `<rect x="${xBar}" y="${ROW_HEIGHT - 4 - totalBarH - 3}" width="${barWidth}" height="3" fill="rgb(239,68,68)" />`,
      );
    }
  });

  svgParts.push("</svg>");
  const svgDataUri = `url("data:image/svg+xml;utf8,${encodeURIComponent(svgParts.join(""))}")`;

  return (
    <div
      data-project-util-bar="true"
      className="absolute inset-0 pointer-events-none"
      style={{
        backgroundImage: svgDataUri,
        backgroundPosition: "0 0",
        backgroundRepeat: "no-repeat",
      }}
    />
  );
}

// ─── Project-view: transparent drag handles ─────────────────────────────────

export function renderProjectDragHandles(
  allocs: TimelineAssignmentEntry[],
  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,
) {
  const selectedAllocationSet = new Set(multiSelectState.selectedAllocationIds);
  return allocs.map((alloc) => {
    const allocStart = new Date(alloc.startDate);
    const allocEnd = new Date(alloc.endDate);

    const isAllocDragged = allocDragState.isActive && allocDragState.allocationId === alloc.id;
    const dispStart =
      isAllocDragged && allocDragState.currentStartDate
        ? allocDragState.currentStartDate
        : allocStart;
    const dispEnd =
      isAllocDragged && allocDragState.currentEndDate ? allocDragState.currentEndDate : allocEnd;

    let left = toLeft(dispStart);
    let width = Math.max(CELL_WIDTH, toWidth(dispStart, dispEnd));
    let dragTransform: string | undefined;

    if (isAllocDragged) {
      const preview = applyPointerOffsetPreviewRect({
        left,
        width,
        mode: allocDragState.mode,
        pointerOffsetX: getDragPointerOffset(
          allocDragState.pointerDeltaX,
          allocDragState.daysDelta,
          CELL_WIDTH,
        ),
        minWidth: CELL_WIDTH,
      });
      left = preview.left;
      width = preview.width;
      dragTransform = preview.transform;
    }
    if (width <= 0 || left >= totalCanvasWidth) return null;

    // Multi-drag visual offset
    const isMultiDragTarget =
      multiSelectState.isMultiDragging && selectedAllocationSet.has(alloc.id);
    const multiDragPx = isMultiDragTarget ? multiSelectState.multiDragDaysDelta * CELL_WIDTH : 0;
    const multiDragMode = multiSelectState.multiDragMode;

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

    // Always show resize handles — for narrow bars, use overlapping handles
    const HANDLE_W = width >= 48 ? 8 : 6;
    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,
    };

    return (
      <div
        key={`dh-${alloc.id}`}
        data-allocation-id={alloc.id}
        data-timeline-entry-type="allocation"
        data-timeline-drag-preview="project-shift allocation"
        data-timeline-project-id={alloc.projectId}
        className={clsx(
          "absolute flex items-stretch rounded",
          hasRecurrence && "border-2 border-dashed border-brand-400/60",
          isAllocDragged
            ? "ring-2 ring-brand-400 z-20"
            : "hover:ring-1 hover:ring-brand-300/70 z-[15]",
          selectedAllocationSet.has(alloc.id) && "ring-2 ring-sky-500 ring-offset-1 z-20",
        )}
        style={{
          left: left + 2,
          width: width - 4,
          top: 2,
          bottom: 2,
          ...((multiDragPx && multiDragMode === "move") || dragTransform
            ? {
                transform: [
                  dragTransform,
                  multiDragPx && multiDragMode === "move" ? `translateX(${multiDragPx}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,
            },
            e.clientX,
            e.clientY,
          );
        }}
      >
        <div
          className="flex-shrink-0 cursor-ew-resize"
          style={{ width: HANDLE_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 flex items-center",
            isAllocDragged ? "cursor-grabbing" : "cursor-grab",
          )}
          onMouseDown={(e) => {
            e.stopPropagation();
            onAllocMouseDown(e, allocInfo);
          }}
          onTouchStart={(e) => {
            e.stopPropagation();
            onAllocTouchStart(e, allocInfo);
          }}
        >
          {hasRecurrence && width > 28 && (
            <span className="text-[10px] text-brand-600 opacity-70 pointer-events-none pl-1">
              ↻
            </span>
          )}
        </div>
        <div
          className="flex-shrink-0 cursor-ew-resize"
          style={{ width: HANDLE_W }}
          onMouseDown={(e) => {
            e.stopPropagation();
            onAllocMouseDown(e, { ...allocInfo, mode: "resize-end" });
          }}
          onTouchStart={(e) => {
            e.stopPropagation();
            onAllocTouchStart(e, { ...allocInfo, mode: "resize-end" });
          }}
        />
      </div>
    );
  });
}
