// React
import React, { FC, useCallback, useState } from 'react';
import ReactDOM from 'react-dom';

// Components
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { Tooltip, IconButton } from '..';

// Utils
import { v4 as uuidv4 } from 'uuid';
import cx from 'classnames';

//Styles
import './styles.scss';

// Types
import { iDragAndDrop } from './types';

// Hooks
import { useTheme } from '../../provider';

// ** Create Portal element to avoid draggable conflicts with modals
const DraggablePortalItem = ({
    provided,
    snapshot,
    item,
    keyItem,
    keyContainer,
    itemRenderer,
    container,
    containers,
    onDragEnd
}: any) => {
    const [theme] = useTheme();
    const usePortal = snapshot.isDragging;

    const child = (
        <div
            ref={provided.innerRef}
            {...provided.draggableProps}
            {...provided.dragHandleProps}
            // ** These inline styles are mandatory to avoid them being overwritten. Don't put them on .scss styles
            style={{
                ...provided.draggableProps.style,
                display: 'flex',
                flexDirection: 'row'
            }}
            className={cx(
                'gtc-drag-and-drop-item',
                `gtc-drag-and-drop-item-${snapshot.isDragging ? 'dragging' : 'dragged'}-${theme}`
            )}
        >
            {itemRenderer(item, keyContainer, keyItem)}

            {container?.isDeletable && (
                <div
                    style={{
                        margin: 5
                    }}
                >
                    <Tooltip title={'Delete'}>
                        <IconButton
                            onClick={() => {
                                const newContainers: any = [...containers];
                                newContainers[keyContainer].items.splice(keyItem, 1);
                                if (onDragEnd) {
                                    onDragEnd(newContainers);
                                }
                            }}
                            variant="warning"
                            size="xsmall"
                            icon="close"
                            isDisabled={snapshot.isDragging}
                        />
                    </Tooltip>
                </div>
            )}
        </div>
    );
    // ** If it's not neccessary, don't create the portal
    if (!usePortal) {
        return child;
    }
    return ReactDOM.createPortal(child, document.body);
};

export const DragAndDrop: FC<iDragAndDrop> = ({ containers = [], onDragEnd, itemRenderer }) => {
    const [theme] = useTheme();

    const reorder = useCallback(
        (sInd: number, startIndex: number, endIndex: number) => {
            const result = Array.from(containers[sInd]?.items || []);
            const [removed] = result.splice(startIndex, 1);
            result.splice(endIndex, 0, removed);
            return result;
        },
        [containers]
    );

    const move = useCallback(
        (sInd: any, dInd: any, droppableSource: any, droppableDestination: any) => {
            const cloneItems = containers[sInd]?.cloneItems ? true : false;
            const source = containers[sInd]?.items || [];
            const destination = containers[dInd]?.items || [];
            const sourceClone = [...source];
            const destClone = [...destination];

            let item: any = [];
            if (cloneItems) {
                const id = uuidv4();
                item = {
                    ...sourceClone[droppableSource.index],
                    id: id,
                    key: id,
                    new: true
                };
            } else {
                item = { ...sourceClone.splice(droppableSource.index, 1) };
            }

            destClone.splice(droppableDestination.index, 0, item);

            const result: any = {};
            result[droppableSource.droppableId] = sourceClone;
            result[droppableDestination.droppableId] = destClone;

            return result;
        },
        [containers]
    );

    const getContainerTitle = (container: any) => {
        if (!container?.title) return '';

        if (typeof container?.title === 'string') {
            return (
                <div
                    className={cx('gtc-drag-and-drop-title', container?.titleClassName || '')}
                    style={container?.titleStyle || {}}
                >
                    {container?.title}
                </div>
            );
        }

        return container?.title;
    };

    const getContainerFooter = (container: any) => {
        if (!container?.footer) return '';

        if (typeof container?.footer === 'string') {
            return (
                <div
                    className={cx('gtc-drag-and-drop-footer', container?.footerClassName || '')}
                    style={container?.footerStyle || {}}
                >
                    {container?.footer}
                </div>
            );
        }

        return container?.footer;
    };

    return (
        <div className="gtc-drag-and-drop-context">
            <DragDropContext
                onDragEnd={(result: any) => {
                    const { source, destination } = result;
                    // ** dropped outside the list
                    if (!destination) return;

                    const sInd = +source.droppableId;
                    const dInd = +destination.droppableId;

                    let newContainers: any = [];

                    if (sInd === dInd) {
                        if (containers[sInd]) {
                            if (containers[sInd]?.cloneItems) return;

                            const items = reorder(sInd, source.index, destination.index);
                            newContainers = [...containers];
                            newContainers[sInd].items = items;
                        }
                    } else {
                        if (containers[sInd] && containers[dInd]) {
                            if (containers[dInd]?.cloneItems) return;

                            const result: any = move(sInd, dInd, source, destination);
                            newContainers = [...containers];
                            newContainers[sInd].items = result[sInd];
                            newContainers[dInd].items = result[dInd];
                        }
                    }

                    if (onDragEnd) {
                        onDragEnd(newContainers);
                    }
                }}
            >
                {containers?.map((container: any, keyContainer: number) => {
                    let direction = container?.direction || 'vertical';
                    return (
                        <Droppable key={keyContainer} droppableId={`${keyContainer}`}>
                            {(provided, snapshot) => (
                                <div
                                    ref={provided.innerRef}
                                    {...provided.droppableProps}
                                    className={cx(
                                        'gtc-drag-and-drop-container',
                                        `gtc-drag-and-drop-container-${direction}`,
                                        `gtc-drag-and-drop-container-${
                                            snapshot.isDraggingOver ? 'dragging' : 'dragged'
                                        }-${theme}`,
                                        container?.className || ''
                                    )}
                                    style={{
                                        ...(container?.style || {}),
                                        position: 'relative !important'
                                    }}
                                >
                                    {getContainerTitle(container)}

                                    {container?.items?.map((item: any, keyItem: number) => {
                                        return (
                                            <Draggable
                                                key={item.id || item.key}
                                                draggableId={item.id || item.key}
                                                index={keyItem}
                                                isDragDisabled={
                                                    container?.isDragDisabled ||
                                                    item?.isDragDisabled
                                                        ? true
                                                        : false
                                                }
                                            >
                                                {(provided, snapshot) => (
                                                    <DraggablePortalItem
                                                        provided={provided}
                                                        snapshot={snapshot}
                                                        item={item}
                                                        keyItem={keyItem}
                                                        keyContainer={keyContainer}
                                                        itemRenderer={itemRenderer}
                                                        container={container}
                                                        containers={containers}
                                                        onDragEnd={onDragEnd}
                                                    />
                                                )}
                                            </Draggable>
                                        );
                                    })}

                                    {getContainerFooter(container)}
                                    {provided.placeholder}
                                </div>
                            )}
                        </Droppable>
                    );
                })}
            </DragDropContext>
        </div>
    );
};
