import {closestCenter, DndContext, PointerSensor, useSensor, useSensors,} from "@dnd-kit/core";
import {arrayMove, SortableContext, verticalListSortingStrategy,} from "@dnd-kit/sortable";
import type {InstructionStepModel} from "../../models/InstructionStepModel";
import {InstructionStepEditorDesktopListItem} from "./InstructionStepEditorDesktopListItem.tsx";
import Button from "../basics/Button";
import {Plus} from "lucide-react";
import {ButtonType} from "../basics/BasicButtonDefinitions";
import {instructionStepListEditorMethods} from "./InstructionStepListEditorMethods.ts";

type InstructionStepListDesktopEditorProps = {
    instructionStepList: InstructionStepModel[];
    onChange: (steps: InstructionStepModel[]) => void;
};

/**
 * Desktop editor using drag-and-drop via @dnd-kit
 * @param instructionStepList List instruction step models
 * @param onChange Method to call on any change to the list
 * @constructor
 */
export function InstructionStepListDesktopEditor({
                                                     instructionStepList,
                                                     onChange,
                                                 }: InstructionStepListDesktopEditorProps) {
    /* Import methods for handling update of instruction steps as well as adding and removing steps.
     * Those methods are shared by all versions of the instruction step editor, e.g., mobile and desktop.
     */
    const {handleUpdate, handleAdd, handleRemove} = instructionStepListEditorMethods(
        instructionStepList,
        onChange
    );

    const sensors = useSensors(useSensor(PointerSensor, {activationConstraint: {distance: 8}}));

    /**
     * Handle the drag end event and reorder list
     */
    const handleDragEnd = (event: any) => {
        const {active, over} = event;
        if (active.id !== over.id) {
            /* find element by internal id
             * Caution! Due to new elements not having a real ID yet, each list item has an internal ID
             * in order to give it a unique identifier before saving it to the backend. We have to compare
             * the id of the drag item to the internalId of the list item here!
             */
            const oldIndex = instructionStepList.findIndex((i) => i.internalId === active.id);
            const newIndex = instructionStepList.findIndex((i) => i.internalId === over.id);
            // move element to new position
            const newOrder = arrayMove(instructionStepList, oldIndex, newIndex);
            onChange(newOrder);
        }
    };

    return (
        <div>
            <h2>Zubereitung</h2>
            <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
                <SortableContext
                    items={instructionStepList.map((i) => i.internalId)}
                    strategy={verticalListSortingStrategy}
                >
                    <div className="flex flex-col gap-3">
                        {instructionStepList.map((step, index) => (
                            <InstructionStepEditorDesktopListItem
                                key={step.internalId}
                                id={step.internalId}
                                index={index}
                                step={step}
                                onUpdate={handleUpdate}
                                onRemove={handleRemove}
                            />
                        ))}
                    </div>
                </SortableContext>
            </DndContext>
            <Button
                onClick={handleAdd}
                icon={Plus}
                text="Schritt hinzufügen"
                buttonType={ButtonType.PrimaryButton}
                className="mt-4"
            />
        </div>
    );
}
