import { FC, useEffect, useState } from 'react';
import { QueryKey, useInfiniteQuery } from 'react-query';

import { AxiosResponse } from 'axios';
import { useDebounce } from '@material-hu/hooks/useDebounce';
import { useDrawerV2 } from '@material-hu/hooks/useDrawerV2';
import { IconSearch } from '@material-hu/icons/tabler';
import Box from '@material-hu/mui/Box';
import CircularProgress from '@material-hu/mui/CircularProgress';
import Stack from '@material-hu/mui/Stack';
import Typography from '@material-hu/mui/Typography';

import Button from '@material-hu/components/design-system/Buttons/Button';
import HuInputClassic from '@material-hu/components/design-system/Inputs/Classic';

import { logEvent } from 'src/config/logging';
import { EVENTS_SOCKETS } from 'src/constants/sockets';
import { useAuth } from 'src/contexts/JWTContext';
import { useSocket } from 'src/contexts/SocketContext';
import useGeneralError from 'src/hooks/useGeneralError';
import useHuGoTheme from 'src/hooks/useHuGoTheme';
import { EventName } from 'src/types/amplitude';
import { type GroupPost } from 'src/types/groups';
import { type Post, type PostReactionSocket, PostType } from 'src/types/posts';
import { Updater } from 'src/types/queries';
import { Reaction } from 'src/types/reaction';
import { CursorPagination } from 'src/types/services';
import { useLokaliseTranslation as useTranslation } from 'src/utils/i18n';
import { flatPages } from 'src/utils/pagination';
import { addReaction, removeReaction } from 'src/utils/reactions';

import InfiniteList from 'src/components/list/InfiniteList';
import PostCard from 'src/components/post/PostCard';
import { SearchEmptyState } from 'src/components/SearchEmptyState';

import GroupPostCard from '../../groups/components/GroupPostCard';

type PostSearchItem = Post | GroupPost;

export type PostSearchProps = {
  cardContext?: 'feed' | 'groups';
  queryKey: (query: string) => QueryKey;
  fetchFn: (
    pageParam: string,
    query: string,
  ) => Promise<AxiosResponse<CursorPagination<PostSearchItem[]>>>;
  updateReactionFn: (
    postId: number,
    query: string,
    updater: Updater<Reaction[]>,
  ) => void;
};

export const PostSearch: FC<PostSearchProps> = ({
  cardContext = 'feed',
  queryKey,
  fetchFn,
  updateReactionFn,
}) => {
  const [open, setOpen] = useState<boolean>(false);
  const [query, setQuery] = useState<string>('');

  const { t } = useTranslation(['post']);
  const showGeneralError = useGeneralError();
  const debouncedQuery = useDebounce(query);
  const socket = useSocket();
  const { user } = useAuth();
  const HuGoThemeProvider = useHuGoTheme();

  // Auto-focus input when drawer opens
  useEffect(() => {
    if (!open) return;
    const timer = setTimeout(() => {
      const input = document.querySelector(
        '[data-post-search-drawer] input',
      ) as HTMLInputElement;
      input?.focus();
    }, 100);
    return () => clearTimeout(timer);
  }, [open]);

  const { data, isLoading, fetchNextPage, hasNextPage, isFetchingNextPage } =
    useInfiniteQuery(
      queryKey(debouncedQuery),
      ({ pageParam = '' }) => fetchFn(pageParam, debouncedQuery),
      {
        getNextPageParam: lastPage => lastPage?.data?.cursor,
        onError: error =>
          showGeneralError(error, t('post:error_loading_posts')),
        enabled: !!debouncedQuery,
      },
    );

  useEffect(() => {
    if (!query) return;

    const addPostReaction = (reaction: PostReactionSocket) => {
      const { emoji, unified, userId, postId } = reaction;

      updateReactionFn(postId, query, reactions =>
        addReaction(reactions ?? [], emoji, unified, userId === user?.id),
      );
    };

    const removePostReaction = (reaction: PostReactionSocket) => {
      const { emoji, userId, postId } = reaction;

      updateReactionFn(postId, query, reactions =>
        removeReaction(reactions ?? [], emoji, userId === user?.id),
      );
    };

    socket.listenEvent(EVENTS_SOCKETS.NEW_POST_REACTION, addPostReaction);

    socket.listenEvent(EVENTS_SOCKETS.REMOVE_POST_REACTION, removePostReaction);

    return () => {
      socket.closeEvent(EVENTS_SOCKETS.NEW_POST_REACTION, addPostReaction);

      socket.closeEvent(
        EVENTS_SOCKETS.REMOVE_POST_REACTION,
        removePostReaction,
      );
    };
  }, [socket, user?.id, query, updateReactionFn]);

  const module = cardContext === 'groups' ? 'feed in group' : 'feed';

  useEffect(() => {
    if (debouncedQuery) {
      logEvent(EventName.FEED_SEARCH_PERFORMED, {
        module,
        query: debouncedQuery,
      });
    }
  }, [debouncedQuery, module]);

  const openSearch = () => {
    logEvent(EventName.FEED_SEARCH_OPENED, { module });
    setOpen(true);
  };
  const closeSearch = () => setOpen(false);

  const handleChange = (value: string) => {
    setQuery(value);
  };

  const renderPostCard = (post: PostSearchItem) => {
    const useGroupCard =
      cardContext === 'groups' && post.type === PostType.GroupType;

    if (useGroupCard) {
      return (
        <GroupPostCard
          post={post as GroupPost}
          hideComments={true}
          hideReactions={true}
          hidePinIcon
          matchText={query}
          showExternalLink={true}
          isPinned={false}
        />
      );
    }

    return (
      <PostCard
        post={post as Post}
        hideComments={true}
        hideReactions={true}
        hidePinIcon
        matchText={query}
        showExternalLink={true}
        isPinned={false}
      />
    );
  };

  const flatData = flatPages<PostSearchItem>(data);

  const { drawer } = useDrawerV2(() => {
    return {
      title: t('post:search_posts'),
      anchor: 'right',
      variant: 'temporary',
      open: open,
      onClose: closeSearch,
      children: (
        <Stack
          data-post-search-drawer
          sx={{
            p: 2,
            borderRadius: theme => theme.shape.borderRadiusL,
            backgroundColor: theme =>
              theme.palette.new.background.layout.default,
          }}
        >
          <HuInputClassic
            placeholder={t('post:search')}
            value={query}
            onChange={handleChange}
            hasCounter={false}
            startAdornment={<IconSearch />}
          />
          <InfiniteList
            isSuccess={!debouncedQuery || !!data}
            isEmpty={!data || data.pages[0].data.items.length === 0}
            isLoading={isLoading || query !== debouncedQuery}
            fetchNextPage={fetchNextPage}
            hasNextPage={hasNextPage}
            isFetchingNextPage={isFetchingNextPage}
            loadingSkeleton
            renderSkeleton={
              <Stack
                sx={{
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'center',
                  mt: 2,
                  p: 2,
                }}
              >
                <CircularProgress />
              </Stack>
            }
            showNoResultsMessage={query !== '' && query === debouncedQuery}
            noResultsLabel={
              <SearchEmptyState label={t('post:no_results_web')} />
            }
          >
            {flatData.map(post => (
              <Box
                key={post.id}
                sx={{ mt: 2 }}
              >
                {renderPostCard(post)}
              </Box>
            ))}
          </InfiniteList>
        </Stack>
      ),
    };
  });

  return (
    <HuGoThemeProvider>
      <Button
        variant="secondary"
        startIcon={<IconSearch />}
        onClick={openSearch}
      >
        <Typography
          variant="globalXS"
          fontWeight="semiBold"
          color="unset"
        >
          {t('post:search')}
        </Typography>
      </Button>
      {drawer}
    </HuGoThemeProvider>
  );
};
