import {
    useCallback,
    useEffect,
    useMemo,
    useState,
} from 'react';

import _ from 'lodash';
import {
    Loader2,
    PencilRuler,
} from 'lucide-react';
import { swap } from 'react-grid-dnd';
import {
    LuMoveLeft,
    LuMoveRight,
    LuPlus,
    LuTrash,
    LuUpload,
} from 'react-icons/lu';

import { rpc } from '@/api/request';
import { StructureType } from '@/api/types';
import {
    Tooltip,
    TooltipContent,
    TooltipTrigger,
} from '@/components/ui/tooltip';
import { imageEditorRef } from '@/componentsRoot/ImageEditor/ImageEditor';
import { toast } from '@/componentsRoot/ToastRoot';
import { Lang } from '@/lang/Lang';

import { getImageUrl } from '../FileData/FileDataImage/imageHelpers';
import { selectFile } from '../FileData/utils/selectFile';
import DataDescription from '../components/DataDescription';
import DataLabel from '../components/DataLabel';
import { ItemDataProps } from '../types';

type ArrayImagesItemProps = {
    structure?: StructureType,
    value: any,
    onDelete?: () => void,
    onMoveRight?: () => void,
    onMoveLeft?: () => void,
    onEdit?: () => void,
    onUpload?: () => void,

}


const ArrayImagesItem = ({ structure, value, onDelete, onMoveRight, onMoveLeft, onEdit, onUpload }: ArrayImagesItemProps) => {



    if (!structure) {
        return <></>;
    }

    const url = useMemo(() => {
        if (structure.type == "fileImage" || structure.type == "fileImageID") {
            return value?.url_small ?? value?.url_medium ?? value?.url;
        }
        if (structure.type == "fileImageLink") {
            return value;
        }
        return null;
    }, [value]);


    const FabBtn = ({ children, className, onClick, tooltip }: { children: React.ReactNode, className?: string, onClick?: () => void, tooltip?: string }) => {

        return <Tooltip>
            <TooltipTrigger asChild>
                <div className={` absolute cursor-pointer opacity-50 hover:opacity-100 rounded-xs p-1 text-white ${className}`} onClick={onClick}>
                    {children}
                </div>
            </TooltipTrigger>
            <TooltipContent>
                {tooltip}
            </TooltipContent>
        </Tooltip>
    }

    return <div className='w-full h-20 border border-gray-200 rounded-md bg-gray-200 overflow-hidden relative'>
        <div

            className='w-full h-20'
            style={{
                backgroundImage: `url(${url})`,
                backgroundSize: 'cover',
                backgroundPosition: 'center',
                backgroundRepeat: 'no-repeat',
            }}></div>

        {!!onDelete && <FabBtn

            tooltip={Lang.key('delete_image')}
            className='top-0 right-0 bg-red-600' onClick={onDelete}>
            <LuTrash className='size-3' />
        </FabBtn>}



        {!!onMoveRight && <FabBtn
            tooltip={Lang.key('move_right')}
            className='bottom-0 right-0 bg-gray-600' onClick={onMoveRight}>
            <LuMoveRight


                className='size-3' />
        </FabBtn>}



        {!!onUpload && <FabBtn
            tooltip={Lang.key('upload_image')}
            className='bottom-0 right-2/3  bg-gray-600' onClick={onUpload}>
            <LuUpload


                className='size-3' />
        </FabBtn>}


        {!!onEdit && <FabBtn
            tooltip={Lang.key('edit_image')}
            className='bottom-0 left-2/3  bg-gray-600' onClick={onEdit}>
            <PencilRuler


                className='size-3' />
        </FabBtn>}




        {!!onMoveLeft && <FabBtn

            tooltip={Lang.key('move_left')}
            className='bottom-0 left-0 bg-gray-600' onClick={onMoveLeft}>
            <LuMoveLeft className='size-3' />
        </FabBtn>}

    </div>
}



const ArrayImagesGridItem = ({ onClick, loading, plus = false, disabled = false }: { onClick?: () => void, loading?: boolean, plus?: boolean, disabled?: boolean }) => {
    return <div

        style={{
            opacity: disabled ? 0.5 : 1,
            cursor: disabled ? 'not-allowed' : 'pointer',
        }}
        onClick={() => {
            onClick?.();
        }}
        className='w-full h-20 border border-gray-200 rounded-md bg-gray-200 flex items-center justify-center cursor-pointer hover:bg-gray-100 transition-all duration-300'>
        {plus && <LuPlus className='size-5' />}
        {loading && <Loader2 className='size-5 animate-spin' />}
    </div>
}


const selectImageFile = (accept?: string): Promise<File[] | null> => {
    return new Promise((resolve) => {
        const input = document.createElement('input');
        input.type = 'file';
        input.accept = !!accept ? accept : 'image/*';
        input.style.display = 'none';
        input.multiple = true;

        input.addEventListener('change', () => {
            const files = [];
            for (const file of input.files ?? []) {
                files.push(file);
            }
            input.remove();
            resolve(files);
        });
        input.onerror = () => {
            resolve(null);
        };
        input.onabort = () => {
            resolve(null);
        };

        document.body.appendChild(input);
        input.click();
    });
}


const ArrayImages = (props: ItemDataProps) => {

    const onlyShow = props.onlyShow || !props.structure.editable;

    const [localValue, setLocalValue] = useState(props.getValue() ?? []);

    useEffect(() => {
        props.setValue(localValue);
    }, [localValue]);

    const [pendingUploads, setPendingUploads] = useState<File[]>([]);
    const [replacingIndex, setReplacingIndex] = useState<number | null>(null);

    const itemType = props.structure.itemType?.type ?? 'fileImage';
    const isUploading = pendingUploads.length > 0;
    const isBusy = isUploading || replacingIndex !== null;

    const mapUploadResponse = useCallback((json: { url?: string } & Record<string, unknown>) => {
        return itemType === 'fileImageLink' ? (json.url ?? '') : json;
    }, [itemType]);

    const uploadAndMap = useCallback(async (file: File | string) => {
        const formData = new FormData();
        formData.append('file', file);
        try {
            const json = await rpc.imageUpload(formData);
            if (json) {
                return mapUploadResponse(json);
            }
            return null;
        } catch {
            toast.error(Lang.key('error_uploading_image'));
            return null;
        }
    }, [mapUploadResponse]);

    const replaceItemAt = useCallback((index: number, item: unknown) => {
        setLocalValue((prev: unknown[]) => {
            const next = [...prev];
            next[index] = item;
            return next;
        });
    }, []);

    const uploadFilesSequentially = useCallback(async (files: File[]) => {
        setPendingUploads(files);
        for (const file of files) {
            const item = await uploadAndMap(file);
            if (item != null) {
                setLocalValue((prev: unknown[]) => [...prev, item]);
            }
            setPendingUploads((prev) => prev.slice(1));
        }
    }, [uploadAndMap]);

    const replaceImageAt = useCallback(async (index: number, file: File | string) => {
        setReplacingIndex(index);
        try {
            const item = await uploadAndMap(file);
            if (item != null) {
                replaceItemAt(index, item);
            }
        } finally {
            setReplacingIndex(null);
        }
    }, [uploadAndMap, replaceItemAt]);

    if (onlyShow) {
        return <div>
            <DataLabel {...props} />
            <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:grid-cols-8 gap-2">
                {_.map(localValue, (item, index) => (
                    <ArrayImagesItem
                        key={item?.uuid ?? item?._id ?? index}
                        structure={props.structure.itemType}
                        value={item}
                    />
                ))}
            </div>
            <DataDescription {...props} />
        </div>
    }

    return <div>


        <DataLabel {...props} />

        <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:grid-cols-8 gap-2">

            {_.map(localValue, (item, index) => {
                if (replacingIndex === index) {
                    return (
                        <ArrayImagesGridItem
                            key={item?.uuid ?? item?._id ?? index}
                            loading
                        />
                    );
                }

                return (
                    <ArrayImagesItem
                        onMoveRight={() => {
                            if (isBusy) return;
                            setLocalValue((prev: unknown[]) => swap(prev, index, index + 1));
                        }}
                        onMoveLeft={() => {
                            if (isBusy) return;
                            setLocalValue((prev: unknown[]) => swap(prev, index, index - 1));
                        }}
                        onDelete={() => {
                            if (isBusy) return;
                            setLocalValue((prev: unknown[]) => prev.filter((_, i) => i !== index));
                        }}
                        onEdit={() => {
                            if (isBusy) return;
                            const imageUrl = getImageUrl(item, itemType);
                            if (!imageUrl) return;
                            imageEditorRef.current?.open({
                                imageUrl,
                                onSave: (base64) => {
                                    if (!base64) return;
                                    void replaceImageAt(index, base64);
                                },
                            });
                        }}
                        onUpload={() => {
                            if (isBusy) return;
                            selectFile('image/*').then((file) => {
                                if (file) {
                                    void replaceImageAt(index, file);
                                }
                            });
                        }}
                        key={item?.uuid ?? item?._id ?? index}
                        structure={props.structure.itemType}
                        value={item}
                    />
                );
            })}
            {_.map(pendingUploads, (file, index) => (
                <ArrayImagesGridItem
                    key={`${file.name}-${file.lastModified}-${file.size}-${index}`}
                    loading
                />
            ))}
            <ArrayImagesGridItem
                plus
                disabled={isBusy}
                onClick={() => {
                    if (isBusy) return;
                    selectImageFile().then((files) => {
                        if (files?.length) {
                            uploadFilesSequentially(files);
                        }
                    });
                }}
            />



        </div>
        <DataDescription {...props} />



    </div>
}

export default ArrayImages;