/**
 * Shared pure render functions used by both TimelineResourcePanel and TimelineProjectPanel.
 * Extracted to avoid duplication of identical vacation blocks, range overlay, and overbooking blink logic.
 */

import React from "react";
import { clsx } from "clsx";
import {
  VACATION_TIMELINE_COLORS,
  VACATION_TIMELINE_BORDER,
  VACATION_TYPE_LABELS_SHORT,
} from "~/lib/status-styles.js";
import type { RangeState } from "~/hooks/useTimelineDrag.js";
import type { TimelineAssignmentEntry } from "./TimelineContext.js";
import type { VacationEntry } from "./TimelineContext.js";

// ─── Shared types ─────────────────────────────────────────────────────────────

export interface VacationBlockInfo {
  vacation: VacationEntry;
  left: number;
  width: number;
}

export function buildVacationBlocksByResource(
  vacationsByResource: Map<string, VacationEntry[]>,
  showVacations: boolean,
  toLeft: (date: Date) => number,
  toWidth: (start: Date, end: Date) => number,
  cellWidth: number,
  totalCanvasWidth: number,
  showWeekends = true,
) {
  if (!showVacations) return new Map<string, VacationBlockInfo[]>();

  const result = new Map<string, VacationBlockInfo[]>();
  for (const [resourceId, vacations] of vacationsByResource) {
    const blocks: VacationBlockInfo[] = [];
    for (const vacation of vacations) {
      const start = new Date(vacation.startDate);
      const end = new Date(vacation.endDate);
      // When weekends are hidden, skip single-day entries that fall on a weekend
      // (e.g. Easter Sunday). They would otherwise render at Monday's column,
      // shadowing Monday's own holiday (e.g. Easter Monday).
      if (!showWeekends && start.toDateString() === end.toDateString()) {
        const dow = start.getDay();
        if (dow === 0 || dow === 6) continue;
      }
      const left = toLeft(start);
      const width = Math.max(cellWidth, toWidth(start, end));
      if (width <= 0 || left >= totalCanvasWidth) continue;
      blocks.push({ vacation, left, width });
    }
    if (blocks.length > 0) {
      result.set(resourceId, blocks);
    }
  }
  return result;
}

// ─── Vacation block overlays ─────────────────────────────────────────────────

export function renderVacationBlocks(blocks: VacationBlockInfo[], rowHeight: number) {
  if (blocks.length === 0) return null;

  return blocks.map(({ vacation: v, left, width }) => {
    const colorClass = VACATION_TIMELINE_COLORS[v.type] ?? "bg-orange-400/40";
    const borderClass = VACATION_TIMELINE_BORDER[v.type] ?? "border-orange-500";
    const label = VACATION_TYPE_LABELS_SHORT[v.type] ?? v.type;
    const isPending = v.status === "PENDING";

    return (
      <div
        key={`vac-${v.id}`}
        data-testid="timeline-vacation-block"
        data-vacation-id={v.id}
        data-vacation-type={v.type}
        data-vacation-status={v.status}
        data-vacation-note={v.note ?? ""}
        className={clsx(
          "absolute z-[5] flex items-end px-1 pb-0.5 overflow-hidden pointer-events-none",
          colorClass,
          isPending ? "border-t-2 border-dashed opacity-60" : "border-t-2",
          isPending ? "" : borderClass,
        )}
        style={{ left: left + 1, width: width - 2, top: 0, height: rowHeight }}
      >
        {width > 40 && (
          <span className="text-[9px] font-bold truncate opacity-70 text-gray-700 dark:text-gray-200 pointer-events-none">
            {isPending ? "\u23F3" : "\uD83C\uDFD6"} {label}
          </span>
        )}
      </div>
    );
  });
}

// ─── Range selection overlay ─────────────────────────────────────────────────

export function renderRangeOverlay(
  rangeState: RangeState,
  resourceId: string,
  rowHeight: number,
  toLeft: (d: Date) => number,
  toWidth: (s: Date, e: Date) => number,
  CELL_WIDTH: number,
) {
  if (!rangeState.isSelecting || rangeState.resourceId !== resourceId || !rangeState.startDate) {
    return null;
  }
  const end = rangeState.currentDate ?? rangeState.startDate;
  const [selStart, selEnd] =
    rangeState.startDate <= end ? [rangeState.startDate, end] : [end, rangeState.startDate];

  const left = toLeft(selStart);
  const width = Math.max(CELL_WIDTH, toWidth(selStart, selEnd));

  return (
    <div
      className="absolute bg-brand-200/40 border-2 border-brand-400 rounded pointer-events-none z-10"
      style={{ left, width, top: 4, height: rowHeight - 8 }}
    />
  );
}

// ─── Overbooking blink overlay ───────────────────────────────────────────────

export function renderOverbookingBlink(
  allocs: TimelineAssignmentEntry[],
  dates: Date[],
  CELL_WIDTH: number,
  capacityHoursByDay?: number[],
  bookingFactorsByDay?: number[],
) {
  const overbooked: number[] = [];

  const allocRanges = allocs.map((a) => {
    const s = new Date(a.startDate);
    s.setHours(0, 0, 0, 0);
    const e = new Date(a.endDate);
    e.setHours(0, 0, 0, 0);
    return { s: s.getTime(), e: e.getTime(), h: a.hoursPerDay };
  });

  for (let i = 0; i < dates.length; i++) {
    const d = new Date(dates[i]!);
    d.setHours(0, 0, 0, 0);
    const t = d.getTime();
    let totalH = 0;
    const factor = bookingFactorsByDay?.[i] ?? 1;
    for (const { s, e, h } of allocRanges) {
      if (t >= s && t <= e) {
        totalH += h * factor;
      }
    }
    if (totalH > (capacityHoursByDay?.[i] ?? 8)) overbooked.push(i);
  }

  if (overbooked.length === 0) return null;

  return overbooked.map((i) => (
    <div
      key={`ob-${i}`}
      className="absolute top-0 bottom-0 pointer-events-none z-[15] animate-overbooking-blink"
      style={{
        left: i * CELL_WIDTH,
        width: CELL_WIDTH,
      }}
    />
  ));
}
