"use client";

import { useDeferredValue, useEffect, useMemo, useState } from "react";
import { ChevronDown, ChevronRight, Edit3, FolderPlus, Plus } from "lucide-react";

import type { EquipmentCategory, EquipmentItem, EquipmentStatus } from "@rental/shared";

import {
  createItemRequest,
  deleteItemRequest,
  getCategoriesRequest,
  getItemsRequest,
  updateItemRequest
} from "../../lib/api";
import { formatPriceInput, parsePriceInput } from "../../lib/booking-pricing";
import {
  moveItemInPreferences,
  sortItemsByPreference
} from "../../lib/user-ordering";
import { cn } from "../../lib/cn";
import { useSessionContext } from "../layout/session-provider";
import { useUserPreferences } from "../layout/user-preferences-provider";
import { useIsMobile } from "../../lib/use-is-mobile";
import { Badge } from "../ui/badge";
import { Button } from "../ui/button";
import { Card } from "../ui/card";
import { Input, Textarea } from "../ui/input";
import { Modal } from "../ui/modal";

type ItemUnitDraft = {
  id?: string;
  serialNumber: string;
  assetTag: string;
};

type ItemDraft = {
  id?: string;
  categoryId: string;
  name: string;
  shortName: string;
  useShortNameOnTimeline: boolean;
  quantity: string;
  units: ItemUnitDraft[];
  suggestedDayRate: string;
  status: "active" | "service" | "retired";
  notes: string;
  isVisibleOnTimeline: boolean;
};

type InlineEditableField = "serialNumber" | "assetTag";

type InlineEditState = {
  itemId: string;
  field: InlineEditableField;
  value: string;
};

type BulkImportState = {
  serialTagPairs: string;
};

type ModelGroupSummary = {
  key: string;
  baseName: string;
  categoryId: string;
  categoryName: string;
  shortName: string;
  suggestedDayRate: number | null;
  notes: string;
  items: EquipmentItem[];
  serialCount: number;
  tagCount: number;
  activeCount: number;
  serviceCount: number;
  retiredCount: number;
};

const ItemActionIcon = ({ kind }: { kind: "up" | "down" | "edit" | "delete" }) => {
  if (kind === "up") {
    return (
      <svg aria-hidden="true" className="h-4 w-4" fill="none" viewBox="0 0 24 24">
        <path d="M12 18V6" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.2" />
        <path d="M7 11L12 6L17 11" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.2" />
      </svg>
    );
  }

  if (kind === "down") {
    return (
      <svg aria-hidden="true" className="h-4 w-4" fill="none" viewBox="0 0 24 24">
        <path d="M12 6V18" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.2" />
        <path d="M7 13L12 18L17 13" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2.2" />
      </svg>
    );
  }

  if (kind === "edit") {
    return (
      <svg aria-hidden="true" className="h-4 w-4" fill="none" viewBox="0 0 24 24">
        <path
          d="M4 20H8L18 10C18.6 9.4 18.6 8.4 18 7.8L16.2 6C15.6 5.4 14.6 5.4 14 6L4 16V20Z"
          stroke="currentColor"
          strokeLinecap="round"
          strokeLinejoin="round"
          strokeWidth="2"
        />
        <path d="M12.5 7.5L16.5 11.5" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" />
      </svg>
    );
  }

  return (
    <svg aria-hidden="true" className="h-4 w-4" fill="none" viewBox="0 0 24 24">
      <path d="M5 7H19" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" />
      <path d="M9 7V5.5C9 4.7 9.7 4 10.5 4H13.5C14.3 4 15 4.7 15 5.5V7" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" />
      <path d="M7 7L8 19C8.1 20 8.9 20.8 9.9 20.8H14.1C15.1 20.8 15.9 20 16 19L17 7" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" />
      <path d="M10 11V17" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" />
      <path d="M14 11V17" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" />
    </svg>
  );
};

const MODEL_SUFFIX_PATTERN = /\s+(?:[A-Z](?:\d{1,3})?|#\d{1,3})$/i;
const MAX_ITEM_QUANTITY = 200;

const createBlankUnitDraft = (): ItemUnitDraft => ({
  serialNumber: "",
  assetTag: ""
});

const blankBulkImportState = (): BulkImportState => ({
  serialTagPairs: ""
});

const blankItem = (categoryId = ""): ItemDraft => ({
  categoryId,
  name: "",
  shortName: "",
  useShortNameOnTimeline: false,
  quantity: "1",
  units: [createBlankUnitDraft()],
  suggestedDayRate: "",
  status: "active",
  notes: "",
  isVisibleOnTimeline: true
});

const statusCycle: EquipmentStatus[] = ["active", "service", "retired"];

const statusLabelByValue: Record<EquipmentStatus, string> = {
  active: "Aktywny",
  service: "Serwis",
  retired: "Wyłączony"
};

const statusToneByValue: Record<EquipmentStatus, "success" | "warning" | "neutral"> = {
  active: "success",
  service: "warning",
  retired: "neutral"
};

const getModelBaseName = (name: string) => {
  const trimmed = name.trim();
  const withoutSuffix = trimmed.replace(MODEL_SUFFIX_PATTERN, "").trim();

  return withoutSuffix || trimmed;
};

const getModelGroupKey = (categoryId: string, name: string) => `${categoryId}::${getModelBaseName(name).toLowerCase()}`;

const expandUnits = (units: ItemUnitDraft[], targetCount: number) => {
  if (targetCount <= units.length) {
    return units;
  }

  return [...units, ...Array.from({ length: targetCount - units.length }, () => createBlankUnitDraft())];
};

const parseBulkSerialTagPairs = (value: string) =>
  value
    .split(/\n+/g)
    .map((row) => row.trim())
    .filter(Boolean)
    .map((row) => {
      const delimiter = row.includes(";") ? ";" : row.includes("\t") ? "\t" : ",";
      const [serialNumber = "", assetTag = ""] = row.split(delimiter).map((entry) => entry.trim());

      return {
        serialNumber,
        assetTag
      };
    })
    .filter((row) => row.serialNumber || row.assetTag);

const parseItemQuantity = (value: string) => {
  const parsed = Number.parseInt(value, 10);

  if (!Number.isFinite(parsed)) {
    return null;
  }

  return Math.min(Math.max(parsed, 1), MAX_ITEM_QUANTITY);
};

const formatUnitIndex = (index: number) => `#${String(index + 1).padStart(2, "0")}`;

const buildItemName = (baseName: string, index: number, totalUnits: number) => {
  const normalizedBaseName = baseName.trim();

  if (totalUnits <= 1) {
    return normalizedBaseName;
  }

  return `${normalizedBaseName} ${formatUnitIndex(index)}`;
};

export const EquipmentAdminPage = () => {
  const { session } = useSessionContext();
  const { preferences, ready: preferencesReady, saving: preferencesSaving, updatePreferences } = useUserPreferences();
  const isMobile = useIsMobile();
  const [categories, setCategories] = useState<EquipmentCategory[]>([]);
  const [items, setItems] = useState<EquipmentItem[]>([]);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [search, setSearch] = useState("");
  const [statusFilter, setStatusFilter] = useState<"all" | "active" | "service" | "retired">("all");
  const [includeInactive, setIncludeInactive] = useState(true);
  const [itemDraft, setItemDraft] = useState<ItemDraft | null>(null);
  const [bulkImport, setBulkImport] = useState<BulkImportState>(blankBulkImportState);
  const [expandedModelKeys, setExpandedModelKeys] = useState<string[]>([]);
  const [inlineEdit, setInlineEdit] = useState<InlineEditState | null>(null);
  const [quickSavingItemId, setQuickSavingItemId] = useState<string | null>(null);

  const deferredSearch = useDeferredValue(search);
  const parsedSuggestedDayRate = parsePriceInput(itemDraft?.suggestedDayRate ?? "");

  const isAdmin = session?.user.role === "admin";
  const canEditItems = session?.user.role === "admin" || session?.user.role === "operator";
  const canReorderView = Boolean(session?.user.id) && preferencesReady && !preferencesSaving;
  const sortedItems = useMemo(() => sortItemsByPreference(items, preferences), [items, preferences]);
  const modelGroups = useMemo<ModelGroupSummary[]>(() => {
    const groups = new Map<string, ModelGroupSummary>();

    for (const item of sortedItems) {
      const key = getModelGroupKey(item.categoryId, item.name);
      const existing = groups.get(key);

      if (existing) {
        existing.items.push(item);
        existing.serialCount += item.serialNumber?.trim() ? 1 : 0;
        existing.tagCount += item.assetTag?.trim() ? 1 : 0;
        existing.activeCount += item.status === "active" ? 1 : 0;
        existing.serviceCount += item.status === "service" ? 1 : 0;
        existing.retiredCount += item.status === "retired" ? 1 : 0;
        continue;
      }

      groups.set(key, {
        key,
        baseName: getModelBaseName(item.name),
        categoryId: item.categoryId,
        categoryName: item.categoryName,
        shortName: item.shortName ?? "",
        suggestedDayRate: item.suggestedDayRate,
        notes: item.notes ?? "",
        items: [item],
        serialCount: item.serialNumber?.trim() ? 1 : 0,
        tagCount: item.assetTag?.trim() ? 1 : 0,
        activeCount: item.status === "active" ? 1 : 0,
        serviceCount: item.status === "service" ? 1 : 0,
        retiredCount: item.status === "retired" ? 1 : 0
      });
    }

    return Array.from(groups.values());
  }, [sortedItems]);
  const itemsByModelKey = useMemo(() => {
    const groups = new Map<string, EquipmentItem[]>();

    for (const item of items) {
      const key = getModelGroupKey(item.categoryId, item.name);
      const group = groups.get(key) ?? [];
      group.push(item);
      groups.set(key, group);
    }

    for (const group of groups.values()) {
      group.sort((left, right) => left.name.localeCompare(right.name, "pl"));
    }

    return groups;
  }, [items]);
  const draftQuantity = parseItemQuantity(itemDraft?.quantity ?? "") ?? 0;
  const visibleDraftUnits = itemDraft ? itemDraft.units.slice(0, draftQuantity) : [];
  const existingDraftUnitCount = itemDraft?.units.filter((unit) => Boolean(unit.id)).length ?? 0;
  const reducingExistingQuantity = itemDraft ? draftQuantity < existingDraftUnitCount : false;
  const quantityNeedsAdmin = reducingExistingQuantity && !isAdmin;

  useEffect(() => {
    setExpandedModelKeys((current) => current.filter((key) => modelGroups.some((group) => group.key === key)));
  }, [modelGroups]);

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

    setIncludeInactive(preferences.equipmentIncludeInactive);
  }, [preferences.equipmentIncludeInactive, preferencesReady]);

  const submitQuickItemUpdate = async (itemId: string, payload: Record<string, unknown>, fallbackMessage: string) => {
    if (!session) {
      return;
    }

    setQuickSavingItemId(itemId);
    setError(null);

    try {
      await updateItemRequest(session, itemId, payload);
      setInlineEdit((current) => (current?.itemId === itemId ? null : current));
      await refresh();
    } catch (requestError) {
      setError(requestError instanceof Error ? requestError.message : fallbackMessage);
    } finally {
      setQuickSavingItemId(null);
    }
  };

  const cycleItemStatus = async (item: EquipmentItem) => {
    const currentIndex = statusCycle.indexOf(item.status);
    const nextStatus = statusCycle[(currentIndex + 1) % statusCycle.length];

    await submitQuickItemUpdate(item.id, { status: nextStatus }, "Nie udało się zmienić statusu urządzenia");
  };

  const startInlineEdit = (item: EquipmentItem, field: InlineEditableField) => {
    if (!canEditItems || saving || Boolean(quickSavingItemId)) {
      return;
    }

    setInlineEdit({
      itemId: item.id,
      field,
      value: field === "serialNumber" ? item.serialNumber ?? "" : item.assetTag ?? ""
    });
  };

  const submitInlineEdit = async (item: EquipmentItem, field: InlineEditableField, value: string) => {
    await submitQuickItemUpdate(
      item.id,
      field === "serialNumber" ? { serialNumber: value } : { assetTag: value },
      `Nie udało się zapisać pola ${field === "serialNumber" ? "S/N" : "TAG"}`
    );
  };

  const renderInlineCell = (item: EquipmentItem, field: InlineEditableField) => {
    const isEditing = inlineEdit?.itemId === item.id && inlineEdit.field === field;

    if (isEditing) {
      return (
        <Input
          autoFocus
          className="h-8 rounded-xl px-3 py-1.5"
          disabled={quickSavingItemId === item.id}
          value={inlineEdit.value}
          onBlur={() =>
            setInlineEdit((current) =>
              current?.itemId === item.id && current.field === field ? null : current
            )
          }
          onChange={(event) =>
            setInlineEdit((current) =>
              current?.itemId === item.id && current.field === field
                ? { ...current, value: event.target.value }
                : current
            )
          }
          onKeyDown={(event) => {
            if (event.key === "Enter") {
              event.preventDefault();
              void submitInlineEdit(item, field, event.currentTarget.value);
            }

            if (event.key === "Escape") {
              event.preventDefault();
              setInlineEdit((current) =>
                current?.itemId === item.id && current.field === field ? null : current
              );
            }
          }}
        />
      );
    }

    return (
      <button
        className="w-full rounded-xl px-2 py-1.5 text-left text-sm text-muted transition hover:bg-[#f7f2ea] disabled:cursor-default disabled:hover:bg-transparent"
        disabled={!canEditItems || saving || Boolean(quickSavingItemId)}
        onDoubleClick={() => startInlineEdit(item, field)}
        title={canEditItems ? `Dwuklik, aby edytować pole ${field === "serialNumber" ? "S/N" : "TAG"}` : undefined}
        type="button"
      >
        <span className="block truncate">{field === "serialNumber" ? item.serialNumber || "—" : item.assetTag || "—"}</span>
      </button>
    );
  };

  const toggleModelGroup = (groupKey: string) => {
    setExpandedModelKeys((current) =>
      current.includes(groupKey) ? current.filter((key) => key !== groupKey) : [...current, groupKey]
    );
  };

  const getModelGroupStatus = (group: ModelGroupSummary): { label: string; tone: "success" | "warning" | "neutral" } => {
    if (group.items.length === group.activeCount) {
      return { label: "Aktywny", tone: "success" };
    }

    if (group.items.length === group.serviceCount) {
      return { label: "Serwis", tone: "warning" };
    }

    if (group.items.length === group.retiredCount) {
      return { label: "Wyłączony", tone: "neutral" };
    }

    return { label: "Mieszany", tone: "warning" };
  };

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

    setLoading(true);
    setError(null);

    try {
      const [nextCategories, nextItems] = await Promise.all([
        getCategoriesRequest(session),
        getItemsRequest(session, {
          includeInactive,
          search: deferredSearch,
          status: statusFilter
        })
      ]);

      setCategories(nextCategories);
      setItems(nextItems);
    } catch (requestError) {
      setError(requestError instanceof Error ? requestError.message : "Nie udało się pobrać sprzętu");
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    void refresh();
  }, [deferredSearch, includeInactive, session, statusFilter]);

  const handleEquipmentIncludeInactiveChange = (checked: boolean) => {
    setIncludeInactive(checked);
    void updatePreferences({ equipmentIncludeInactive: checked }).catch((requestError) => {
      setError(requestError instanceof Error ? requestError.message : "Nie udało się zapisać ustawienia widoku");
    });
  };

  const moveItem = async (itemId: string, direction: -1 | 1) => {
    if (!canReorderView) {
      return;
    }

    setError(null);

    try {
      const nextPreferences = moveItemInPreferences(items, preferences, itemId, direction);
      await updatePreferences({ equipmentItemOrder: nextPreferences.equipmentItemOrder });
    } catch (requestError) {
      setError(requestError instanceof Error ? requestError.message : "Nie udało się zapisać kolejności urządzeń");
    }
  };

  const moveModelGroup = async (groupKey: string, direction: -1 | 1) => {
    if (!canReorderView) {
      return;
    }

    const currentIndex = modelGroups.findIndex((group) => group.key === groupKey);
    const targetIndex = currentIndex + direction;

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

    const nextGroups = [...modelGroups];
    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));

    setError(null);

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

  const openItemDraft = (item?: EquipmentItem) => {
    if (!item) {
      setItemDraft(blankItem(categories[0]?.id ?? ""));
      setBulkImport(blankBulkImportState());
      return;
    }

    const modelGroup = itemsByModelKey.get(getModelGroupKey(item.categoryId, item.name)) ?? [item];

    setItemDraft({
      id: item.id,
      categoryId: item.categoryId,
      name: getModelBaseName(item.name),
      shortName: item.shortName ?? "",
      useShortNameOnTimeline: item.useShortNameOnTimeline,
      quantity: String(modelGroup.length),
      units: modelGroup.map((groupItem) => ({
        id: groupItem.id,
        serialNumber: groupItem.serialNumber ?? "",
        assetTag: groupItem.assetTag ?? ""
      })),
      suggestedDayRate: formatPriceInput(item.suggestedDayRate),
      status: item.status,
      notes: item.notes ?? "",
      isVisibleOnTimeline: item.isVisibleOnTimeline
    });
    setBulkImport({
      serialTagPairs: modelGroup
        .map((groupItem) => `${groupItem.serialNumber ?? ""};${groupItem.assetTag ?? ""}`.trim())
        .filter((value) => value !== ";")
        .join("\n")
    });
  };

  const updateDraftQuantity = (value: string) => {
    const digitsOnly = value.replace(/[^\d]/g, "");

    setItemDraft((current) => {
      if (!current) {
        return current;
      }

      if (digitsOnly === "") {
        return {
          ...current,
          quantity: ""
        };
      }

      const nextQuantity = parseItemQuantity(digitsOnly);

      if (!nextQuantity) {
        return current;
      }

      return {
        ...current,
        quantity: String(nextQuantity),
        units: expandUnits(current.units, nextQuantity)
      };
    });
  };

  const updateDraftUnit = (index: number, field: keyof ItemUnitDraft, value: string) => {
    setItemDraft((current) => {
      if (!current) {
        return current;
      }

      return {
        ...current,
        units: current.units.map((unit, unitIndex) =>
          unitIndex === index
            ? {
                ...unit,
                [field]: value
              }
            : unit
        )
      };
    });
  };

  const applyBulkImport = (source: string) => {
    const values = parseBulkSerialTagPairs(source);

    if (values.length === 0) {
      return;
    }

    setItemDraft((current) => {
      if (!current) {
        return current;
      }

      const quantity = parseItemQuantity(current.quantity) ?? current.units.length;
      const nextUnits = expandUnits(current.units, Math.max(quantity, values.length)).map((unit, index) =>
        index < values.length
          ? {
              ...unit,
              serialNumber: values[index]?.serialNumber ?? "",
              assetTag: values[index]?.assetTag ?? ""
            }
          : unit
      );

      return {
        ...current,
        quantity: String(Math.max(quantity, values.length)),
        units: nextUnits
      };
    });
  };

  const handleBulkImportFile = async (file: File) => {
    try {
      const fileContent = await file.text();
      const rows = fileContent
        .split(/\r?\n/g)
        .map((row) => row.trim())
        .filter(Boolean);
      const normalizedContent = rows
        .filter((row, index) => {
          if (index > 0) {
            return true;
          }

          const firstCell = row.split(/[;,\t]/)[0]?.trim().toLowerCase() ?? "";
          return !["s/n", "sn", "serial", "serialnumber", "serial_number"].includes(firstCell);
        })
        .join("\n");

      setBulkImport({ serialTagPairs: normalizedContent });
      applyBulkImport(normalizedContent);
    } catch (requestError) {
      setError(requestError instanceof Error ? requestError.message : "Nie udało się odczytać pliku CSV");
    }
  };

  const saveItemDraft = async () => {
    if (!session || !itemDraft || parsedSuggestedDayRate === null) {
      return;
    }

    const quantity = parseItemQuantity(itemDraft.quantity);

    if (!quantity || quantityNeedsAdmin) {
      return;
    }

    const baseName = itemDraft.name.trim();

    if (!baseName) {
      return;
    }

    const activeUnits = itemDraft.units.slice(0, quantity);
    const removedUnits = itemDraft.units.slice(quantity).filter((unit) => Boolean(unit.id));

    setSaving(true);
    setError(null);

    try {
      if (removedUnits.length > 0) {
        for (const unit of removedUnits) {
          if (unit.id) {
            await deleteItemRequest(session, unit.id);
          }
        }
      }

      for (const [index, unit] of activeUnits.entries()) {
        const payload = {
          categoryId: itemDraft.categoryId,
          name: buildItemName(baseName, index, activeUnits.length),
          shortName: itemDraft.shortName.trim(),
          useShortNameOnTimeline: itemDraft.useShortNameOnTimeline,
          serialNumber: unit.serialNumber.trim(),
          assetTag: unit.assetTag.trim(),
          suggestedDayRate: parsedSuggestedDayRate,
          status: itemDraft.status,
          notes: itemDraft.notes.trim(),
          isVisibleOnTimeline: itemDraft.isVisibleOnTimeline
        };

        if (unit.id) {
          await updateItemRequest(session, unit.id, payload);
          continue;
        }

        await createItemRequest(session, payload);
      }

      setItemDraft(null);
      await refresh();
    } catch (requestError) {
      setError(requestError instanceof Error ? requestError.message : "Nie udało się zapisać urządzeń");
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="space-y-5">
      <div className="min-w-0">
        <Card className="min-w-0 overflow-hidden p-4 sm:p-5">
          <div className="flex flex-col gap-4">
            <div className="flex flex-col gap-3 lg:flex-row lg:items-end lg:justify-between">
              <div className="grid min-w-0 grid-cols-[minmax(0,1.25fr)_minmax(0,0.9fr)] items-end gap-2 sm:grid-cols-[minmax(0,1.4fr)_minmax(0,0.9fr)_auto] sm:gap-3">
                <label className="block min-w-0 space-y-1.5">
                  <span className="text-xs uppercase tracking-[0.18em] text-muted">Szukaj</span>
                  <Input
                    className="h-[45px] py-0"
                    value={search}
                    onChange={(event) => setSearch(event.target.value)}
                    placeholder="Nazwa, S/N lub TAG"
                  />
                </label>
                <label className="block min-w-0 space-y-1.5">
                  <span className="text-xs uppercase tracking-[0.18em] text-muted">Status</span>
                  <select
                    className="h-[45px] w-full rounded-2xl border border-line bg-white px-4 py-0 text-sm text-ink outline-none focus:border-accent"
                    value={statusFilter}
                    onChange={(event) => setStatusFilter(event.target.value as typeof statusFilter)}
                  >
                    <option value="all">Wszystkie</option>
                    <option value="active">Aktywne</option>
                    <option value="service">Serwis</option>
                    <option value="retired">Wyłączone</option>
                  </select>
                </label>
                <label className="col-span-2 mt-0 flex min-h-8 items-center gap-2 px-0 py-0 sm:col-span-1 sm:mt-[26px] sm:min-h-[45px] sm:self-end">
                  <input
                    checked={includeInactive}
                    onChange={(event) => handleEquipmentIncludeInactiveChange(event.target.checked)}
                    className="h-3.5 w-3.5"
                    type="checkbox"
                  />
                  <span className="text-xs text-ink sm:text-sm">Pokaż nieaktywne</span>
                </label>
              </div>
              {canEditItems ? (
                <Button
                  className={cn(
                    "!border-[#ff5a1f] !bg-[#ff5a1f] !text-white hover:!bg-[#e74d16]",
                    isMobile ? "h-10 w-10 self-start rounded-lg p-0" : "h-[45px] self-end"
                  )}
                  variant="secondary"
                  onClick={() => openItemDraft()}
                >
                  {isMobile ? <FolderPlus className="h-9 w-9" /> : <Plus className="h-4 w-4" />}
                  {isMobile ? null : "Nowa grupa"}
                </Button>
              ) : null}
            </div>

            <div className="max-w-full overflow-x-auto rounded-[28px] border border-line">
              <div className="min-w-[920px]">
                <div className="hidden grid-cols-[minmax(300px,2fr)_54px_minmax(108px,0.72fr)_78px_86px_80px_110px] items-center gap-1 border-b border-line bg-[#f5efe5] px-4 py-2 text-[11px] font-semibold uppercase tracking-[0.16em] text-muted lg:grid">
                  <div>Grupa sprzętu</div>
                  <div className="text-center">Szt.</div>
                  <div>Stawka</div>
                  <div className="text-center">S/N</div>
                  <div className="text-center">Status</div>
                  <div className="text-center">Pozycja</div>
                  <div className="text-right">Akcje</div>
                </div>

                {modelGroups.map((group) => {
                  const isExpanded = expandedModelKeys.includes(group.key);
                  const groupStatus = getModelGroupStatus(group);
                  const siblingIndex = modelGroups.findIndex((entry) => entry.key === group.key);

                  return (
                    <div key={group.key} className="border-b border-line/60 bg-white">
                      <button
                        className="grid w-full gap-3 px-4 py-3 text-left transition hover:bg-[#fbf7f0] lg:grid-cols-[minmax(300px,2fr)_54px_minmax(108px,0.72fr)_78px_86px_80px_110px] lg:items-center lg:gap-1 lg:py-2"
                        type="button"
                        onClick={() => toggleModelGroup(group.key)}
                      >
                        <div className="min-w-0">
                          <p className="mb-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-muted lg:hidden">Grupa sprzętu</p>
                          <div className="flex min-w-0 items-center gap-2">
                            {isExpanded ? <ChevronDown className="h-4 w-4 shrink-0 text-muted" /> : <ChevronRight className="h-4 w-4 shrink-0 text-muted" />}
                            <p className="truncate text-sm font-semibold text-ink">{group.baseName}</p>
                            {group.shortName ? <Badge tone="accent">{group.shortName}</Badge> : null}
                          </div>
                          {group.notes ? <p className="mt-1 truncate text-xs text-muted">{group.notes}</p> : null}
                        </div>
                        <div className="flex items-center justify-between gap-3 lg:block">
                          <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-muted lg:hidden">Szt.</span>
                          <div className="text-right text-sm text-muted lg:text-center">{group.items.length}</div>
                        </div>
                        <div className="flex items-center justify-between gap-3 lg:block">
                          <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-muted lg:hidden">Stawka sugerowana</span>
                          <div className="text-right text-sm text-muted lg:text-left">
                            {group.suggestedDayRate !== null ? `${formatPriceInput(group.suggestedDayRate)} / dzień` : "—"}
                          </div>
                        </div>
                        <div className="flex items-center justify-between gap-3 lg:block">
                          <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-muted lg:hidden">S/N</span>
                          <div className="text-right text-sm text-muted lg:text-center">{group.serialCount} / {group.items.length}</div>
                        </div>
                        <div>
                          <p className="mb-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-muted lg:hidden">Status</p>
                          <div className="lg:flex lg:justify-center">
                            <Badge tone={groupStatus.tone}>{groupStatus.label}</Badge>
                          </div>
                        </div>
                        <div>
                          <p className="mb-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-muted lg:hidden">Pozycja</p>
                          <div className="flex min-h-8 items-center justify-start gap-1 lg:justify-center">
                            <button
                              className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border border-line bg-[#f7f2e9] text-[#20332b] transition hover:bg-[#efe7d9] disabled:cursor-default disabled:opacity-40"
                              disabled={!canReorderView || siblingIndex <= 0}
                              title="Przesuń grupę wyżej"
                              onClick={(event) => {
                                event.preventDefault();
                                event.stopPropagation();
                                void moveModelGroup(group.key, -1);
                              }}
                              type="button"
                            >
                              <ItemActionIcon kind="up" />
                            </button>
                            <button
                              className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border border-line bg-[#f7f2e9] text-[#20332b] transition hover:bg-[#efe7d9] disabled:cursor-default disabled:opacity-40"
                              disabled={!canReorderView || siblingIndex === -1 || siblingIndex >= modelGroups.length - 1}
                              title="Przesuń grupę niżej"
                              onClick={(event) => {
                                event.preventDefault();
                                event.stopPropagation();
                                void moveModelGroup(group.key, 1);
                              }}
                              type="button"
                            >
                              <ItemActionIcon kind="down" />
                            </button>
                          </div>
                        </div>
                        <div className="min-w-[110px]">
                          <p className="mb-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-muted lg:hidden">Akcje</p>
                          <div className="flex min-h-8 items-center justify-start gap-2 lg:justify-end">
                            <span className="text-xs text-muted">{isExpanded ? "Zwiń" : "Rozwiń"}</span>
                          </div>
                        </div>
                      </button>

                      {isExpanded ? (
                        <div className="border-t border-line/60 bg-[#fbf8f2] px-4 py-4">
                          <div className="mb-3 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
                            <div>
                              <p className="text-sm font-semibold text-ink">Egzemplarze w grupie {group.baseName}</p>
                              <p className="text-xs text-muted">
                                Kliknij status, aby przełączać Aktywny / Serwis / Wyłączony. Dwuklik w polu S/N albo TAG uruchamia edycję inline.
                              </p>
                            </div>
                            <div className="flex flex-wrap gap-2">
                              {canEditItems ? (
                                <Button variant="secondary" onClick={() => openItemDraft(group.items[0])}>
                                  <Edit3 className="h-4 w-4" />
                                  Edytuj grupę
                                </Button>
                              ) : null}
                            </div>
                          </div>

                          <div className="space-y-1.5">
                            <div className="hidden grid-cols-[88px_minmax(0,1fr)_minmax(0,1fr)_120px_112px] gap-2 px-1 text-[10px] font-semibold uppercase tracking-[0.14em] text-muted lg:grid">
                              <div>Egzemplarz</div>
                              <div>S/N</div>
                              <div>TAG</div>
                              <div>Status</div>
                              <div className="text-right">Akcje</div>
                            </div>

                            {group.items.map((item) => {
                              const siblingItems = group.items;
                              const siblingIndex = siblingItems.findIndex((candidate) => candidate.id === item.id);

                              return (
                                <div
                                  key={item.id}
                                  className="grid gap-1.5 rounded-xl border border-line bg-white px-3 py-1.5 lg:grid-cols-[88px_minmax(0,1fr)_minmax(0,1fr)_120px_112px] lg:items-center"
                                >
                                  <div>
                                    <p className="text-[10px] font-semibold uppercase tracking-[0.14em] text-muted lg:hidden">Egzemplarz</p>
                                    <p className="text-[13px] font-semibold leading-tight text-ink">{item.name}</p>
                                  </div>
                                  <div>
                                    <p className="mb-0.5 text-[10px] font-semibold uppercase tracking-[0.14em] text-muted lg:hidden">S/N</p>
                                    {renderInlineCell(item, "serialNumber")}
                                  </div>
                                  <div>
                                    <p className="mb-0.5 text-[10px] font-semibold uppercase tracking-[0.14em] text-muted lg:hidden">TAG</p>
                                    {renderInlineCell(item, "assetTag")}
                                  </div>
                                  <div>
                                    <p className="mb-0.5 text-[10px] font-semibold uppercase tracking-[0.14em] text-muted lg:hidden">Status</p>
                                    <button
                                      className="rounded-full"
                                      disabled={!canEditItems || saving || Boolean(quickSavingItemId)}
                                      onClick={() => void cycleItemStatus(item)}
                                      title={canEditItems ? "Kliknij, aby przełączyć status urządzenia" : undefined}
                                      type="button"
                                    >
                                      <Badge tone={statusToneByValue[item.status]}>{statusLabelByValue[item.status]}</Badge>
                                    </button>
                                  </div>
                                  <div>
                                    <p className="mb-0.5 text-[10px] font-semibold uppercase tracking-[0.14em] text-muted lg:hidden">Akcje</p>
                                    <div className="flex min-h-8 flex-nowrap items-center justify-start gap-1 lg:justify-end">
                                      <button
                                        className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border border-line bg-[#f7f2e9] text-[#20332b] transition hover:bg-[#efe7d9] disabled:cursor-default disabled:opacity-40"
                                        disabled={!canReorderView || siblingIndex <= 0}
                                        title="Przesuń urządzenie wyżej"
                                        onClick={() => void moveItem(item.id, -1)}
                                        type="button"
                                      >
                                        <ItemActionIcon kind="up" />
                                      </button>
                                      <button
                                        className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border border-line bg-[#f7f2e9] text-[#20332b] transition hover:bg-[#efe7d9] disabled:cursor-default disabled:opacity-40"
                                        disabled={!canReorderView || siblingIndex === -1 || siblingIndex >= siblingItems.length - 1}
                                        title="Przesuń urządzenie niżej"
                                        onClick={() => void moveItem(item.id, 1)}
                                        type="button"
                                      >
                                        <ItemActionIcon kind="down" />
                                      </button>
                                      {isAdmin ? (
                                        <button
                                          className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border border-line bg-[#f7f2e9] text-[#20332b] transition hover:bg-[#efe7d9]"
                                          onClick={async () => {
                                            if (!session || !window.confirm(`Usunąć urządzenie "${item.name}"?`)) {
                                              return;
                                            }

                                            try {
                                              await deleteItemRequest(session, item.id);
                                              await refresh();
                                            } catch (requestError) {
                                              setError(requestError instanceof Error ? requestError.message : "Nie udało się usunąć urządzenia");
                                            }
                                          }}
                                          title="Usuń urządzenie"
                                          type="button"
                                        >
                                          <ItemActionIcon kind="delete" />
                                        </button>
                                      ) : null}
                                    </div>
                                  </div>
                                </div>
                              );
                            })}
                          </div>
                        </div>
                      ) : null}
                    </div>
                  );
                })}
              </div>
            </div>
          </div>
        </Card>
      </div>

      <Modal
        open={Boolean(itemDraft)}
        onClose={() => {
          setItemDraft(null);
          setBulkImport(blankBulkImportState());
        }}
        title={itemDraft?.id ? "Edytuj grupę sprzętu" : "Dodaj grupę sprzętu"}
        footer={
          <>
            <Button
              variant="ghost"
              onClick={() => {
                setItemDraft(null);
                setBulkImport(blankBulkImportState());
              }}
            >
              Anuluj
            </Button>
            <Button
              disabled={
                saving ||
                !itemDraft?.name.trim() ||
                !itemDraft?.categoryId ||
                parsedSuggestedDayRate === null ||
                !draftQuantity ||
                quantityNeedsAdmin
              }
              onClick={() => void saveItemDraft()}
            >
              {saving ? "Zapisywanie..." : "Zapisz"}
            </Button>
          </>
        }
      >
        <div className="grid gap-4">
          <div className="grid gap-4 sm:grid-cols-2">
            <label className="block space-y-2">
              <span className="text-sm font-medium text-ink">Nazwa grupy sprzętu</span>
              <Input
                value={itemDraft?.name ?? ""}
                onChange={(event) => setItemDraft((current) => (current ? { ...current, name: event.target.value } : current))}
                placeholder='Np. Laptopy G5, Laptopy Legion'
              />
            </label>

            <label className="block space-y-2">
              <span className="text-sm font-medium text-ink">Ilość sztuk</span>
              <Input
                inputMode="numeric"
                value={itemDraft?.quantity ?? ""}
                onChange={(event) => updateDraftQuantity(event.target.value)}
                placeholder="1"
              />
            </label>

            <label className="block space-y-2 sm:col-span-2">
              <span className="text-sm font-medium text-ink">Nazwa skrócona</span>
              <Input
                value={itemDraft?.shortName ?? ""}
                onChange={(event) => setItemDraft((current) => (current ? { ...current, shortName: event.target.value } : current))}
                placeholder='Np. G5-01, LEG-12, IPP-03'
              />
              <p className="text-xs text-muted">To alternatywna, krótsza nazwa grupy używana w timeline, jeśli włączysz to poniżej.</p>
            </label>

            <label className="flex items-center gap-3 rounded-2xl border border-line bg-white px-4 py-3 sm:col-span-2">
              <input
                checked={itemDraft?.useShortNameOnTimeline ?? false}
                onChange={(event) =>
                  setItemDraft((current) =>
                    current ? { ...current, useShortNameOnTimeline: event.target.checked } : current
                  )
                }
                type="checkbox"
              />
              <span className="text-sm font-medium text-ink">Pokazuj nazwę skróconą w timeline zamiast pełnej nazwy</span>
            </label>

            <label className="block space-y-2">
              <span className="text-sm font-medium text-ink">Sugerowana stawka dzienna</span>
              <Input
                inputMode="decimal"
                value={itemDraft?.suggestedDayRate ?? ""}
                onChange={(event) =>
                  setItemDraft((current) => (current ? { ...current, suggestedDayRate: event.target.value } : current))
                }
                placeholder="Np. 150,00"
              />
            </label>

            <label className="block space-y-2">
              <span className="text-sm font-medium text-ink">Status egzemplarzy</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={itemDraft?.status ?? "active"}
                onChange={(event) =>
                  setItemDraft((current) =>
                    current ? { ...current, status: event.target.value as ItemDraft["status"] } : current
                  )
                }
              >
                <option value="active">Aktywne</option>
                <option value="service">W serwisie</option>
                <option value="retired">Wyłączone</option>
              </select>
            </label>
          </div>

          <label className="flex items-center gap-3 rounded-2xl border border-line bg-white px-4 py-3">
            <input
              checked={itemDraft?.isVisibleOnTimeline ?? true}
              onChange={(event) =>
                setItemDraft((current) =>
                  current ? { ...current, isVisibleOnTimeline: event.target.checked } : current
                )
              }
              type="checkbox"
            />
            <span className="text-sm font-medium text-ink">Widoczne na timeline</span>
          </label>

          {itemDraft && parsedSuggestedDayRate === null ? (
            <div className="rounded-2xl bg-[#f8d9d3] px-4 py-3 text-sm text-[#8d2f20]">
              Sugerowana stawka dzienna jest wymagana i musi być dodatnią liczbą, np. `150` albo `150,50`.
            </div>
          ) : null}

          {quantityNeedsAdmin ? (
            <div className="rounded-2xl bg-[#f6e1cf] px-4 py-3 text-sm text-[#8f5b1f]">
              Zmniejszanie ilości istniejącej grupy usuwa nadmiarowe egzemplarze, więc wymaga konta admina.
            </div>
          ) : null}

          <div className="rounded-2xl border border-line bg-[#fbf8f2] p-4">
            <div className="flex flex-col gap-3">
              <div>
                <p className="text-sm font-semibold text-ink">Masowy import danych dla grupy sprzętu</p>
                <p className="text-xs text-muted">
                  Edycja działa na całej grupie sprzętu. System oczekuje obu parametrów oddzielonych średnikiem, czyli w formacie
                  `SN-A;TAG-A`. Każda kolejna linia to kolejny egzemplarz. Możesz też wczytać plik `CSV` z dwiema kolumnami.
                </p>
              </div>

              <div className="grid gap-3">
                <label className="block space-y-2">
                  <span className="text-sm font-medium text-ink">Wklej pary S/N i TAG</span>
                  <Textarea
                    className="min-h-[128px]"
                    value={bulkImport.serialTagPairs}
                    onChange={(event) =>
                      setBulkImport({
                        serialTagPairs: event.target.value
                      })
                    }
                    placeholder={"SN-001;TAG-001\nSN-002;TAG-002\nSN-003;TAG-003"}
                  />
                  <p className="text-xs text-muted">System oczekuje obu parametrów oddzielonych średnikiem: `S/N;TAG`.</p>
                </label>

                <div className="flex flex-col gap-3 sm:flex-row sm:items-center">
                  <Button className="w-full sm:w-auto" variant="secondary" onClick={() => applyBulkImport(bulkImport.serialTagPairs)}>
                    Uzupełnij egzemplarze z listy
                  </Button>
                  <label className="inline-flex cursor-pointer items-center gap-3 rounded-2xl border border-line bg-white px-4 py-2.5 text-sm font-medium text-ink transition hover:bg-[#f8f3ea]">
                    <input
                      accept=".csv,.txt"
                      className="hidden"
                      type="file"
                      onChange={(event) => {
                        const file = event.target.files?.[0];

                        if (!file) {
                          return;
                        }

                        void handleBulkImportFile(file);
                        event.currentTarget.value = "";
                      }}
                    />
                    Wczytaj plik CSV
                  </label>
                </div>
              </div>
            </div>
          </div>

          <label className="block space-y-2">
            <span className="text-sm font-medium text-ink">Notatka</span>
            <Textarea
              value={itemDraft?.notes ?? ""}
              onChange={(event) => setItemDraft((current) => (current ? { ...current, notes: event.target.value } : current))}
            />
          </label>

          <div className="space-y-3 rounded-2xl border border-line bg-[#fbf8f2] p-4">
            <div className="flex items-center justify-between gap-3">
              <div>
                <p className="text-sm font-semibold text-ink">Egzemplarze w grupie</p>
                <p className="text-xs text-muted">
                  System utworzy nazwy w formacie `Nazwa grupy #01`, `Nazwa grupy #02` itd. Tutaj wpisujesz tylko `S/N` i `TAG`.
                </p>
              </div>
              <Badge tone="neutral">{draftQuantity || 0} szt.</Badge>
            </div>

            <div className="space-y-2">
              <div className="hidden grid-cols-[88px_minmax(0,1fr)_minmax(0,1fr)] gap-2 px-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-muted sm:grid">
                <div>Egzemplarz</div>
                <div>S/N</div>
                <div>TAG</div>
              </div>

              {visibleDraftUnits.map((unit, index) => (
                <div
                  key={unit.id ?? `draft-unit-${index}`}
                  className="grid gap-2 rounded-2xl border border-line bg-white px-3 py-3 sm:grid-cols-[88px_minmax(0,1fr)_minmax(0,1fr)] sm:items-center"
                >
                  <div>
                    <p className="text-[11px] font-semibold uppercase tracking-[0.16em] text-muted sm:hidden">Egzemplarz</p>
                    <p className="text-sm font-semibold text-ink">{formatUnitIndex(index)}</p>
                  </div>
                  <label className="block space-y-1 sm:space-y-0">
                    <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-muted sm:hidden">S/N</span>
                    <Input
                      value={unit.serialNumber}
                      onChange={(event) => updateDraftUnit(index, "serialNumber", event.target.value)}
                      placeholder="Numer seryjny"
                    />
                  </label>
                  <label className="block space-y-1 sm:space-y-0">
                    <span className="text-[11px] font-semibold uppercase tracking-[0.16em] text-muted sm:hidden">TAG</span>
                    <Input
                      value={unit.assetTag}
                      onChange={(event) => updateDraftUnit(index, "assetTag", event.target.value)}
                      placeholder="TAG wewnętrzny"
                    />
                  </label>
                </div>
              ))}
            </div>
          </div>
        </div>
      </Modal>
    </div>
  );
};
