"use client";

import React, {
    CSSProperties,
    ReactNode,
    useEffect,
    useMemo,
    useState,
} from "react";
import { Active, DndContext, DragEndEvent } from "@dnd-kit/core";
import { SortableContext } from "@dnd-kit/sortable";
import { LexoRank } from "lexorank";
import { Box, Group } from "@mantine/core";
import useDndSensors from "@/app/deals/hooks/useSensors";
import { SortableItem } from "@/components/SortableDnd/SortableItem";
import { SortableOverlay } from "@/components/SortableDnd/SortableOverlay";
import { getNewLexorank, sortByLexorank } from "@/utils/lexorank";

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

type Props<T extends BaseItem> = {
    initialItems: T[];
    renderItem: (item: T) => ReactNode;
    onDragEnd: (itemId: number, newLexorank: string) => void;
    onItemClick: (item: T) => void;
    rowStyle?: CSSProperties;
    itemStyle?: CSSProperties;
};

const SortableDnd = <T extends BaseItem>({
    initialItems,
    renderItem,
    onDragEnd,
    onItemClick,
    rowStyle,
    itemStyle,
}: 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
            sensors={sensors}
            onDragStart={({ active }) => setActive(active)}
            onDragEnd={onDragEndLocal}
            onDragCancel={() => setActive(null)}>
            <SortableContext items={items}>
                <Group
                    gap={0}
                    style={rowStyle}
                    role="application">
                    {items.map((item, index) => (
                        <Box
                            key={index}
                            onClick={e => {
                                e.preventDefault();
                                e.stopPropagation();
                                onItemClick(item);
                            }}>
                            <SortableItem
                                dragHandleStyle={{ cursor: "pointer" }}
                                itemStyle={itemStyle}
                                id={item.id}>
                                {renderItem(item)}
                            </SortableItem>
                        </Box>
                    ))}
                </Group>
            </SortableContext>
            <SortableOverlay>
                <div style={{ cursor: "grabbing" }}>
                    {activeItem ? renderItem(activeItem) : null}
                </div>
            </SortableOverlay>
        </DndContext>
    );
};

export default SortableDnd;
