"use client";

import React, {
    CSSProperties,
    ReactNode,
    useEffect,
    useMemo,
    useState,
} from "react";
import { Active, DndContext, DragEndEvent } from "@dnd-kit/core";
import { restrictToHorizontalAxis } from "@dnd-kit/modifiers";
import { SortableContext } from "@dnd-kit/sortable";
import { LexoRank } from "lexorank";
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 { getNewLexorank, sortByLexorank } from "@/utils/lexorank";

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;
};

const SortableDnd = <T extends BaseItem>({
    initialItems,
    renderItem,
    renderDraggable,
    dragHandleStyle,
    onDragEnd,
    onItemClick,
    containerStyle,
    vertical,
    disabled = 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(sortByLexorank(initialItems));
    }, [initialItems]);

    const sensors = useDndSensors();

    const onDragEndLocal = ({ active, over }: DragEndEvent) => {
        if (over && active.id !== over?.id && activeItem) {
            const overIndex: number = items.findIndex(
                ({ id }) => id === over.id
            );
            const activeIndex: number = items.findIndex(
                ({ id }) => id === activeItem.id
            );

            let leftIndex = overIndex;
            let rightIndex = overIndex + 1;
            if (overIndex < activeIndex) {
                leftIndex = overIndex - 1;
                rightIndex = overIndex;
            }

            const leftLexorank: LexoRank | null =
                leftIndex >= 0
                    ? LexoRank.parse(items[leftIndex].lexorank)
                    : null;
            const rightLexorank: LexoRank | null =
                rightIndex < items.length
                    ? LexoRank.parse(items[rightIndex].lexorank)
                    : null;

            const newLexorank = getNewLexorank(
                leftLexorank,
                rightLexorank
            ).toString();

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

    return (
        <DndContext
            modifiers={[restrictToHorizontalAxis]}
            sensors={sensors}
            onDragStart={({ active }) => setActive(active)}
            onDragEnd={onDragEndLocal}
            onDragCancel={() => setActive(null)}>
            <SortableContext
                items={items}
                disabled={disabled}>
                <Flex
                    gap={0}
                    style={{
                        flexWrap: "nowrap",
                        flexDirection: vertical ? "column" : "row",
                        ...containerStyle,
                    }}
                    role="application">
                    {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>
            </SortableContext>
            <SortableOverlay>
                {activeItem ? renderItem(activeItem, renderDraggable) : null}
            </SortableOverlay>
        </DndContext>
    );
};

export default SortableDnd;
