import { nanoid } from 'nanoid';
import { Dispatch } from 'preact/hooks';
import { reorderElements } from '../shared/collections.js';
import { Coordinates } from '../shared/types';
import { Marker, ReducerAction, ReorderMarkersParams } from './types';

const reindexMarkers = (markers: Marker[]) =>
    markers.map((marker, i) => {
        const newLabel = `${i + 1}`;
        if (marker.label === newLabel) {
            return marker;
        }

        return {
            ...marker,
            label: newLabel,
        };
    });

export const markersReducer = (
    markers: Marker[],
    action: ReducerAction,
): Marker[] => {
    const type = action.type;
    switch (action.type) {
        case 'add': {
            const key = nanoid(10);
            return reindexMarkers([
                ...markers,
                {
                    key,
                    label: 'placeholder',
                    remove: () => action.data.remove(key),
                    moveUp: () => action.data.moveUp(key),
                    moveDown: () => action.data.moveDown(key),
                    coordinates: action.data.coordinates,
                },
            ]);
        }
        case 'remove': {
            return reindexMarkers([
                ...markers.filter(({ key }) => key !== action.data.key),
            ]);
        }
        case 'moveUp': {
            const oldIndex = markers.findIndex(
                ({ key }) => key === action.data.key,
            );
            if (oldIndex < 1) {
                return markers;
            }

            return reindexMarkers(
                reorderElements(markers, oldIndex, oldIndex - 1),
            );
        }
        case 'moveDown': {
            const oldIndex = markers.findIndex(
                ({ key }) => key === action.data.key,
            );
            if (oldIndex < 0 || oldIndex + 1 >= markers.length) {
                return markers;
            }

            return reindexMarkers(
                reorderElements(markers, oldIndex, oldIndex + 1),
            );
        }
        case 'reorder': {
            return reindexMarkers(
                reorderElements(
                    markers,
                    action.data.oldIndex,
                    action.data.newIndex,
                ),
            );
        }
        default:
            throw new Error(`Unsupported type ${type}`);
    }
};

export const createChangeStateMethods = (
    dispatchMarkers: Dispatch<ReducerAction>,
) => {
    const reorderMarkers = ({
        oldIndex,
        newIndex,
    }: ReorderMarkersParams): void =>
        dispatchMarkers({
            type: 'reorder',
            data: { oldIndex, newIndex },
        });

    const moveMarkerUp = (key: string) =>
        dispatchMarkers({
            type: 'moveUp',
            data: { key },
        });

    const moveMarkerDown = (key: string) =>
        dispatchMarkers({
            type: 'moveDown',
            data: { key },
        });

    const removeMarker = (key: string) =>
        dispatchMarkers({
            type: 'remove',
            data: { key },
        });

    const addMarker = (coordinates: Coordinates) =>
        dispatchMarkers({
            type: 'add',
            data: {
                coordinates,
                moveUp: moveMarkerUp,
                moveDown: moveMarkerDown,
                remove: removeMarker,
            },
        });

    return {
        reorderMarkers,
        moveMarkerUp,
        moveMarkerDown,
        removeMarker,
        addMarker,
    };
};
