import { Spin } from "antd";
import { useCallback, useEffect, useState } from "react";
import { hooks, mediaUtils } from "wp";

import { RouteHttpVerb } from "@devowl-wp/utils";
import type { RouteParamsInterface, RouteRequestInterface, RouteResponseInterface } from "@devowl-wp/utils";

import { getOtherOptionsFromWindow } from "../utils/getOtherOptionsFromWindow.js";
import { request as utilsRequest } from "../utils/request.js";

import type { FC, ReactNode } from "react";

let addedFilter = false;

/**
 * Some page builders (e.g. Divi) move the `mediaUtils` to the footer and/or after Real Media Library scripts.
 */
function addMediaUploadFilterLazily() {
    const { addFilter } = hooks;
    const { MediaUpload } = mediaUtils;
    if (!addedFilter) {
        addedFilter = true;

        const replaceMediaUpload = () => MediaUpload;

        addFilter(
            "editor.MediaUpload",
            "core/edit-post/components/media-upload/replace-media-upload",
            replaceMediaUpload,
        );
    }

    return MediaUpload;
}

type RequestRouteMinimalMediaGet = RouteRequestInterface;

interface ParamsRouteMinimalMediaGet extends RouteParamsInterface {
    id: number;
    _dataLocale?: string;
}

interface ResponseRouteMinimalMediaGet extends RouteResponseInterface {
    [key: string]: any;
    id: number;
    source_url: string;
}

function useMediaAttachment(id: number) {
    const [fetching, setFetching] = useState(false);
    const [data, setData] = useState<ResponseRouteMinimalMediaGet>();
    const [error, setError] = useState<any>();
    const fetch = useCallback(async (id: number) => {
        setFetching(true);
        const { currentLanguage } = getOtherOptionsFromWindow();
        try {
            const result: ResponseRouteMinimalMediaGet = await utilsRequest<
                RequestRouteMinimalMediaGet,
                ParamsRouteMinimalMediaGet,
                ResponseRouteMinimalMediaGet
            >({
                location: {
                    path: `/media/:id`,
                    method: RouteHttpVerb.GET,
                    namespace: "wp/v2",
                },
                params: {
                    id,
                    _dataLocale: currentLanguage,
                },
            });
            setData(result);
            setError(undefined);
        } catch (e) {
            setData(undefined);
            setError(e);
        } finally {
            setFetching(false);
        }
    }, []);

    useEffect(() => {
        if (id) {
            fetch(id);
        } else {
            setData(undefined);
            setError(undefined);
        }
    }, [id]);

    return { fetching, data, fetch, error };
}

/**
 * @see https://github.com/WordPress/gutenberg/tree/trunk/packages/block-editor/src/components/media-upload
 * @see https://wordpress.stackexchange.com/a/385777/83335
 * @see https://www.liip.ch/en/blog/add-an-image-selector-to-a-gutenberg-block
 */
const MediaLibrarySelector: FC<{
    attachmentId?: number;
    title: string;
    allowedTypes: string[];
    onChange?: (id: number, attachment: any) => void;
    render: ({
        open,
        reset,
        attachmentId,
        url,
    }: {
        open: () => void;
        attachmentId?: number;
        url?: string;
        reset: () => void;
    }) => ReactNode;
}> = ({ attachmentId, title, allowedTypes, render, onChange }) => {
    const useAttachmentId = attachmentId || undefined; // Zero should be treated as `undefined`
    const { data, error, fetching } = useMediaAttachment(useAttachmentId);
    const url = data?.source_url;

    useEffect(() => {
        if (error?.responseJSON?.code === "rest_post_invalid_id") {
            onChange(undefined, undefined);
        }
    }, [error]);

    const MediaUpload = addMediaUploadFilterLazily();

    return (
        <Spin spinning={fetching}>
            <MediaUpload
                onSelect={(media: any) => {
                    onChange?.(media?.id, media);
                }}
                title={title}
                allowedTypes={allowedTypes}
                value={useAttachmentId}
                render={({ open }) =>
                    render({
                        open,
                        reset: () => onChange(undefined, undefined),
                        attachmentId: useAttachmentId,
                        url,
                    })
                }
            />
        </Spin>
    );
};

export { MediaLibrarySelector };
