import { format } from "date-fns";
import { useRef, useState } from "react";

import {
  buildAllocationBarSegment,
  dateStringsFromDayIndices,
  dayIndexFromPointer,
  formatAllocationEffort,
  projectBarStyle,
  roundToHalfHour,
  visibleWeekdayCount,
  type EffortDisplayMode,
} from "../../planner/allocationTimeline";
import type { Allocation, Person } from "../../types";
import { AllocationBarMenuTrigger } from "./AllocationBarMenu";
import { AllocationCreatePopover, type AllocationDragDraft } from "./AllocationCreatePopover";

interface AllocationDragTimelineProps {
  dayDates: Date[];
  color: string;
  fixedPerson?: Person;
  candidatePeople?: Person[];
  transferPeople?: Person[];
  allocation?: Allocation;
  weeklyCapacityHrs?: number;
  allocationPct?: number;
  onSave: (draft: AllocationDragDraft) => Promise<Allocation | undefined> | Allocation | undefined;
  onUpdate?: (draft: AllocationDragDraft) => void;
  onDelete?: (allocation: Allocation) => void;
  onTransfer?: (personId: number) => void;
  onStartClone?: () => void;
  cloneSourceId?: number;
  onSelectAllToRight?: () => void;
  onToggleMultiSelect?: () => void;
  multiSelectActive?: boolean;
  selected?: boolean;
  onToggleSelect?: () => void;
  isSaving?: boolean;
  effortMode?: EffortDisplayMode;
}

export function AllocationDragTimeline({
  dayDates,
  color,
  fixedPerson,
  candidatePeople = [],
  allocation,
  weeklyCapacityHrs,
  allocationPct,
  onSave,
  onUpdate,
  onDelete,
  transferPeople = [],
  onTransfer,
  onStartClone,
  cloneSourceId,
  onSelectAllToRight,
  onToggleMultiSelect,
  multiSelectActive = false,
  selected = false,
  onToggleSelect,
  isSaving = false,
  effortMode = "hours",
}: AllocationDragTimelineProps) {
  const trackRef = useRef<HTMLDivElement>(null);
  const barHostRef = useRef<HTMLDivElement>(null);
  const dragPointerRef = useRef<{ x: number; y: number } | null>(null);
  const [dragRange, setDragRange] = useState<{ start: number; end: number } | null>(null);
  const [popover, setPopover] = useState<{
    startDate: string;
    endDate: string;
    anchor: { top: number; left: number };
    allocation?: Allocation;
  } | null>(null);

  const totalDays = dayDates.length;
  const existingSegment =
    allocation && weeklyCapacityHrs !== undefined && allocationPct !== undefined
      ? buildAllocationBarSegment(dayDates, allocation.start_date, allocation.end_date)
      : null;

  const previewSegment =
    dragRange && totalDays > 0
      ? {
          startIndex: Math.min(dragRange.start, dragRange.end),
          endIndex: Math.max(dragRange.start, dragRange.end),
        }
      : null;

  const openPopoverAt = (startDate: string, endDate: string, clientX: number, clientY: number, edit?: Allocation) => {
    setPopover({
      startDate,
      endDate,
      anchor: { top: clientY + 8, left: clientX - 180 },
      allocation: edit,
    });
  };

  const openEditPopover = (event?: React.MouseEvent<HTMLButtonElement>) => {
    if (!allocation) return;
    event?.stopPropagation();
    if (multiSelectActive && onToggleSelect) {
      onToggleSelect();
      return;
    }
    const rect = event?.currentTarget.getBoundingClientRect() ?? barHostRef.current?.getBoundingClientRect();
    if (!rect) return;
    openPopoverAt(
      allocation.start_date,
      allocation.end_date,
      rect.left + rect.width / 2,
      rect.bottom,
      allocation,
    );
  };

  const onPointerDown = (event: React.PointerEvent<HTMLDivElement>) => {
    if (!trackRef.current || event.button !== 0) return;
    if ((event.target as HTMLElement).closest(".allocation-bar-host, .allocation-bar")) return;
    dragPointerRef.current = { x: event.clientX, y: event.clientY };
    const index = dayIndexFromPointer(event.clientX, trackRef.current.getBoundingClientRect(), totalDays);
    setDragRange({ start: index, end: index });
    trackRef.current.setPointerCapture(event.pointerId);
  };

  const onPointerMove = (event: React.PointerEvent<HTMLDivElement>) => {
    if (!dragRange || !trackRef.current) return;
    const index = dayIndexFromPointer(event.clientX, trackRef.current.getBoundingClientRect(), totalDays);
    setDragRange((prev) => (prev ? { ...prev, end: index } : null));
  };

  const onPointerUp = (event: React.PointerEvent<HTMLDivElement>) => {
    if (!trackRef.current?.hasPointerCapture(event.pointerId)) return;
    trackRef.current.releasePointerCapture(event.pointerId);
    const pointerStart = dragPointerRef.current;
    dragPointerRef.current = null;
    const range = dragRange;
    setDragRange(null);
    if (!range) return;
    const movedPx = pointerStart
      ? Math.hypot(event.clientX - pointerStart.x, event.clientY - pointerStart.y)
      : 0;
    const didDrag = range.start !== range.end || movedPx > 6;
    if (!didDrag) return;
    const { start_date, end_date } = dateStringsFromDayIndices(dayDates, range.start, range.end);
    openPopoverAt(start_date, end_date, event.clientX, event.clientY);
  };

  const pickerPeople = fixedPerson ? [fixedPerson] : candidatePeople;
  const editingAllocation = popover?.allocation;

  return (
    <>
      <div
        className={`planner-timeline planner-drag-timeline${dragRange ? " is-dragging" : ""}`}
        onPointerDown={onPointerDown}
        onPointerMove={onPointerMove}
        onPointerUp={onPointerUp}
        ref={trackRef}
      >
        <div className="planner-day-grid" />
        {dayDates.map((day, index) => (
          <div
            className={`planner-day-hit${format(day, "yyyy-MM-dd") === format(new Date(), "yyyy-MM-dd") ? " is-today" : ""}`}
            key={day.toISOString()}
            style={{
              left: `${(index / totalDays) * 100}%`,
              width: `${(1 / totalDays) * 100}%`,
            }}
          />
        ))}
        {previewSegment ? (
          <div
            className="planner-drag-preview"
            style={{
              ...projectBarStyle(
                {
                  startIndex: previewSegment.startIndex,
                  endIndex: previewSegment.endIndex,
                  className: "project-bar allocation-bar preview",
                  label: "",
                },
                totalDays,
              ),
              background: color,
              borderTopColor: color,
            }}
          />
        ) : null}
        {existingSegment && weeklyCapacityHrs !== undefined && allocationPct !== undefined && allocation ? (
          (() => {
            const weekdayCount = visibleWeekdayCount(
              dayDates,
              allocation.start_date,
              allocation.end_date,
            );
            const daySpan = existingSegment.endIndex - existingSegment.startIndex + 1;
            const widthPct = totalDays > 0 ? (daySpan / totalDays) * 100 : 100;
            const isCompact = weekdayCount <= 1 || widthPct <= 18;
            return (
          <div
            className={`allocation-bar-host project-bar${isCompact ? " is-compact" : " is-span"}${selected ? " is-selected" : ""}${cloneSourceId === allocation.id ? " is-clone-source" : ""}`}
            ref={barHostRef}
            style={{
              ...projectBarStyle(existingSegment, totalDays),
              minWidth: 76,
            }}
          >
            <button
              className="allocation-bar-main project-bar allocation-bar"
              onClick={openEditPopover}
              onPointerDown={(event) => event.stopPropagation()}
              onPointerUp={(event) => event.stopPropagation()}
              style={{
                background: color,
                borderTopColor: color,
              }}
              title={formatAllocationEffort(
                effortMode,
                allocationPct,
                weeklyCapacityHrs,
                weekdayCount,
              )}
              type="button"
            >
              <span className="planner-bar-label">
                {formatAllocationEffort(
                  effortMode,
                  allocationPct,
                  weeklyCapacityHrs,
                  weekdayCount,
                )}
              </span>
            </button>
            <AllocationBarMenuTrigger
              multiSelectActive={multiSelectActive}
              onClone={() => onStartClone?.()}
              onDelete={onDelete ? () => onDelete(allocation) : undefined}
              onEdit={() => openEditPopover()}
              onSelectAllToRight={onSelectAllToRight}
              onToggleMultiSelect={onToggleMultiSelect}
              onTransferPerson={onTransfer}
              transferPeople={transferPeople}
            />
          </div>
            );
          })()
        ) : null}
      </div>

      {popover && pickerPeople.length > 0 ? (
        <AllocationCreatePopover
          key={`${popover.startDate}-${popover.endDate}-${editingAllocation?.id ?? "new"}`}
          allocation={editingAllocation}
          anchor={popover.anchor}
          dayDates={dayDates}
          endDate={popover.endDate}
          fixedPerson={fixedPerson}
          isSaving={isSaving}
          onCancel={() => setPopover(null)}
          onDelete={
            editingAllocation && onDelete
              ? () => {
                  onDelete(editingAllocation);
                  setPopover(null);
                }
              : undefined
          }
          onSave={async (draft) => {
            const created = await onSave(draft);
            if (created) {
              setPopover({
                startDate: created.start_date,
                endDate: created.end_date,
                anchor: popover.anchor,
                allocation: created,
              });
              return;
            }
            setPopover(null);
          }}
          onUpdate={
            editingAllocation && onUpdate
              ? (draft) => {
                  onUpdate(draft);
                  setPopover(null);
                }
              : undefined
          }
          people={pickerPeople}
          startDate={popover.startDate}
          defaultHoursPerDay={
            fixedPerson ? roundToHalfHour(fixedPerson.weekly_capacity_hrs / 5) : undefined
          }
          multiSelectActive={multiSelectActive}
          onClone={
            editingAllocation && onStartClone
              ? () => {
                  onStartClone();
                  setPopover(null);
                }
              : undefined
          }
          onSelectAllToRight={
            editingAllocation && onSelectAllToRight
              ? () => {
                  onSelectAllToRight();
                  setPopover(null);
                }
              : undefined
          }
          onToggleMultiSelect={
            onToggleMultiSelect
              ? () => {
                  onToggleMultiSelect();
                  setPopover(null);
                }
              : undefined
          }
          onTransfer={
            editingAllocation && onTransfer
              ? (personId) => {
                  onTransfer(personId);
                  setPopover(null);
                }
              : undefined
          }
          transferPeople={transferPeople}
        />
      ) : null}
    </>
  );
}
