import { Flex, Select, Spin } from "antd";
import { isValidElement, useCallback, useEffect, useState } from "react";

import { htmlDecode, useDebounce } from "@devowl-wp/react-utils";
import type { AbstractPost, PostStatus, RouteParamsInterface, RouteRequestInterface } from "@devowl-wp/utils";
import { RouteHttpVerb } from "@devowl-wp/utils";

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

import type { FC } from "react";

interface RequestRouteMinimalPostTypeSelectGet extends RouteRequestInterface {
    search?: string;
    per_page?: number;
    subtype?: string;
    include?: number[];
    status?: PostStatus[];
}

interface ParamsRouteMinimalPostTypeSelectGet extends RouteParamsInterface {
    _dataLocale?: string;
    _embed?: string;
    _rcbExtendSearchResult?: boolean;
}

type ExtendedPostData = AbstractPost["data"] & { type_singular?: string };

type ResponseRouteMinimalPostTypeSelectGet = Array<
    Pick<AbstractPost["data"], "id" | "title" | "type"> & {
        subtype: string;
        url: string;
        _embedded: {
            self: Array<ExtendedPostData>;
        };
    }
>;

const PostTypeSelect: FC<{
    postType?: string[];
    perPage?: number;
    postStatus?: PostStatus[];
    value?: number | number[];
    multiple?: boolean;
    disabled?: boolean;
    forceDefaultLanguage?: boolean;
    onChange?: (value: number | number[]) => void;
    filter?: (post: ExtendedPostData) => boolean;
    titleRender?: (post: ExtendedPostData | undefined, selected: number[]) => string;
    applyTitleRenderOnSelectOption?: boolean;
}> = ({
    postType,
    postStatus = ["draft", "publish", "private"],
    perPage = 10,
    value,
    multiple,
    disabled,
    forceDefaultLanguage,
    onChange,
    titleRender = (post) => post?.title.rendered,
    applyTitleRenderOnSelectOption,
    filter = () => true,
}) => {
    const [term, setTerm] = useState<string | false>(false);
    const [postId, setPostId] = useState<typeof value>(value);
    const [fetching, setFetching] = useState(false);
    const [data, setData] = useState<ExtendedPostData[]>([]);

    const fetch = useCallback(async (request?: RequestRouteMinimalPostTypeSelectGet, updateData = true) => {
        setFetching(true);
        const { defaultLanguage, currentLanguage } = getOtherOptionsFromWindow();
        const pages: ResponseRouteMinimalPostTypeSelectGet = await utilsRequest<
            RequestRouteMinimalPostTypeSelectGet,
            ParamsRouteMinimalPostTypeSelectGet,
            ResponseRouteMinimalPostTypeSelectGet
        >({
            location: {
                path: `/search`,
                method: RouteHttpVerb.GET,
                namespace: "wp/v2",
            },
            request: {
                status: request.include ? ["draft", "publish", "private"] : postStatus,
                ...(postType ? { subtype: postType.join(",") } : {}),
                ...request,
            },
            params: {
                _dataLocale: forceDefaultLanguage ? defaultLanguage : currentLanguage,
                _embed: "self",
                _rcbExtendSearchResult: true,
            },
        });

        const result = pages.map(
            ({
                _embedded: {
                    self: [self],
                },
            }) => ({
                content: {
                    rendered: "",
                    raw: "",
                },
                ...self,
            }),
        );
        if (updateData) {
            setData(result);
        }

        setFetching(false);
        return result;
    }, []);

    useDebounce(
        term,
        term === "" ? 0 : 800,
        (debouncedValue) => {
            if (debouncedValue !== false) {
                fetch({ search: debouncedValue, per_page: debouncedValue.length ? 50 : perPage });
            }
        },
        (changedValue) => {
            if (changedValue !== false) {
                setFetching(true);
            }
            setData([]);
        },
    );

    useEffect(() => {
        const fnFetch = (updateData = true) => {
            if ((typeof postId === "number" && postId > 0) || (Array.isArray(postId) && postId.length > 0)) {
                return fetch({ include: Array.isArray(postId) ? postId : [postId] }, updateData);
            }

            return Promise.resolve<AbstractPost["data"][]>([]);
        };

        // When the tab gets visible, immediate fetch current selected item and replace it within our data
        const fnVisibilityChange = async () => {
            if (document.visibilityState === "visible") {
                const [post] = await fnFetch(false);
                if (post) {
                    setData((d) =>
                        d.map((row) => {
                            return row.id === post.id ? post : row;
                        }),
                    );
                }
            }
        };

        // Initial loader
        fnFetch();

        document.addEventListener("visibilitychange", fnVisibilityChange);
        return () => {
            document.removeEventListener("visibilitychange", fnVisibilityChange);
        };
    }, []);

    // Listen to changes from outside and refetch the data
    useEffect(() => {
        if (
            JSON.stringify(postId) !== JSON.stringify(value) &&
            ((typeof value === "number" && value > 0) || (Array.isArray(value) && value.length > 0))
        ) {
            setPostId(value);
            fetch({ include: Array.isArray(value) ? value : [value] });
        }
    }, [value, postId]);

    const selected = Array.isArray(postId) ? postId : [postId].filter(Boolean);
    const selectOptionTitle = `— ${__("Select")} —`;

    return (
        <Select
            mode={multiple ? "multiple" : undefined}
            disabled={disabled}
            showSearch
            value={postId}
            placeholder={__("Search...")}
            notFoundContent={fetching ? <Spin size="small" /> : null}
            onClick={() => setTerm("")}
            onSearch={setTerm}
            onChange={(e) => {
                const value = Array.isArray(e) ? e.map(Number) : +e;
                setPostId(value);
                onChange?.(value);
            }}
            filterOption={false}
            loading={fetching}
            labelRender={({ label }) => (isValidElement(label) ? label.props["data-label"] : label)}
        >
            {!multiple && !fetching && (
                <Select.Option value={0}>
                    {htmlDecode(
                        applyTitleRenderOnSelectOption
                            ? titleRender(undefined, selected) || selectOptionTitle
                            : selectOptionTitle,
                    )}
                </Select.Option>
            )}
            {data.map((d) => {
                const title = htmlDecode(titleRender(d, selected));
                const extra = d.type_singular && (postType?.length > 1 || !postType) ? d.type_singular : undefined;

                return (
                    <Select.Option key={d.id} value={d.id} style={{ display: filter(d) ? undefined : "none" }}>
                        <Flex
                            justify="space-between"
                            align="center"
                            data-label={`${title}${extra ? ` (${extra})` : ""}`}
                        >
                            <span>{title}</span>
                            {extra && <span style={{ opacity: 0.7, paddingLeft: 5, paddingRight: 5 }}>{extra}</span>}
                        </Flex>
                    </Select.Option>
                );
            })}
        </Select>
    );
};

export { PostTypeSelect };
