import React, { useMemo, useCallback, PropsWithChildren } from "react";
import { useTranslations } from "next-intl";
import { Plus } from "lucide-react";
import { useFormContext, useFieldArray, useWatch } from "react-hook-form";
import { useFormGroupName, FormGroup } from "react-compose-form";
import { FormMessage } from "@/components/form";
import { FormLabel } from "@/components/form/form-label";
import { InputControl } from "@/components/form/control/input.control";
import { useMediaPicker } from "@/components/ui/media-picker";
import { MediaPreview } from "@/components/dashboard/media/media-preview";
import { Button } from "@/components/ui/button";
import { api } from "@/lib/trpc/client";
import { MediaScope } from "@/server/modules/media/types/media-scope.enum";

type MediaCollectionRowProps = {
    name: string;
    selected?: boolean;
    onSelect?: () => void;
    onDelete?: () => void;
};

const MediaCollectionRow: React.FC<MediaCollectionRowProps> = (props) => {
    const { selected, name, onSelect, onDelete } = props;

    const id = useWatch({
        name: useFormGroupName(`${name}.mediaId`)
    });

    const { data: media } = api.media.get.useQuery({
        id
    });

    return (
        <FormGroup name={name}>
            <div className="relative h-[125px] w-[125px] overflow-hidden rounded-md">
                <InputControl type="hidden" name="mediaId" />

                <MediaPreview
                    size="100%"
                    selected={selected}
                    originalName={media?.originalName}
                    mimeType={media?.mimeType}
                    url={media?.url}
                    alt={media?.alt || ""}
                    author={media?.author || ""}
                    caption={media?.caption || ""}
                    onSelect={onSelect}
                    onDelete={onDelete}
                />
            </div>
        </FormGroup>
    );
};

type MediaCollectionProps = PropsWithChildren<{
    withAttachments?: boolean;
    label?: string;
    scope?: MediaScope;
    name: string;
    preview?: string;
}>;

export const MediaCollection: React.FC<MediaCollectionProps> = (props) => {
    const { withAttachments, label, scope, preview: previewName, name } = props;

    const { watch, setValue } = useFormContext();
    const t = useTranslations("dashboard.media");

    const previewId = previewName ? watch(previewName) : "";
    const fullName = useFormGroupName(name);

    const { replace, remove, fields } = useFieldArray<{
        [key: typeof fullName]: { mediaId: string }[];
    }>({
        name: fullName
    });

    const ids = useMemo(() => {
        return fields.map((field) => field.mediaId);
    }, [fields]);

    const mediaPicker = useMediaPicker();

    const handleAdd = useCallback(async () => {
        try {
            const selected = await mediaPicker.pick({
                withAttachments,
                multiple: true,
                scope,
                selected: ids
            });

            replace(selected.map((id) => ({ mediaId: id })));

            if (previewName && (!previewId || !selected.includes(previewId))) {
                const [id] = selected;

                setValue(previewName, id);
            }
        } catch {}
    }, [mediaPicker, withAttachments, scope, ids, replace, previewName, previewId, setValue]);

    const handlePrimary = useCallback(
        (id: string) => {
            if (!previewName) {
                return;
            }

            setValue(previewName, id);
        },
        [previewName, setValue]
    );

    const handleRemove = useCallback(
        (index: number) => {
            const id = ids[index];

            remove(index);

            if (previewName && previewId === id) {
                const newPreviewId = ids.find((id) => id !== previewId);

                setValue(previewName, newPreviewId);
            }
        },
        [ids, remove, previewName, previewId, setValue]
    );

    return (
        <div className="grid gap-2">
            {label && (
                <FormLabel className="flex justify-between" name={fullName}>
                    <span>{label}</span>

                    <Button variant="ghost" type="button" size="icon" title={t("actions.add")} onClick={handleAdd}>
                        <Plus />
                    </Button>
                </FormLabel>
            )}

            <div className="grid grid-cols-2 gap-2">
                {fields.map((field, index) => {
                    return (
                        <MediaCollectionRow
                            key={field.id}
                            selected={field.mediaId === previewId}
                            name={`${name}.${index}`}
                            onSelect={previewName ? () => handlePrimary(field.mediaId) : undefined}
                            onDelete={() => handleRemove(index)}
                        />
                    );
                })}
            </div>

            <FormMessage name={name} />

            {previewName && <FormMessage name={previewName} />}

            {previewName && <InputControl type="hidden" name={previewName} />}
        </div>
    );
};
