"use client";

import { startTransition, useDeferredValue, useEffect, useMemo, useState } from "react";
import { CalendarClock } from "lucide-react";

import type { BookingOperationsPlan, EquipmentItem, TimelineResponse } from "@rental/shared";
import { addDays, bookingIntervalsOverlap, dayDiffInclusive, shiftBookingOperationsPlan, type RentalBooking } from "@rental/shared";

import {
  createBookingsRequest,
  deleteBookingRequest,
  detachBookingRequest,
  getItemsRequest,
  getTimelineRequest,
  updateBookingScopeRequest,
  updateBookingRequest
} from "../../lib/api";
import { cn } from "../../lib/cn";
import { bookingUsesManualTotalPrice } from "../../lib/booking-pricing";
import { sortTimelineGroupsByPreference } from "../../lib/user-ordering";
import { useSessionContext } from "../layout/session-provider";
import { useUserPreferences } from "../layout/user-preferences-provider";
import { useRuntimeUiConfig } from "../layout/runtime-ui-config-provider";
import { Button } from "../ui/button";
import { Card } from "../ui/card";
import { useViewSettings } from "../layout/view-settings-provider";
import { useIsMobile } from "../../lib/use-is-mobile";
import { BookingAssignmentModal } from "./booking-assignment-modal";
import { BookingDetailsModal } from "./booking-details-modal";
import { TimelineGrid } from "./timeline-grid";
import { TimelineDateActions, TimelineToolbar } from "./timeline-toolbar";
import type { TimelineStatusFilter } from "./timeline-ui-config";

type SelectionPayload = {
  equipmentItemIds: string[];
  startDate: string;
  endDate: string;
};

type BookingWithContext = RentalBooking & {
  itemName: string;
  categoryName: string;
};

type SelectedBookingState = BookingWithContext & {
  sourceBookingIds: string[];
};

type BookingCopyClipboard = {
  booking: BookingWithContext;
  scopeBookings: BookingWithContext[];
  sourceBookingIds: string[];
};

const compareBookingsForRender = (left: RentalBooking, right: RentalBooking) =>
  left.startDate.localeCompare(right.startDate) ||
  left.endDate.localeCompare(right.endDate) ||
  left.customerName.localeCompare(right.customerName) ||
  left.id.localeCompare(right.id);

const getBookingRelationSignature = (booking: RentalBooking) => {
  if (booking.relationGroupKey?.trim()) {
    return [
      "relation-group",
      booking.relationGroupKey.trim().toLowerCase(),
      booking.startDate,
      booking.endDate,
      booking.startTime ?? "",
      booking.endTime ?? ""
    ].join("::");
  }

  const normalizedProjectName = booking.projectName?.trim().toLowerCase() ?? "";
  const normalizedProjectNumber = booking.projectNumber?.trim().toLowerCase() ?? "";

  if (normalizedProjectNumber) {
    return [
      "project-number",
      normalizedProjectNumber,
      booking.startDate,
      booking.endDate,
      booking.startTime ?? "",
      booking.endTime ?? ""
    ].join("::");
  }

  if (normalizedProjectName) {
    return [
      "project-name",
      normalizedProjectName,
      booking.startDate,
      booking.endDate,
      booking.startTime ?? "",
      booking.endTime ?? ""
    ].join("::");
  }

  return `booking::${booking.id}`;
};

const getContiguousRelationBookingIds = (
  anchorBooking: BookingWithContext,
  allBookings: BookingWithContext[],
  visibleItemOrder: Map<string, number>
) => {
  const anchorPosition = visibleItemOrder.get(anchorBooking.equipmentItemId);

  if (anchorPosition === undefined) {
    return [anchorBooking.id];
  }

  const relationSignature = getBookingRelationSignature(anchorBooking);
  const bookingByRowIndex = new Map<number, BookingWithContext>();

  for (const booking of allBookings) {
    if (getBookingRelationSignature(booking) !== relationSignature) {
      continue;
    }

    const rowIndex = visibleItemOrder.get(booking.equipmentItemId);

    if (rowIndex === undefined || bookingByRowIndex.has(rowIndex)) {
      continue;
    }

    bookingByRowIndex.set(rowIndex, booking);
  }

  const contiguousRowIndexes = [anchorPosition];

  for (let nextRowIndex = anchorPosition - 1; bookingByRowIndex.has(nextRowIndex); nextRowIndex -= 1) {
    contiguousRowIndexes.unshift(nextRowIndex);
  }

  for (let nextRowIndex = anchorPosition + 1; bookingByRowIndex.has(nextRowIndex); nextRowIndex += 1) {
    contiguousRowIndexes.push(nextRowIndex);
  }

  const contiguousBookingIds = contiguousRowIndexes
    .map((rowIndex) => bookingByRowIndex.get(rowIndex)?.id)
    .filter((bookingId): bookingId is string => Boolean(bookingId));

  return contiguousBookingIds.length > 0 ? contiguousBookingIds : [anchorBooking.id];
};

const bookingBelongsToProjectScope = (candidate: RentalBooking, anchor: RentalBooking) =>
  anchor.relationGroupKey
    ? candidate.relationGroupKey === anchor.relationGroupKey &&
      candidate.startDate === anchor.startDate &&
      candidate.endDate === anchor.endDate &&
      (candidate.startTime ?? null) === (anchor.startTime ?? null) &&
      (candidate.endTime ?? null) === (anchor.endTime ?? null)
    : anchor.projectNumber
      ? candidate.projectNumber === anchor.projectNumber &&
        candidate.startDate === anchor.startDate &&
        candidate.endDate === anchor.endDate &&
        (candidate.startTime ?? null) === (anchor.startTime ?? null) &&
        (candidate.endTime ?? null) === (anchor.endTime ?? null)
    : candidate.id === anchor.id;

const bookingMatchesScopeSignature = (
  candidate: RentalBooking,
  payload: {
    equipmentItemIds: string[];
    scopeBookingIds?: string[];
    startDate?: string;
    endDate?: string;
  },
  anchor: RentalBooking
) => {
  if (payload.scopeBookingIds?.includes(candidate.id)) {
    return true;
  }

  if (anchor.relationGroupKey && candidate.relationGroupKey === anchor.relationGroupKey) {
    return payload.equipmentItemIds.includes(candidate.equipmentItemId);
  }

  if (!anchor.projectNumber) {
    return false;
  }

  const targetStartDate = payload.startDate ?? anchor.startDate;
  const targetEndDate = payload.endDate ?? anchor.endDate;

  return (
    candidate.projectNumber === anchor.projectNumber &&
    payload.equipmentItemIds.includes(candidate.equipmentItemId) &&
    candidate.startDate === targetStartDate &&
    candidate.endDate === targetEndDate &&
    (candidate.startTime ?? null) === (anchor.startTime ?? null) &&
    (candidate.endTime ?? null) === (anchor.endTime ?? null)
  );
};

const shiftOperationsPlanForPaste = (
  operationsPlan: BookingOperationsPlan | null | undefined,
  sourceStartDate: string,
  sourceEndDate: string,
  nextStartDate: string,
  nextEndDate: string,
  sourceStartTime?: string | null,
  sourceEndTime?: string | null
) => {
  return shiftBookingOperationsPlan(
    operationsPlan,
    {
      startDate: sourceStartDate,
      endDate: sourceEndDate,
      startTime: sourceStartTime,
      endTime: sourceEndTime
    },
    {
      startDate: nextStartDate,
      endDate: nextEndDate,
      startTime: sourceStartTime,
      endTime: sourceEndTime
    }
  );
};

const buildOptimisticScopedBookings = (
  currentScopeBookings: RentalBooking[],
  anchorBooking: RentalBooking,
  payload: {
    equipmentItemIds: string[];
    startDate?: string;
    endDate?: string;
  }
) => {
  const uniqueEquipmentItemIds = Array.from(new Set(payload.equipmentItemIds));
  const prioritizedScopeBookings = [
    ...currentScopeBookings.filter((booking) => booking.id === anchorBooking.id),
    ...currentScopeBookings.filter((booking) => booking.id !== anchorBooking.id)
  ];
  const existingByEquipmentItemId = new Map<string, RentalBooking>();

  for (const scopeBooking of prioritizedScopeBookings) {
    if (!existingByEquipmentItemId.has(scopeBooking.equipmentItemId)) {
      existingByEquipmentItemId.set(scopeBooking.equipmentItemId, scopeBooking);
    }
  }

  return uniqueEquipmentItemIds.map((equipmentItemId) => {
    const existingBooking = existingByEquipmentItemId.get(equipmentItemId);

    return {
      ...(existingBooking ?? anchorBooking),
      id: existingBooking?.id ?? `optimistic-scope:${anchorBooking.id}:${equipmentItemId}`,
      equipmentItemId,
      startDate: payload.startDate ?? anchorBooking.startDate,
      endDate: payload.endDate ?? anchorBooking.endDate,
      updatedAt: new Date().toISOString()
    } satisfies RentalBooking;
  });
};

const applyOptimisticBookingUpdate = (
  currentTimeline: TimelineResponse | null,
  bookingId: string,
  nextBooking: RentalBooking
) => {
  if (!currentTimeline) {
    return currentTimeline;
  }

  return {
    ...currentTimeline,
    groups: currentTimeline.groups.map((group) => ({
      ...group,
      items: group.items.map((item) => {
        const remainingBookings = item.bookings.filter((booking) => booking.id !== bookingId);
        const bookings =
          item.id === nextBooking.equipmentItemId
            ? [...remainingBookings, nextBooking].sort(compareBookingsForRender)
            : remainingBookings;

        return {
          ...item,
          bookings
        };
      })
    }))
  };
};

const applyOptimisticBookingScopeUpdate = (
  currentTimeline: TimelineResponse | null,
  anchorBooking: RentalBooking,
  payload: {
    equipmentItemIds: string[];
    scopeBookingIds?: string[];
    startDate?: string;
    endDate?: string;
  },
  resolvedBookings?: RentalBooking[]
) => {
  if (!currentTimeline) {
    return currentTimeline;
  }

  const allBookings = currentTimeline.groups.flatMap((group) => group.items.flatMap((item) => item.bookings));
  const resolvedBookingIds = new Set((resolvedBookings ?? []).map((booking) => booking.id));
  const explicitScopeIds = new Set(payload.scopeBookingIds ?? []);
  const currentScopeBookings = allBookings.filter(
    (booking) =>
      (explicitScopeIds.size > 0
        ? explicitScopeIds.has(booking.id)
        : bookingBelongsToProjectScope(booking, anchorBooking) || bookingMatchesScopeSignature(booking, payload, anchorBooking)) ||
      resolvedBookingIds.has(booking.id)
  );
  const nextScopeBookings =
    resolvedBookings && resolvedBookings.length > 0
      ? resolvedBookings
      : buildOptimisticScopedBookings(currentScopeBookings, anchorBooking, payload);
  const scopeBookingIds = new Set(currentScopeBookings.map((booking) => booking.id));
  const nextByEquipmentItemId = new Map(nextScopeBookings.map((booking) => [booking.equipmentItemId, booking]));

  return {
    ...currentTimeline,
    groups: currentTimeline.groups.map((group) => ({
      ...group,
      items: group.items.map((item) => {
        const remainingBookings = item.bookings.filter((booking) => !scopeBookingIds.has(booking.id));
        const nextScopedBooking = nextByEquipmentItemId.get(item.id);
        const bookings =
          nextScopedBooking !== undefined
            ? [...remainingBookings, nextScopedBooking].sort(compareBookingsForRender)
            : remainingBookings;

        return {
          ...item,
          bookings
        };
      })
    }))
  };
};

const DEFAULT_TIMELINE_RANGE_DAYS = 42;
const TIMELINE_EXTENSION_DAYS = 30;
const initialFromDate = () => addDays(new Date().toISOString().slice(0, 10), -7);

export const TimelinePage = () => {
  const { session } = useSessionContext();
  const { preferences, updatePreferences } = useUserPreferences();
  const { settings: viewSettings, updateSettings } = useViewSettings();
  const { timelineCopy } = useRuntimeUiConfig();
  const isMobile = useIsMobile();

  const [timeline, setTimeline] = useState<TimelineResponse | null>(null);
  const [items, setItems] = useState<EquipmentItem[]>([]);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [deleting, setDeleting] = useState(false);
  const [deletingProject, setDeletingProject] = useState(false);
  const [detaching, setDetaching] = useState(false);
  const [inlineBookingSavingId, setInlineBookingSavingId] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [fromDate, setFromDate] = useState(initialFromDate);
  const [rangeDays, setRangeDays] = useState(DEFAULT_TIMELINE_RANGE_DAYS);
  const [search, setSearch] = useState("");
  const [status, setStatus] = useState<TimelineStatusFilter>("all");
  const [collapsedGroups, setCollapsedGroups] = useState<string[]>([]);
  const [selection, setSelection] = useState<SelectionPayload | null>(null);
  const [selectedBooking, setSelectedBooking] = useState<SelectedBookingState | null>(null);
  const [bookingClipboard, setBookingClipboard] = useState<BookingCopyClipboard | null>(null);

  const deferredSearch = useDeferredValue(search);
  const rangeTo = addDays(fromDate, rangeDays - 1);
  const sortedTimelineGroups = useMemo(
    () => (timeline ? sortTimelineGroupsByPreference(timeline.groups, preferences) : []),
    [preferences, timeline]
  );
  const visibleTimelineItems = useMemo(
    () =>
      sortedTimelineGroups.flatMap((group) =>
        collapsedGroups.includes(group.categoryId) ? [] : group.items.map((item) => item.id)
      ),
    [collapsedGroups, sortedTimelineGroups]
  );
  const visibleItemOrder = useMemo(
    () => new Map(visibleTimelineItems.map((itemId, index) => [itemId, index])),
    [visibleTimelineItems]
  );

  const refreshAll = async () => {
    if (!session) {
      return;
    }

    setLoading(true);
    setError(null);

    try {
      const [timelineData, itemsData] = await Promise.all([
        getTimelineRequest(session, {
          from: fromDate,
          to: rangeTo,
          search: deferredSearch,
          status,
          includeInactive: false
        }),
        getItemsRequest(session, { includeInactive: true })
      ]);

      startTransition(() => {
        setTimeline(timelineData);
        setItems(itemsData);
      });
    } catch (requestError) {
      setError(requestError instanceof Error ? requestError.message : "Nie udało się pobrać timeline");
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    void refreshAll();
  }, [deferredSearch, fromDate, rangeTo, session, status]);

  useEffect(() => {
    if (!bookingClipboard) {
      return;
    }

    const handleEscape = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        setBookingClipboard(null);
      }
    };

    window.addEventListener("keydown", handleEscape);
    return () => window.removeEventListener("keydown", handleEscape);
  }, [bookingClipboard]);

  const toggleCategory = (categoryId: string) => {
    setCollapsedGroups((current) =>
      current.includes(categoryId) ? current.filter((value) => value !== categoryId) : [...current, categoryId]
    );
  };

  const moveTimelineCategory = async (categoryId: string, direction: -1 | 1) => {
    const currentIndex = sortedTimelineGroups.findIndex((group) => group.categoryId === categoryId);
    const targetIndex = currentIndex + direction;

    if (sortedTimelineGroups.length <= 1 || currentIndex < 0 || targetIndex < 0 || targetIndex >= sortedTimelineGroups.length) {
      return;
    }

    const nextGroups = [...sortedTimelineGroups];
    const [movedGroup] = nextGroups.splice(currentIndex, 1);

    if (!movedGroup) {
      return;
    }

    nextGroups.splice(targetIndex, 0, movedGroup);
    const nextItemOrder = nextGroups.flatMap((group) => group.items.map((item) => item.id));

    try {
      await updatePreferences({ equipmentItemOrder: nextItemOrder });
    } catch (requestError) {
      setError(requestError instanceof Error ? requestError.message : "Nie udało się zmienić kolejności grup sprzętu");
    }
  };

  const flatBookings: BookingWithContext[] = (timeline?.groups ?? []).flatMap((group) =>
    group.items.flatMap((item) =>
      item.bookings.map((booking) => ({
        ...booking,
        itemName: item.name,
        categoryName: item.categoryName
      }))
    )
  );
  const copyBookingScope = (booking: BookingWithContext, sourceBookingIds: string[]) => {
    const scopeBookings = flatBookings
      .filter((candidate) => sourceBookingIds.includes(candidate.id))
      .sort((left, right) => {
        const leftIndex = visibleItemOrder.get(left.equipmentItemId) ?? Number.MAX_SAFE_INTEGER;
        const rightIndex = visibleItemOrder.get(right.equipmentItemId) ?? Number.MAX_SAFE_INTEGER;

        return leftIndex - rightIndex || compareBookingsForRender(left, right);
      });

    setBookingClipboard({
      booking,
      scopeBookings: scopeBookings.length > 0 ? scopeBookings : [booking],
      sourceBookingIds
    });
  };

  const pasteBookingScope = async (target: { equipmentItemIds: string[]; startDate: string; endDate: string; requestedItemCount: number }) => {
    if (!session) {
      return;
    }

    if (!bookingClipboard) {
      return;
    }

    const sourceScope = bookingClipboard.scopeBookings;
    const sourceCount = sourceScope.length;

    if (target.equipmentItemIds.length !== target.requestedItemCount || target.equipmentItemIds.length < sourceCount) {
      return;
    }

    const anchorBooking = bookingClipboard.booking;
    const targetEquipmentItemIds = target.equipmentItemIds.slice(0, sourceCount);
    const pastedEndDate = addDays(target.startDate, dayDiffInclusive(anchorBooking.startDate, anchorBooking.endDate) - 1);
    const operationsPlan = shiftOperationsPlanForPaste(
      anchorBooking.operationsPlan,
      anchorBooking.startDate,
      anchorBooking.endDate,
      target.startDate,
      pastedEndDate,
      anchorBooking.startTime,
      anchorBooking.endTime
    );
    const bookingPayload = {
      equipmentItemIds: targetEquipmentItemIds,
      customerName: anchorBooking.customerName,
      orderNumber: anchorBooking.orderNumber,
      projectNumber: anchorBooking.projectNumber,
      projectName: anchorBooking.projectName,
      notes: anchorBooking.notes,
      startDate: target.startDate,
      endDate: pastedEndDate,
      startTime: anchorBooking.startTime,
      endTime: anchorBooking.endTime,
      operationsPlan,
      dayRate: anchorBooking.dayRate,
      totalPrice: null,
      useSuggestedItemRates: false
    };

    setSaving(true);
    setError(null);

    try {
      await createBookingsRequest(session, {
        ...bookingPayload,
        allowConflict: false
      });

      setBookingClipboard(null);
      await refreshAll();
    } catch (requestError) {
      const message = requestError instanceof Error ? requestError.message : "Nie udało się wkleić rezerwacji";

      if (message.toLowerCase().includes("overlap") || message.toLowerCase().includes("konfl")) {
        try {
          await createBookingsRequest(session, {
            ...bookingPayload,
            allowConflict: true
          });
          setBookingClipboard(null);
          await refreshAll();
          return;
        } catch (retryError) {
          console.error("Paste booking retry failed", retryError);
          return;
        }
      }

      console.error("Paste booking failed", requestError);
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className={cn("space-y-5", isMobile && "-mx-4 space-y-0")}>
      <div className={cn("space-y-4", isMobile && "space-y-0")}>
          <Card className={cn("border-0 bg-transparent p-0 shadow-none", isMobile && "rounded-none")}>
            <div className={cn("flex flex-wrap items-end justify-between gap-2", isMobile ? "px-3 pt-3" : "rounded-t-[14px] border border-b-0 border-line bg-panel px-5 py-4")}>
              <TimelineToolbar
                isMobile={isMobile}
                search={search}
                status={status}
                onSearchChange={setSearch}
                onStatusChange={setStatus}
              />

              <div className="flex flex-wrap items-end gap-2 lg:justify-end">
                <TimelineDateActions
                  isMobile={isMobile}
                  onDateShift={(deltaDays) => setFromDate((value) => addDays(value, deltaDays))}
                  onGoToday={() => setFromDate(initialFromDate())}
                />
                <Button
                  aria-label={timelineCopy.refreshButton}
                  className={cn("rounded-xl p-0", isMobile ? "h-[29px] w-[29px]" : "h-[36px] w-[36px]")}
                  title={timelineCopy.refreshButton}
                  variant="secondary"
                  onClick={() => void refreshAll()}
                >
                  <svg
                    aria-hidden="true"
                    className={cn("shrink-0 text-ink", isMobile ? "h-[15px] w-[15px]" : "h-[19px] w-[19px]")}
                    fill="none"
                    viewBox="0 0 24 24"
                  >
                    <path
                      d="M20 11a8 8 0 1 0-2.34 5.66"
                      stroke="currentColor"
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      strokeWidth="2.6"
                    />
                    <path
                      d="M20 4v7h-7"
                      stroke="currentColor"
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      strokeWidth="2.6"
                    />
                  </svg>
                </Button>
              </div>
            </div>

            {error ? <p className="mt-4 rounded-2xl bg-[#f8d9d3] px-4 py-3 text-sm text-[#8d2f20]">{error}</p> : null}
            {loading && !timeline ? (
              <div className="mt-4 rounded-3xl border border-dashed border-line bg-white/70 px-5 py-14 text-center text-sm text-muted">
                <CalendarClock className="mx-auto mb-3 h-5 w-5" />
                {timelineCopy.loadingTimeline}
              </div>
            ) : timeline ? (
              <div className="relative mt-0">
                {loading ? (
                  <div className="pointer-events-none absolute inset-x-3 top-3 z-20 rounded-2xl border border-line/70 bg-white/88 px-3 py-2 text-xs font-medium text-muted shadow-soft backdrop-blur">
                    {timelineCopy.refreshingTimeline}
                  </div>
                ) : null}
                <TimelineGrid
                  dayWidth={viewSettings.dayWidth}
                  days={Array.from({ length: timeline.range.dayCount }, (_, index) => addDays(timeline.range.from, index))}
                  groups={sortedTimelineGroups}
                  labelWidth={viewSettings.labelWidth}
                  activeSelection={selection}
                  collapsedGroups={collapsedGroups}
                  mobileMode={isMobile}
                  rowHeight={viewSettings.rowHeight}
                  selectionEnabled={!isMobile}
                  interactionsLocked={loading || saving || deleting || Boolean(inlineBookingSavingId)}
                  suppressConflictIndicators={Boolean(inlineBookingSavingId)}
                  onLabelWidthChange={(nextWidth) => updateSettings({ labelWidth: nextWidth })}
                  onToggleGroup={toggleCategory}
                  onMoveGroup={moveTimelineCategory}
                  onSelectionComplete={(nextSelection) => {
                    setSelection(nextSelection);
                  }}
                  onRequestFutureRange={() => {
                    if (loading) {
                      return;
                    }

                    setRangeDays((current) => current + TIMELINE_EXTENSION_DAYS);
                  }}
                  onBookingClick={(booking, bookingIds) =>
                    setSelectedBooking({
                      ...booking,
                      sourceBookingIds: bookingIds
                    })
                  }
                  onBookingCopy={copyBookingScope}
                  onCopyDrop={(target) => {
                    void pasteBookingScope(target);
                  }}
                  copyPreview={
                    bookingClipboard
                      ? {
                          customerName: bookingClipboard.booking.customerName,
                          rowSpan: bookingClipboard.scopeBookings.length,
                          daySpan: dayDiffInclusive(bookingClipboard.booking.startDate, bookingClipboard.booking.endDate)
                        }
                      : null
                  }
                  onBookingQuickUpdate={async (booking, payload) => {
                    if (!session) {
                      return;
                    }

                    const conflicts = flatBookings.filter(
                      (existingBooking) =>
                        existingBooking.id !== booking.id &&
                        existingBooking.equipmentItemId === payload.equipmentItemId &&
                        bookingIntervalsOverlap(existingBooking, {
                          startDate: payload.startDate,
                          endDate: payload.endDate,
                          startTime: booking.startTime,
                          endTime: booking.endTime
                        })
                    );
                    const allowConflict =
                      conflicts.length > 0
                        ? window.confirm(
                            [
                              "Wykryto konflikt rezerwacji dla tego urządzenia.",
                              "Jeśli chcesz rozdzielić taski tego samego dnia, wejdź potem w szczegóły i ustaw godziny wydania oraz zwrotu.",
                              "",
                              "OK = zapisz mimo konfliktu",
                              "Anuluj = przerwij zapis"
                            ].join("\n")
                          )
                        : false;

                    if (conflicts.length > 0 && !allowConflict) {
                      return;
                    }

                    setInlineBookingSavingId(booking.id);
                    setError(null);
                    const previousTimeline = timeline;
                    const optimisticBooking = {
                      ...booking,
                      equipmentItemId: payload.equipmentItemId,
                      startDate: payload.startDate,
                      endDate: payload.endDate,
                      updatedAt: new Date().toISOString()
                    } satisfies RentalBooking;

                    try {
                      if (previousTimeline) {
                        setTimeline(applyOptimisticBookingUpdate(previousTimeline, booking.id, optimisticBooking));
                      }

                      const updatedBooking = await updateBookingRequest(session, booking.id, {
                        equipmentItemId: payload.equipmentItemId,
                        customerName: booking.customerName,
                        orderNumber: booking.orderNumber ?? "",
                        projectNumber: booking.projectNumber ?? "",
                        projectName: booking.projectName ?? "",
                        notes: booking.notes ?? "",
                        startDate: payload.startDate,
                        endDate: payload.endDate,
                        startTime: booking.startTime,
                        endTime: booking.endTime,
                        dayRate: booking.dayRate,
                        totalPrice: bookingUsesManualTotalPrice(booking) ? booking.totalPrice : null,
                        allowConflict
                      });
                      setTimeline((currentTimeline) =>
                        applyOptimisticBookingUpdate(currentTimeline, booking.id, updatedBooking)
                      );
                      await refreshAll();
                    } catch (requestError) {
                      setTimeline(previousTimeline);
                      console.error("Timeline booking quick update failed", requestError);
                    } finally {
                      setInlineBookingSavingId(null);
                    }
                  }}
                  onBookingScopeUpdate={async (booking, payload) => {
                    if (!session) {
                      return;
                    }

                    setInlineBookingSavingId(booking.id);
                    setError(null);
                    const previousTimeline = timeline;
                    const optimisticPayload = {
                      equipmentItemIds: payload.equipmentItemIds,
                      scopeBookingIds: payload.scopeBookingIds
                    };

                    try {
                      if (previousTimeline) {
                        setTimeline(applyOptimisticBookingScopeUpdate(previousTimeline, booking, optimisticPayload));
                      }

                      const updatedScopeBookings = await updateBookingScopeRequest(session, booking.id, {
                        equipmentItemIds: payload.equipmentItemIds,
                        scopeBookingIds: payload.scopeBookingIds,
                        allowConflict: false
                      });
                      setTimeline((currentTimeline) =>
                        applyOptimisticBookingScopeUpdate(currentTimeline, booking, optimisticPayload, updatedScopeBookings)
                      );
                      await refreshAll();
                    } catch (requestError) {
                      const message =
                        requestError instanceof Error
                          ? requestError.message
                          : "Nie udało się rozszerzyć projektu na kolejne urządzenia";

                      if (message.toLowerCase().includes("overlap") || message.toLowerCase().includes("konfl")) {
                        const updatedScopeBookings = await updateBookingScopeRequest(session, booking.id, {
                          equipmentItemIds: payload.equipmentItemIds,
                          scopeBookingIds: payload.scopeBookingIds,
                          allowConflict: true
                        });
                        setTimeline((currentTimeline) =>
                          applyOptimisticBookingScopeUpdate(currentTimeline, booking, optimisticPayload, updatedScopeBookings)
                        );
                        await refreshAll();
                        return;
                      }

                      setTimeline(previousTimeline);
                      console.error("Timeline booking scope update failed", requestError);
                    } finally {
                      setInlineBookingSavingId(null);
                    }
                  }}
                  onBookingScaleUpdate={async (booking, payload) => {
                    if (!session) {
                      return;
                    }

                    setInlineBookingSavingId(booking.id);
                    setError(null);
                    const previousTimeline = timeline;
                    const optimisticPayload = {
                      equipmentItemIds: payload.equipmentItemIds,
                      scopeBookingIds: payload.scopeBookingIds,
                      startDate: payload.startDate,
                      endDate: payload.endDate
                    };

                    try {
                      if (previousTimeline) {
                        setTimeline(applyOptimisticBookingScopeUpdate(previousTimeline, booking, optimisticPayload));
                      }

                      const updatedScopeBookings = await updateBookingScopeRequest(session, booking.id, {
                        equipmentItemIds: payload.equipmentItemIds,
                        scopeBookingIds: payload.scopeBookingIds,
                        startDate: payload.startDate,
                        endDate: payload.endDate,
                        allowConflict: false
                      });
                      setTimeline((currentTimeline) =>
                        applyOptimisticBookingScopeUpdate(currentTimeline, booking, optimisticPayload, updatedScopeBookings)
                      );
                      await refreshAll();
                    } catch (requestError) {
                      const message =
                        requestError instanceof Error
                          ? requestError.message
                          : "Nie udało się przeskalować rezerwacji z osi czasu";

                      if (message.toLowerCase().includes("overlap") || message.toLowerCase().includes("konfl")) {
                        const updatedScopeBookings = await updateBookingScopeRequest(session, booking.id, {
                          equipmentItemIds: payload.equipmentItemIds,
                          scopeBookingIds: payload.scopeBookingIds,
                          startDate: payload.startDate,
                          endDate: payload.endDate,
                          allowConflict: true
                        });
                        setTimeline((currentTimeline) =>
                          applyOptimisticBookingScopeUpdate(currentTimeline, booking, optimisticPayload, updatedScopeBookings)
                        );
                        await refreshAll();
                        return;
                      } else {
                        setTimeline(previousTimeline);
                        console.error("Timeline booking scale update failed", requestError);
                      }
                    } finally {
                      setInlineBookingSavingId(null);
                    }
                  }}
                />
              </div>
            ) : null}
          </Card>
      </div>

      <BookingAssignmentModal
        open={Boolean(selection)}
        selection={selection}
        items={items}
        existingBookings={flatBookings}
        submitting={saving}
        onClose={() => setSelection(null)}
        onSubmit={async (payload) => {
          if (!session || !selection) {
            return;
          }

          setSaving(true);

          try {
            await createBookingsRequest(session, {
              equipmentItemIds: selection.equipmentItemIds,
              startDate: selection.startDate,
              endDate: selection.endDate,
              ...payload
            });
            setSelection(null);
            await refreshAll();
          } catch (requestError) {
            setError(requestError instanceof Error ? requestError.message : "Nie udało się utworzyć rezerwacji");
          } finally {
            setSaving(false);
          }
        }}
      />

      <BookingDetailsModal
        open={Boolean(selectedBooking)}
        booking={selectedBooking}
        existingBookings={flatBookings}
        saving={saving}
        deleting={deleting}
        deletingProject={deletingProject}
        detaching={detaching}
        onClose={() => setSelectedBooking(null)}
        onSave={async (payload) => {
          if (!session || !selectedBooking) {
            return;
          }

          setSaving(true);

          try {
            await updateBookingRequest(session, selectedBooking.id, payload);
            setSelectedBooking(null);
            await refreshAll();
          } catch (requestError) {
            setError(requestError instanceof Error ? requestError.message : "Nie udało się zaktualizować rezerwacji");
          } finally {
            setSaving(false);
          }
        }}
        onDelete={async () => {
          if (!session || !selectedBooking) {
            return;
          }

          setDeleting(true);

          try {
            const bookingIds = Array.from(
              new Set(
                selectedBooking.sourceBookingIds.length > 1
                  ? selectedBooking.sourceBookingIds
                  : getContiguousRelationBookingIds(selectedBooking, flatBookings, visibleItemOrder)
              )
            );
            await Promise.all(bookingIds.map((bookingId) => deleteBookingRequest(session, bookingId)));
            setSelectedBooking(null);
            await refreshAll();
          } catch (requestError) {
            setError(requestError instanceof Error ? requestError.message : "Nie udało się usunąć rezerwacji");
          } finally {
            setDeleting(false);
          }
        }}
        onDeleteProject={async () => {
          if (!session || !selectedBooking?.projectNumber) {
            return;
          }

          setDeletingProject(true);

          try {
            const projectBookingIds = Array.from(
              new Set(
                flatBookings
                  .filter((booking) => booking.projectNumber === selectedBooking.projectNumber)
                  .map((booking) => booking.id)
              )
            );
            await Promise.all(projectBookingIds.map((bookingId) => deleteBookingRequest(session, bookingId)));
            setSelectedBooking(null);
            await refreshAll();
          } catch (requestError) {
            setError(requestError instanceof Error ? requestError.message : "Nie udało się usunąć całego projektu");
          } finally {
            setDeletingProject(false);
          }
        }}
        onDetach={async () => {
          if (!session || !selectedBooking) {
            return;
          }

          setDetaching(true);

          try {
            await detachBookingRequest(session, selectedBooking.id, {
              scopeBookingIds:
                selectedBooking.sourceBookingIds.length > 1
                  ? selectedBooking.sourceBookingIds
                  : getContiguousRelationBookingIds(selectedBooking, flatBookings, visibleItemOrder)
            });
            setSelectedBooking(null);
            await refreshAll();
          } catch (requestError) {
            setError(requestError instanceof Error ? requestError.message : "Nie udało się rozdzielić rezerwacji");
          } finally {
            setDetaching(false);
          }
        }}
      />
    </div>
  );
};
