import Button from "../layout/Button";
import Stack from "../layout/Stack";
import { ImagePlus, X } from "lucide-react";
import React, { DetailedHTMLProps } from "react";
import Card from "../elements/Card";
import Span from "../layout/Span";
import Center from "../layout/Center";
import imageInputToBase64, {
    ImageInputToBase64FunctionReturn,
} from "../utils/form/imageInputToBase64";
import { twMerge } from "tailwind-merge";
import Tag from "../elements/Tag";
import Input from "./Input";
import Row from "../layout/Row";

type ImageUploadProps = DetailedHTMLProps<
    React.HTMLAttributes<HTMLDivElement>,
    HTMLDivElement
> & {
    onChangeHandler?: (
        imgData: ImageInputToBase64FunctionReturn | undefined,
    ) => any;
    fileInputProps?: DetailedHTMLProps<
        React.InputHTMLAttributes<HTMLInputElement>,
        HTMLInputElement
    >;
    placeHolderWrapper?: DetailedHTMLProps<
        React.HTMLAttributes<HTMLDivElement>,
        HTMLDivElement
    >;
    previewImageWrapperProps?: DetailedHTMLProps<
        React.HTMLAttributes<HTMLDivElement>,
        HTMLDivElement
    >;
    previewImageProps?: DetailedHTMLProps<
        React.ImgHTMLAttributes<HTMLImageElement>,
        HTMLImageElement
    >;
    label?: string;
    disablePreview?: boolean;
    multiple?: boolean;
    existingImageUrl?: string;
    externalSetImage?: React.Dispatch<
        React.SetStateAction<ImageInputToBase64FunctionReturn | undefined>
    >;
    externalSetImages?: React.Dispatch<
        React.SetStateAction<ImageInputToBase64FunctionReturn[] | undefined>
    >;
    setLoading?: React.Dispatch<React.SetStateAction<boolean>>;
    setImgURL?: React.Dispatch<React.SetStateAction<string | undefined>>;
    externalImage?: ImageInputToBase64FunctionReturn;
    restoreImageFn?: () => void;
};

/**
 * @note use the `onChangeHandler` prop to grab the parsed base64 image object
 */
export default function ImageUpload({
    onChangeHandler,
    fileInputProps,
    placeHolderWrapper,
    previewImageWrapperProps,
    previewImageProps,
    label,
    disablePreview,
    existingImageUrl,
    externalSetImage,
    externalSetImages,
    externalImage,
    multiple,
    restoreImageFn,
    setLoading,
    setImgURL,
    ...props
}: ImageUploadProps) {
    const [imageObject, setImageObject] = React.useState<
        ImageInputToBase64FunctionReturn | undefined
    >(externalImage);
    const [src, setSrc] = React.useState<string | undefined>(existingImageUrl);
    const inputRef = React.useRef<HTMLInputElement>(null);
    const imageUrlRef = React.useRef("");

    React.useEffect(() => {
        setImgURL?.(src);
    }, [src]);

    React.useEffect(() => {
        if (existingImageUrl) setSrc(existingImageUrl);
    }, [existingImageUrl]);

    return (
        <Stack
            {...props}
            className={twMerge(
                "w-full h-[300px] overflow-hidden",
                props?.className,
            )}
        >
            <input
                type="file"
                className={twMerge("hidden", fileInputProps?.className)}
                multiple={multiple}
                accept="image/*"
                {...fileInputProps}
                onChange={(e) => {
                    setLoading?.(true);

                    if (multiple) {
                        (async () => {
                            const files = e.target.files;
                            if (!files?.[0]) return;

                            let imgArr: ImageInputToBase64FunctionReturn[] = [];

                            for (let i = 0; i < files.length; i++) {
                                const file = files[i];
                                const fileObj = await imageInputToBase64({
                                    file,
                                });
                                imgArr.push(fileObj);
                            }

                            externalSetImages?.(imgArr);
                            setLoading?.(false);
                        })();
                    } else {
                        imageInputToBase64({ imageInput: e.target }).then(
                            (res) => {
                                setSrc(res.imageBase64Full);
                                onChangeHandler?.(res);
                                setImageObject?.(res);
                                externalSetImage?.(res);
                                fileInputProps?.onChange?.(e);
                                setLoading?.(false);
                            },
                        );
                    }
                }}
                ref={inputRef as any}
            />

            {src || imageObject?.imageBase64Full ? (
                <Card
                    className="w-full relative h-full items-center justify-center"
                    {...previewImageWrapperProps}
                >
                    {label && (
                        <label
                            className={twMerge(
                                "absolute top-0 left-0 text-xs z-50",
                            )}
                        >
                            <Tag color="gray">
                                <span className="opacity-70">{label}</span>
                            </Tag>
                        </label>
                    )}
                    {disablePreview ? (
                        <Span className="opacity-50" size="small">
                            Image Uploaded!
                        </Span>
                    ) : (
                        <img
                            src={imageObject?.imageBase64Full || src}
                            className="w-full h-full object-contain"
                            {...previewImageProps}
                        />
                    )}
                    <div
                        className={twMerge(
                            "absolute p-1 top-2 right-2 z-20 bg-background-light dark:bg-background-dark",
                            "cursor-pointer",
                        )}
                        onClick={(e) => {
                            setSrc(undefined);
                            onChangeHandler?.(undefined);
                            setImageObject?.(undefined);
                            externalSetImage?.(undefined);
                            if (inputRef.current) {
                                inputRef.current.value == "";
                            }
                        }}
                        title="Cancel Image Upload Button"
                    >
                        <X className="text-slate-950 dark:text-white" />
                    </div>
                </Card>
            ) : (
                <Card
                    className={twMerge(
                        "w-full h-full cursor-pointer hover:bg-slate-100 dark:hover:bg-white/20",
                        placeHolderWrapper?.className,
                    )}
                    onClick={(e) => {
                        const targetEl = e.target as HTMLElement | undefined;
                        if (targetEl?.closest(".cancel-upload")) {
                            e.preventDefault();
                            return;
                        }
                        inputRef.current?.click();
                        placeHolderWrapper?.onClick?.(e);
                    }}
                    {...placeHolderWrapper}
                >
                    <Center>
                        <Stack className="items-center gap-2">
                            <ImagePlus className="text-slate-400" />
                            <Span size="smaller" variant="faded">
                                {label || "Click to Upload Image"}
                            </Span>
                            <Stack className="cancel-upload w-full items-stretch gap-1">
                                <Input
                                    placeholder="Eg. https://example.com/img.png"
                                    className="text-sm twui-image-url-input"
                                    title="Enter Image URL"
                                    wrapperWrapperProps={{ className: "mt-2" }}
                                    changeHandler={(value) => {
                                        imageUrlRef.current = value;
                                    }}
                                    showLabel
                                />
                                <Button
                                    title="Restore Image Button"
                                    size="smaller"
                                    variant="outlined"
                                    color="gray"
                                    onClick={() => {
                                        if (!imageUrlRef.current) return;
                                        setSrc(imageUrlRef.current);
                                    }}
                                >
                                    Set Image URL
                                </Button>
                            </Stack>
                            {existingImageUrl && (
                                <Button
                                    title="Restore Image Button"
                                    size="smaller"
                                    variant="ghost"
                                    onClick={() => {
                                        restoreImageFn?.() ||
                                            setSrc(existingImageUrl);
                                    }}
                                    className="cancel-upload"
                                >
                                    Restore Original Image
                                </Button>
                            )}
                        </Stack>
                    </Center>
                </Card>
            )}
        </Stack>
    );
}
