"use client";

import React, {
    CSSProperties,
    ReactNode,
    useEffect,
    useMemo,
    useState,
} from "react";
import { Active, DndContext, DragEndEvent } from "@dnd-kit/core";
import {
    restrictToHorizontalAxis,
    restrictToVerticalAxis,
} from "@dnd-kit/modifiers";
import { SortableContext } from "@dnd-kit/sortable";
import { FreeMode, Mousewheel, Scrollbar } from "swiper/modules";
import { Swiper, SwiperSlide } from "swiper/react";
import { Box, Flex } from "@mantine/core";
import useDndSensors from "@/app/deals/hooks/useSensors";
import { SortableOverlay } from "@/components/dnd/SortableDnd/SortableOverlay";
import SortableItem from "@/components/dnd/SortableItem";
import { getNewDndLexorank } from "@/utils/lexorank/generation";
import { sortByLexorank } from "@/utils/lexorank/sort";
import classes from "./SortableDnd.module.css";

type BaseItem = {
    id: number;
    lexorank: string;
};

type Props<T extends BaseItem> = {
    initialItems: T[];
    renderItem: (
        item: T,
        renderDraggable?: (item: T) => ReactNode
    ) => ReactNode;
    renderDraggable?: (item: T) => ReactNode; // if not passed - the whole item renders as draggable
    dragHandleStyle?: CSSProperties;
    onDragEnd: (itemId: number, newLexorank: string) => void;
    onItemClick?: (item: T) => void;
    containerStyle?: CSSProperties;
    vertical?: boolean;
    disabled?: boolean;
    swiperEnabled?: boolean;
};

const SortableDnd = <T extends BaseItem>({
    initialItems,
    renderItem,
    renderDraggable,
    dragHandleStyle,
    onDragEnd,
    onItemClick,
    containerStyle,
    vertical = false,
    disabled = false,
    swiperEnabled = false,
}: Props<T>) => {
    const [active, setActive] = useState<Active | null>(null);
    const [items, setItems] = useState<T[]>([]);
    const activeItem = useMemo(
        () => initialItems.find(item => item.id === active?.id),
        [active, items]
    );

    useEffect(() => {
        setItems(initialItems);
    }, [initialItems]);

    const sensors = useDndSensors();

    const onDragEndLocal = ({ active, over }: DragEndEvent) => {
        if (!over || active.id === over?.id || !activeItem) {
            setActive(null);
            return;
        }

        const overIndex: number = items.findIndex(({ id }) => id === over.id);
        const activeIndex: number = items.findIndex(
            ({ id }) => id === activeItem.id
        );

        const newLexorank = getNewDndLexorank(
            activeIndex,
            overIndex,
            items,
            "lexorank"
        );

        items[activeIndex].lexorank = newLexorank;
        onDragEnd(items[activeIndex].id, newLexorank);
        const sortedItems = sortByLexorank(items);
        setItems([...sortedItems]);
        setActive(null);
    };

    const renderWithSwiper = () => (
        <Swiper
            modules={[Scrollbar, Mousewheel, FreeMode]}
            spaceBetween={15}
            slidesPerView={"auto"}
            scrollbar={{ hide: false }}
            mousewheel={{
                enabled: true,
                sensitivity: 0.2,
            }}
            className={classes["swiper-container"]}
            style={containerStyle}
            direction={vertical ? "vertical" : "horizontal"}
            freeMode={{ enabled: true }}
            grabCursor>
            {items.map((item, index) => (
                <SwiperSlide
                    style={{ width: "fit-content" }}
                    key={index}
                    onClick={e => {
                        if (!onItemClick) return;
                        e.stopPropagation();
                        onItemClick(item);
                    }}>
                    <SortableItem
                        id={item.id}
                        disabled={disabled}
                        renderItem={renderDraggable =>
                            renderItem(item, renderDraggable)
                        }
                        renderDraggable={
                            renderDraggable
                                ? () => renderDraggable(item)
                                : undefined
                        }
                        dragHandleStyle={dragHandleStyle}
                    />
                </SwiperSlide>
            ))}
        </Swiper>
    );

    const renderWithFlex = () => (
        <Flex
            style={{
                gap: 0,
                flexWrap: "nowrap",
                flexDirection: vertical ? "column" : "row",
                ...containerStyle,
            }}>
            {items.map((item, index) => (
                <Box
                    key={index}
                    onClick={e => {
                        if (!onItemClick) return;
                        e.stopPropagation();
                        onItemClick(item);
                    }}>
                    <SortableItem
                        id={item.id}
                        disabled={disabled}
                        renderItem={renderDraggable =>
                            renderItem(item, renderDraggable)
                        }
                        renderDraggable={
                            renderDraggable
                                ? () => renderDraggable(item)
                                : undefined
                        }
                        dragHandleStyle={dragHandleStyle}
                    />
                </Box>
            ))}
        </Flex>
    );

    const restrictModifier = vertical
        ? restrictToVerticalAxis
        : restrictToHorizontalAxis;

    return (
        <DndContext
            modifiers={[restrictModifier]}
            sensors={sensors}
            onDragStart={({ active }) => setActive(active)}
            onDragEnd={onDragEndLocal}
            onDragCancel={() => setActive(null)}>
            <SortableContext
                items={items}
                disabled={disabled}>
                {swiperEnabled ? renderWithSwiper() : renderWithFlex()}
            </SortableContext>
            <SortableOverlay>
                {activeItem ? renderItem(activeItem, renderDraggable) : null}
            </SortableOverlay>
        </DndContext>
    );
};

export default SortableDnd;
