"use client";

import { startTransition, useDeferredValue, useEffect, useMemo, useState } from "react";
import { CalendarClock, ChevronLeft, ChevronRight, Filter, RefreshCcw, Smartphone } from "lucide-react";

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

import {
  createBookingsRequest,
  deleteBookingRequest,
  getCategoriesRequest,
  getItemsRequest,
  getTimelineRequest,
  updateBookingScopeRequest,
  updateBookingRequest
} from "../../lib/api";
import { bookingUsesManualTotalPrice } from "../../lib/booking-pricing";
import { sortCategoriesByPreference, sortTimelineGroupsByPreference } from "../../lib/user-ordering";
import { useSessionContext } from "../layout/session-provider";
import { useUserPreferences } from "../layout/user-preferences-provider";
import { Badge } from "../ui/badge";
import { Button } from "../ui/button";
import { Card } from "../ui/card";
import { useViewSettings } from "../layout/view-settings-provider";
import { Input } from "../ui/input";
import { useIsMobile } from "../../lib/use-is-mobile";
import { BookingAssignmentModal } from "./booking-assignment-modal";
import { BookingDetailsModal } from "./booking-details-modal";
import { TimelineGrid } from "./timeline-grid";

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

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

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 } = useUserPreferences();
  const { settings: viewSettings } = useViewSettings();
  const isMobile = useIsMobile();

  const [timeline, setTimeline] = useState<TimelineResponse | null>(null);
  const [categories, setCategories] = useState<EquipmentCategory[]>([]);
  const [items, setItems] = useState<EquipmentItem[]>([]);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [deleting, setDeleting] = 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 [customerName, setCustomerName] = useState("");
  const [status, setStatus] = useState<"all" | "active" | "service" | "retired">("all");
  const [includeInactive, setIncludeInactive] = useState(false);
  const [selectedCategoryIds, setSelectedCategoryIds] = useState<string[]>([]);
  const [collapsedGroups, setCollapsedGroups] = useState<string[]>([]);
  const [mobileSelectionMode, setMobileSelectionMode] = useState(false);
  const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false);
  const [selection, setSelection] = useState<SelectionPayload | null>(null);
  const [selectedBooking, setSelectedBooking] = useState<BookingWithContext | null>(null);

  const deferredSearch = useDeferredValue(search);
  const deferredCustomer = useDeferredValue(customerName);
  const rangeTo = addDays(fromDate, rangeDays - 1);
  const sortedCategories = useMemo(() => sortCategoriesByPreference(categories, preferences), [categories, preferences]);
  const sortedTimelineGroups = useMemo(
    () => (timeline ? sortTimelineGroupsByPreference(timeline.groups, preferences) : []),
    [preferences, timeline]
  );

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

    setLoading(true);
    setError(null);

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

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

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

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

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

  const flatBookings: BookingWithContext[] = (timeline?.groups ?? []).flatMap((group) =>
    group.items.flatMap((item) =>
      item.bookings.map((booking) => ({
        ...booking,
        itemName: item.name,
        categoryName: item.categoryName
      }))
    )
  );

  return (
    <div className="space-y-5">
      <Card className="p-3 sm:p-4">
        <div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
          <div>
            <div className="flex items-center gap-2">
              <Badge tone="accent">Timeline</Badge>
              <Badge tone="neutral">{fromDate}</Badge>
              <Badge tone="neutral">{rangeTo}</Badge>
            </div>
            <h2 className="mt-2 text-2xl font-semibold text-ink">Planowanie dostępności sprzętu</h2>
          </div>

          <div className="flex flex-wrap gap-2">
            <Button variant="secondary" onClick={() => setFromDate(initialFromDate())}>
              Dziś
            </Button>
            <Button variant="secondary" onClick={() => setFromDate((value) => addDays(value, -7))}>
              <ChevronLeft className="h-4 w-4" />
              Tydzień
            </Button>
            <Button variant="secondary" onClick={() => setFromDate((value) => addDays(value, 7))}>
              Tydzień
              <ChevronRight className="h-4 w-4" />
            </Button>
            <Button variant="secondary" onClick={() => setFromDate((value) => addDays(value, -30))}>
              Poprzedni miesiąc
            </Button>
            <Button variant="secondary" onClick={() => setFromDate((value) => addDays(value, 30))}>
              Następny miesiąc
            </Button>
          </div>
        </div>
      </Card>

      <div className="grid gap-5 xl:grid-cols-[340px_minmax(0,1fr)]">
        <Card className={`p-4 sm:p-5 ${mobileFiltersOpen || !isMobile ? "block" : "hidden xl:block"}`}>
          <div className="flex items-center justify-between">
            <div>
              <p className="text-xs uppercase tracking-[0.2em] text-muted">Filtry</p>
              <h3 className="mt-2 text-lg font-semibold text-ink">Widok operacyjny</h3>
            </div>
            {isMobile ? (
              <Button variant="ghost" onClick={() => setMobileFiltersOpen(false)}>
                Zamknij
              </Button>
            ) : null}
          </div>

          <div className="mt-5 space-y-4">
            <label className="block space-y-2">
              <span className="text-sm font-medium text-ink">Szukaj urządzenia</span>
              <Input
                placeholder="Nazwa, S/N albo TAG"
                value={search}
                onChange={(event) => setSearch(event.target.value)}
              />
            </label>

            <label className="block space-y-2">
              <span className="text-sm font-medium text-ink">Filtr klienta</span>
              <Input
                placeholder="Nazwa klienta w rezerwacji"
                value={customerName}
                onChange={(event) => setCustomerName(event.target.value)}
              />
            </label>

            <label className="block space-y-2">
              <span className="text-sm font-medium text-ink">Status sprzętu</span>
              <select
                className="w-full rounded-2xl border border-line bg-white px-4 py-2.5 text-sm text-ink outline-none focus:border-accent"
                value={status}
                onChange={(event) => setStatus(event.target.value as typeof status)}
              >
                <option value="all">Wszystkie statusy</option>
                <option value="active">Tylko aktywne</option>
                <option value="service">Tylko serwis</option>
                <option value="retired">Tylko wycofane</option>
              </select>
            </label>

            <label className="flex items-center gap-3 rounded-2xl border border-line bg-white px-4 py-3">
              <input
                checked={includeInactive}
                onChange={(event) => setIncludeInactive(event.target.checked)}
                type="checkbox"
              />
              <span className="text-sm font-medium text-ink">Pokaż także nieaktywne na timeline</span>
            </label>

            <div>
              <div className="flex items-center justify-between">
                <span className="text-sm font-medium text-ink">Typy sprzętu</span>
                <Badge tone="neutral">{selectedCategoryIds.length || sortedCategories.length}</Badge>
              </div>
              <div className="mt-3 max-h-64 space-y-2 overflow-y-auto pr-1">
                {sortedCategories.map((category) => {
                  const checked = selectedCategoryIds.length === 0 || selectedCategoryIds.includes(category.id);

                  return (
                    <label
                      key={category.id}
                      className="flex items-start gap-3 rounded-2xl border border-line bg-white px-4 py-3"
                    >
                      <input
                        checked={checked}
                        type="checkbox"
                        onChange={() => toggleCategoryFilter(category.id)}
                      />
                      <span className="min-w-0">
                        <span className="block text-sm font-semibold text-ink">{category.name}</span>
                        <span className="block text-xs text-muted">{category.itemCount} urządzeń</span>
                      </span>
                    </label>
                  );
                })}
              </div>
            </div>
          </div>
        </Card>

        <div className="space-y-4">
          <Card className="p-4 sm:p-5">
            <div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
              <div className="flex flex-wrap gap-2">
                <Badge tone="success">{sortedTimelineGroups.reduce((count, group) => count + group.items.length, 0) ?? 0} urządzeń w widoku</Badge>
                <Badge tone="neutral">{timeline?.range.dayCount ?? rangeDays} dni</Badge>
                <Badge tone="warning">
                  {sortedTimelineGroups.flatMap((group) => group.items).reduce((count, item) => count + item.bookings.length, 0)} rezerwacji
                </Badge>
              </div>

              <div className="flex flex-wrap gap-2">
                {isMobile ? (
                  <>
                    <Button variant="secondary" onClick={() => setMobileFiltersOpen(true)}>
                      <Filter className="h-4 w-4" />
                      Filtry
                    </Button>
                    <Button
                      variant={mobileSelectionMode ? "primary" : "secondary"}
                      onClick={() => setMobileSelectionMode((value) => !value)}
                    >
                      <Smartphone className="h-4 w-4" />
                      {mobileSelectionMode ? "Tryb zaznaczania: ON" : "Tryb zaznaczania"}
                    </Button>
                  </>
                ) : null}
                <Button variant="secondary" onClick={() => void refreshAll()}>
                  <RefreshCcw className="h-4 w-4" />
                  Odśwież
                </Button>
              </div>
            </div>

            {error ? <p className="mt-4 rounded-2xl bg-[#f8d9d3] px-4 py-3 text-sm text-[#8d2f20]">{error}</p> : null}
            {loading ? (
              <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" />
                Ładowanie osi czasu...
              </div>
            ) : timeline ? (
              <div className="mt-4">
                <TimelineGrid
                  bookingBarHeight={viewSettings.bookingBarHeight}
                  dayWidth={viewSettings.dayWidth}
                  days={Array.from({ length: timeline.range.dayCount }, (_, index) => addDays(timeline.range.from, index))}
                  groups={sortedTimelineGroups}
                  collapsedGroups={collapsedGroups}
                  mobileMode={isMobile}
                  rowHeight={viewSettings.rowHeight}
                  selectionEnabled={!isMobile || mobileSelectionMode}
                  interactionsLocked={loading || saving || deleting || Boolean(inlineBookingSavingId)}
                  onToggleGroup={toggleCategory}
                  onSelectionComplete={(nextSelection) => {
                    setSelection(nextSelection);
                  }}
                  onRequestFutureRange={() => {
                    if (loading) {
                      return;
                    }

                    setRangeDays((current) => current + TIMELINE_EXTENSION_DAYS);
                  }}
                  onBookingClick={(booking) => setSelectedBooking(booking)}
                  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);

                    try {
                      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
                      });
                      await refreshAll();
                    } catch (requestError) {
                      setError(
                        requestError instanceof Error
                          ? requestError.message
                          : "Nie udało się zaktualizować rezerwacji z osi czasu"
                      );
                    } finally {
                      setInlineBookingSavingId(null);
                    }
                  }}
                  onBookingScopeUpdate={async (booking, payload) => {
                    if (!session) {
                      return;
                    }

                    setInlineBookingSavingId(booking.id);
                    setError(null);

                    try {
                      await updateBookingScopeRequest(session, booking.id, {
                        equipmentItemIds: payload.equipmentItemIds,
                        allowConflict: false
                      });
                      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 shouldAllowConflict = window.confirm(
                          [
                            "Wykryto konflikt rezerwacji dla jednego z wybranych urządzeń.",
                            "OK = zapisz mimo konfliktu",
                            "Anuluj = wróć do edycji"
                          ].join("\n")
                        );

                        if (!shouldAllowConflict) {
                          return;
                        }

                        await updateBookingScopeRequest(session, booking.id, {
                          equipmentItemIds: payload.equipmentItemIds,
                          allowConflict: true
                        });
                        await refreshAll();
                        return;
                      }

                      setError(message);
                    } finally {
                      setInlineBookingSavingId(null);
                    }
                  }}
                />
              </div>
            ) : null}
          </Card>
        </div>
      </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}
        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 {
            await deleteBookingRequest(session, selectedBooking.id);
            setSelectedBooking(null);
            await refreshAll();
          } catch (requestError) {
            setError(requestError instanceof Error ? requestError.message : "Nie udało się usunąć rezerwacji");
          } finally {
            setDeleting(false);
          }
        }}
      />
    </div>
  );
};
