import {useCallback, useEffect, useRef, useState} from "@wordpress/element";
import {__, _n} from "@wordpress/i18n";
import {GetChats, GetChat, DeleteChat} from "../../../../../../../../../rest/chats";
import {dateStringToUtc} from "../../../../../../../../../../helpers";
import SelectedChatsActions from "./components/selected-chats-actions";
import ChatsSearch from "./components/chats-search";
import ChatsNavHeader from "./components/chats-nav-header";
import ChatList from "./components/chat-list";
import {handleError} from "../../../../../../../../../helpers/notifications";
import FilterChatsPopup from "../popups/filter-chats";
import confirm from "../../../../../../../../../helpers/confirm";
import {UpdateChatMetas} from "../../../../../../../../../rest/chat-metas";

const CHATS_DEFAULT_PARAMS = {
    perPage: 10,
    orderBy: 'updated_at',
    order: 'desc',
};

const ChatsNav = ({chatbotUuid, currentChatUuid, setSelectedChat, setLoading, notifications}) => {
    const [chats, setChats] = useState([]);
    const [chatsPagination, setChatsPagination] = useState({
        page: 1,
        perPage: LimbChatbot.screen_options?.lbaic_items_per_page || CHATS_DEFAULT_PARAMS.perPage,
        total: 0,
    });
    const [chatsFilters, setChatsFilters] = useState({});
    const [selectedChats, setSelectedChats] = useState([]);
    const [isLoadingMore, setIsLoadingMore] = useState(false);
    const [isChatsFetched, setIsChatsFetched] = useState(false);
    const [targetChatUuid, setTargetChatUuid] = useState(null);

    // Extract chat_uuid from query parameters on mount
    useEffect(() => {
        const urlParams = new URLSearchParams(window.location.search);
        const chatUuid = urlParams.get('chat_uuid');
        if (chatUuid) {
            setTargetChatUuid(chatUuid);
        }
    }, []);

    const chatsNavRef = useRef(null);
    const sentinelRef = useRef(null);

    const searchDebounceTimeout = useRef(null);

    // Filter popup
    const [showFilterPopup, setShowFilterPopup] = useState(false);
    const [startDate, setStartDate] = useState('');
    const [endDate, setEndDate] = useState('');

    const hasMoreChats = chatsPagination.page * chatsPagination.perPage < chatsPagination.total;
    const isChatbotAvailable =
        Boolean(chatbotUuid) // CPT
        || chatbotUuid === null; // Default chatbot

    useEffect(() => {
        if (isChatbotAvailable) {
            fetchChats();
        }
    }, [isChatbotAvailable]);

    // Auto-select chat from query parameter
    useEffect(() => {
        if (!isChatsFetched) {
            return;
        }
        fetchAndSelectTargetChat(targetChatUuid);
    }, [targetChatUuid, isChatsFetched]);

    const fetchAndSelectTargetChat = async (targetChatUuid) => {
        if (!targetChatUuid) {
            return;
        }

        setLoading(prev => prev + 1);
        try {
            let chat = chats.find(chat => chat.uuid === targetChatUuid)

            if (!chat) {
                chat = await GetChat(LimbChatbot.rest.url, LimbChatbot.rest.nonce, targetChatUuid, {
                    chatbot_uuid: [chatbotUuid],
                    include: ['user', 'messages', 'messages_count'/*, 'conversation_state'*/, 'metas'],
                });
            }

            if (chat) {
                onSelect(chat);
            }
        } catch (e) {
            handleError(e, notifications.set, {
                title: __("Failed to load chat.", 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __("Please check and try again.", 'limb-chatbot'),
            });
        }
        setLoading(prev => prev - 1);
    };

    // Intersection Observer for load more chats
    useEffect(() => {
        const sentinel = sentinelRef.current;
        const root = chatsNavRef.current;

        if (!sentinel || !hasMoreChats || isLoadingMore) {
            return;
        }

        const observer = new IntersectionObserver(
            (entries) => {
                if (entries[0].isIntersecting && hasMoreChats && !isLoadingMore) {
                    fetchChats(chatsPagination.page + 1, chatsPagination.perPage, chatsFilters, false);
                }
            },
            {
                root: root,
                threshold: 0.1,
            }
        );

        observer.observe(sentinel);

        return () => {
            observer.disconnect();
        };
    }, [hasMoreChats, chatsPagination.page, chatsPagination.perPage, chatsFilters, isLoadingMore]);

    const applyFilters = useCallback((start, end) => {
        // First close the popup
        setShowFilterPopup(false);
        // Convert dates to UTC
        const startUtcDate = dateStringToUtc(start + ' 00:00:00');
        const endUtcDate = dateStringToUtc(end + ' 00:00:00');
        setChatsFilters(prevState => ({
            ...prevState,
            start_date: startUtcDate,
            end_date: endUtcDate
        }));
        // Filter chats
        fetchChats(1, chatsPagination.perPage, {
            ...chatsFilters,
            start_date: startUtcDate,
            end_date: endUtcDate,
        });
    }, [chatsPagination.perPage, chatsFilters]);

    const onSearchInputChange = useCallback((newSearchTerm) => {
        setChatsFilters(prevState => ({
            ...prevState,
            search: newSearchTerm,
        }));

        clearTimeout(searchDebounceTimeout.current);

        searchDebounceTimeout.current = setTimeout(() => {
            fetchChats(1, chatsPagination.perPage, {
                ...chatsFilters,
                search: newSearchTerm,
            });
        }, 400);
    }, [chatsPagination.perPage, chatsFilters]);

    const onRefresh = useCallback(() => {
        setChatsFilters(prevState => ({
            ...prevState,
            search: '',
        }));

        fetchChats(1, chatsPagination.perPage, null);
    }, [chatsPagination.perPage, isChatbotAvailable]);

    const onSortNewestFirst = useCallback(() => {
        fetchChats(1, chatsPagination.perPage, {
            ...chatsFilters,
            orderBy: 'updated_at',
            order: 'desc',
        });
    }, [chatsPagination.perPage, chatsFilters, isChatbotAvailable]);

    const onSortOldestFirst = useCallback(() => {
        fetchChats(1, chatsPagination.perPage, {
            ...chatsFilters,
            orderBy: 'updated_at',
            order: 'asc',
        });
    }, [chatsPagination.perPage, chatsFilters, isChatbotAvailable]);

    const fetchChats = async (page, perPage, params = {}, reset = true) => {
        if (!isChatbotAvailable) {
            return;
        }
        setLoading(prev => prev + 1);
        if (!reset) {
            setIsLoadingMore(true);
        }
        const reqParams = {
            page: page || 1,
            per_page: perPage || CHATS_DEFAULT_PARAMS.perPage,
            orderby: params?.orderBy || CHATS_DEFAULT_PARAMS.orderBy,
            order: params?.order || CHATS_DEFAULT_PARAMS.order,
            chatbot_uuid: [chatbotUuid],
            include: ['user', 'messages', 'messages_count', /*'conversation_state',*/ 'metas'],
        };
        // Add search param
        if (params?.search?.trim()) {
            reqParams.search = params.search;
            // reqParams.search_fields = ['name'];
        }
        // Add a date range param
        if (params?.start_date) {
            reqParams.start_date = params.start_date;
        }
        if (params?.end_date) {
            reqParams.end_date = params.end_date;
        }
        try {
            const res = await GetChats(LimbChatbot.rest.url, LimbChatbot.rest.nonce, reqParams);
            // Update state
            if (reset) {
                setChats([...(res.items?.length ? res.items : [])]);
            } else {
                setChats(prevState => [...prevState, ...(res.items?.length ? res.items : [])]);
            }
            // Pagination info
            setChatsPagination(prevState => ({
                ...prevState,
                page: reqParams.page,
                total: res.total,
            }));
            // Chats filters ordering...
            setChatsFilters(prevState => ({
                ...prevState,
                orderBy: reqParams.orderby,
                order: reqParams.order,
            }));
        } catch (e) {
            handleError(e, notifications.set, {
                title: __("Failed to get chats.", 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __("Please check and try again.", 'limb-chatbot'),
            });
        }
        setLoading(prev => prev - 1);
        if (!reset) {
            setIsLoadingMore(false);
        }
        setIsChatsFetched(true);
    };

    /**
     * Mark chats as unread
     *
     * @param {object} chat Chat
     * @return {Promise<void>}
     */
    const markChatAsUnread = async (chat) => {
        try {
            const isAdminReadMeta = chat.metas?.find(item => item.meta_key === 'admin_read');
            if (!isAdminReadMeta || +isAdminReadMeta?.meta_value) {
                // Request to update the chat meta
                const res = await UpdateChatMetas(LimbChatbot.rest.url, LimbChatbot.rest.nonce, chat.uuid, [
                    {
                        meta_key: 'admin_read',
                        meta_value: 0
                    }
                ]);
                const updatedMeta = res?.find(item => item.meta_key === 'admin_read');
                if (updatedMeta) {
                    // Update state
                    setChats(prevState => prevState.map(item => {
                        if (item.uuid === chat.uuid) {
                            // Update chat meta
                            return {
                                ...item,
                                metas: isAdminReadMeta ?
                                    // Update the meta
                                    item.metas.map(meta => {
                                        if (meta.id === isAdminReadMeta.id) {
                                            return updatedMeta;
                                        }
                                        return meta;
                                    })
                                    :
                                    // Add the meta
                                    [...item.metas, updatedMeta]
                            }
                        }
                        return item;
                    }));
                } else {
                    handleError(
                        {
                            message: "Chat meta not updated.",
                            data: res,
                        }, notifications.set, {
                            title: __('Failed to mark chat as unread.', 'limb-chatbot'),
                            description: __('Something went wrong', 'limb-chatbot'),
                        }
                    );
                }
            }
        } catch (e) {
            handleError(e, notifications.set, {
                title: __('Failed to mark chat as unread.', 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __('Please check and try again.', 'limb-chatbot'),
            });
        }
    }

    const deleteChats = async (uuids) => {
        if (!await confirm(_n("Are you sure you want to delete the chat?", "Are you sure you want to delete chats?", uuids.length, 'limb-chatbot'))) {
            return;
        }
        setLoading(prev => prev + 1);
        try {
            await Promise.all(
                uuids.map(uuid => DeleteChat(LimbChatbot.rest.url, LimbChatbot.rest.nonce, uuid))
            );

            setChats(prevChats => prevChats.filter(chat => !uuids.includes(chat.uuid)));
            setSelectedChats([]);
        } catch (e) {
            handleError(e, notifications.set, {
                title: _n("Failed to delete chat.", "Failed to delete chats.", uuids.length, 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __("Please check and try again.", 'limb-chatbot'),
            });
        }
        setLoading(prev => prev - 1);
    };

    const toggleChat = (chatUuid) => {
        setSelectedChats(prevState =>
            prevState.includes(chatUuid) ? prevState.filter(uuid => chatUuid !== uuid) : [...prevState, chatUuid]
        )
    };

    /**
     * Mark chats as read
     *
     * @param {object} chat Chat
     * @return {Promise<void>}
     */
    const markChatsAsRead = async (chat) => {
        try {
            const isAdminReadMeta = chat.metas?.find(item => item.meta_key === 'admin_read');
            if (!isAdminReadMeta || !+isAdminReadMeta.meta_value) {
                // Request to update the chat meta
                const res = await UpdateChatMetas(LimbChatbot.rest.url, LimbChatbot.rest.nonce, chat.uuid, [
                    {
                        meta_key: 'admin_read',
                        meta_value: 1
                    }
                ]);
                const updatedMeta = res?.find(item => item.meta_key === 'admin_read');
                if (updatedMeta) {
                    // Update state
                    setChats(prevState => prevState.map(item => {
                        if (item.uuid === chat.uuid) {
                            // Update chat meta
                            return {
                                ...item,
                                metas: isAdminReadMeta ?
                                    // Update the meta
                                    item.metas.map(meta => {
                                        if (meta.id === isAdminReadMeta.id) {
                                            return updatedMeta;
                                        }
                                        return meta;
                                    })
                                    :
                                    // Add the meta
                                    [...item.metas, updatedMeta]
                            }
                        }
                        return item;
                    }));
                } else {
                    handleError(
                        {
                            message: "Chat meta not updated.",
                            data: res,
                        }, notifications.set, {
                            title: __('Failed to mark chat as read.', 'limb-chatbot'),
                            description: __('Something went wrong', 'limb-chatbot'),
                        }
                    );
                }
            }
        } catch (e) {
            handleError(e, notifications.set, {
                title: __('Failed to mark chat as read.', 'limb-chatbot'),
                description: e.message ? __(e.message, 'limb-chatbot') : __('Please check and try again.', 'limb-chatbot'),
            });
        }
    }

    const onSelect = (chat) => {
        if (!chat?.uuid) {
            return;
        }
        // Mark chat as read
        markChatsAsRead(chat);
        // Select the chat
        if (typeof setSelectedChat === 'function') {
            setSelectedChat(chat);
        }
    };

    return (
        <>
                <div className={`lbaic-settings-chats-nav-header${selectedChats.length > 0 ? ' active' : ''}`}>
                    {selectedChats.length > 0 ? (
                        <SelectedChatsActions
                            selectedChats={selectedChats}
                            setSelectedChats={setSelectedChats}
                            deleteChats={deleteChats}
                        />
                    ) : (
                        <div className="lbaic-settings-chats-nav-header-in">
                            <ChatsNavHeader
                                chats={chats}
                                chatsPagination={chatsPagination}
                                setShowFilterPopup={setShowFilterPopup}
                                refresh={onRefresh}
                                sortNewestFirst={onSortNewestFirst}
                                sortOldestFirst={onSortOldestFirst}
                            />
                            <ChatsSearch
                                value={chatsFilters?.search || ''}
                                onChange={onSearchInputChange}
                            />
                        </div>
                    )}
                </div>

            <div className='lbaic-settings-chats-nav-body'>
                <ChatList
                    chats={chats}
                    currentChatUuid={currentChatUuid}
                    selectedChats={selectedChats}
                    toggleChat={toggleChat}
                    onSelect={onSelect}
                    markAsUnread={markChatAsUnread}
                    deleteChats={deleteChats}
                    chatsNavRef={chatsNavRef}
                    sentinelRef={sentinelRef}
                />
                <div className='lbaic-settings-chats-nav-footer'>
                    <div className='lbaic-settings-chats-nav-footer-in'>
                        {isLoadingMore && (
                            <div className='lbaic-settings-chats-nav-footer-loading'>
                                <p className='lbaic-settings-chats-desc'>{__('Loading', 'limb-chatbot')}...</p>
                            </div>
                        )}
                    </div>
                </div>
            </div>
            {showFilterPopup && (
                <FilterChatsPopup
                    onClose={() => setShowFilterPopup(false)}
                    startDate={startDate}
                    endDate={endDate}
                    setStartDate={setStartDate}
                    setEndDate={setEndDate}
                    onApplyFilters={applyFilters}
                />
            )}
        </>
    );
};

export default ChatsNav;
