import { ref, watch } from 'vue';
import type { Ref } from 'vue';
import { useSortable } from '@admin-theme/lib/useSortable';
import type { SortableRowProps } from '@admin-theme/lib/useSortable';

/**
 * Variante de `useSortable` pour une `SimpleTable`, dont les lignes ne
 * sont pas exposées : la poignée (`DragHandle`) porte `handleProps(index)`,
 * le conteneur qui enveloppe la table porte `containerProps` et retrouve
 * la ligne survolée par délégation (`tbody > tr`). La ligne cible reçoit
 * la classe `bg-muted` le temps du survol.
 *
 * ```ts
 * const { containerRef, containerProps, handleProps } = useSortableTable(
 *     (from, to) => (items.value = moveItem(items.value, from, to)),
 * );
 * <div ref="containerRef" v-bind="containerProps"><SimpleTable …>
 *     <template #cell-drag="{ row }"><span v-bind="handleProps(indexOf(row))"><DragHandle /></span></template>
 * ```
 */
export type SortableHandleProps = Pick<
    SortableRowProps,
    'draggable' | 'onDragstart' | 'onDragend'
>;

export type SortableContainerProps = Pick<
    SortableRowProps,
    'onDragover' | 'onDragenter' | 'onDrop'
>;

const OVER_CLASS = 'bg-muted';

export function useSortableTable(onMove: (from: number, to: number) => void): {
    containerRef: Ref<HTMLElement | null>;
    containerProps: SortableContainerProps;
    dragging: Ref<number | null>;
    over: Ref<number | null>;
    handleProps: (index: number) => SortableHandleProps;
} {
    const { dragging, over, rowProps } = useSortable(onMove);
    const containerRef = ref<HTMLElement | null>(null);

    const rowIndexOf = (event: DragEvent): number | null => {
        const target = event.target;

        if (!(target instanceof Element)) {
            return null;
        }

        const row = target.closest('tbody > tr');

        if (!(row instanceof HTMLTableRowElement)) {
            return null;
        }

        if (!containerRef.value?.contains(row)) {
            return null;
        }

        return row.sectionRowIndex;
    };

    const delegate =
        (key: 'onDragover' | 'onDragenter' | 'onDrop') =>
        (event: DragEvent): void => {
            const index = rowIndexOf(event);

            if (index === null) {
                return;
            }

            rowProps(index)[key](event);
        };

    const containerProps: SortableContainerProps = {
        onDragover: delegate('onDragover'),
        onDragenter: delegate('onDragenter'),
        onDrop: delegate('onDrop'),
    };

    const handleProps = (index: number): SortableHandleProps => {
        const props = rowProps(index);

        return {
            draggable: props.draggable,
            onDragstart: props.onDragstart,
            onDragend: props.onDragend,
        };
    };

    watch(over, (index) => {
        const rows = containerRef.value?.querySelectorAll('tbody > tr') ?? [];

        rows.forEach((row, rowIndex) => {
            row.classList.toggle(
                OVER_CLASS,
                index !== null &&
                    rowIndex === index &&
                    dragging.value !== index,
            );
        });
    });

    return { containerRef, containerProps, dragging, over, handleProps };
}
