import type { MutableRefObject, RefObject } from "react";
import type { TimelineDemandEntry, VacationEntry } from "./TimelineContext.js";
import type { DemandHoverData } from "./TimelineTooltip.js";

export type TooltipPosition = {
  left: number;
  top: number;
};

function toUtcDayTimestamp(value: Date | string): number {
  const date = new Date(value);
  return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
}

export function updateTooltipPosition(
  positionRef: MutableRefObject<TooltipPosition>,
  tooltipRef: RefObject<HTMLDivElement | null>,
  clientX: number,
  clientY: number,
  offsetX: number,
  offsetY: number,
) {
  positionRef.current = { left: clientX + offsetX, top: clientY + offsetY };
  if (!tooltipRef.current) return;

  tooltipRef.current.style.left = `${positionRef.current.left}px`;
  tooltipRef.current.style.top = `${positionRef.current.top}px`;
}

export function cancelHoverFrame(frameRef: MutableRefObject<number | null>) {
  if (frameRef.current === null) return;
  cancelAnimationFrame(frameRef.current);
  frameRef.current = null;
}

export function findVacationHit<T extends { startDate: Date | string; endDate: Date | string }>(
  vacations: T[],
  date: Date,
): T | null {
  const target = toUtcDayTimestamp(date);

  return (
    vacations.find((vacation) => {
      const start = toUtcDayTimestamp(vacation.startDate);
      const end = toUtcDayTimestamp(vacation.endDate);
      return target >= start && target <= end;
    }) ?? null
  );
}

export function collectResourcesWithVacations(
  vacationsByResource: Map<string, VacationEntry[]>,
) {
  const result = new Set<string>();
  for (const [resourceId, vacations] of vacationsByResource) {
    if (vacations.length > 0) {
      result.add(resourceId);
    }
  }
  return result;
}

export function scheduleVacationHoverUpdate<T extends { id: string; startDate: Date | string; endDate: Date | string }>(
  args: {
    frameRef: MutableRefObject<number | null>;
    hoveredKeyRef: MutableRefObject<string | null>;
    resourceId: string;
    clientX: number;
    rect: DOMRect;
    xToDate: (clientX: number, rect: DOMRect) => Date;
    vacations: T[];
    onHoverChange: (vacation: T | null) => void;
  },
) {
  const {
    frameRef,
    hoveredKeyRef,
    resourceId,
    clientX,
    rect,
    xToDate,
    vacations,
    onHoverChange,
  } = args;

  if (frameRef.current !== null) return;

  frameRef.current = requestAnimationFrame(() => {
    frameRef.current = null;
    const date = xToDate(clientX, rect);
    const hit = findVacationHit(vacations, date);
    const nextKey = hit ? `${resourceId}:${hit.id}` : null;
    if (nextKey === hoveredKeyRef.current) return;

    hoveredKeyRef.current = nextKey;
    onHoverChange(hit);
  });
}

export function buildDemandHoverData(demand: TimelineDemandEntry): DemandHoverData {
  const startDate = new Date(demand.startDate);
  const endDate = new Date(demand.endDate);
  const days = Math.max(1, Math.round((endDate.getTime() - startDate.getTime()) / 86_400_000) + 1);

  return {
    roleName: demand.roleEntity?.name ?? demand.role ?? "Open demand",
    roleColor: demand.roleEntity?.color ?? "#f59e0b",
    projectName: demand.project.name,
    projectShortCode: demand.project.shortCode,
    requestedHeadcount: demand.requestedHeadcount,
    unfilledHeadcount: demand.unfilledHeadcount,
    startDate: demand.startDate,
    endDate: demand.endDate,
    hoursPerDay: demand.hoursPerDay,
    totalHours: demand.hoursPerDay * days,
    percentage: demand.percentage,
    status: demand.status,
    ...(demand.dailyCostCents > 0
      ? {
          totalCostCents: demand.dailyCostCents * days,
          dailyCostCents: demand.dailyCostCents,
        }
      : {}),
  };
}
