import {
  Avatar,
  Box,
  BoxedNextImage as Image,
  BoxProps,
  Button,
  CloseIcon,
  ExternalLinkIcon,
  Flex,
  HamburgerIcon,
  HStack,
  Input,
  InputGroup,
  InputLeftElement,
  Link,
  Modal,
  ModalBody,
  ModalContent,
  ModalOverlay,
  SimpleGrid,
  Stack,
  Text,
  Tooltip,
  useBreakpointValue,
  useDisclosure,
} from '@metafam/ds';
import { Maybe } from '@metafam/utils';
import { ConnectKitButton } from 'connectkit';
import {
  GuildFragment,
  Player,
  PlayerFragment,
  SearchQuestsQuery,
} from 'graphql/autogen/hasura-sdk';
import { useRouter } from 'next/router';
import React, {
  FormEventHandler,
  ReactNode,
  useEffect,
  useRef,
  useState,
} from 'react';
import { distinctUntilChanged, forkJoin, from, Subject } from 'rxjs';
import { debounceTime, filter, shareReplay, switchMap } from 'rxjs/operators';
import {
  getPlayerName,
  getPlayerURL,
  getPlayerUsername,
} from 'utils/playerHelpers';
import { useAccount } from 'wagmi';

import LogoImage from '#assets/new_logo_svg.svg';
import SearchIcon from '#assets/search-icon.svg';
import { MetaLink } from '#components/Link';
import { DesktopNavLinks } from '#components/MegaMenu/DesktopNavLinks';
import { DesktopPlayerStats } from '#components/MegaMenu/DesktopPlayerStats';
import { searchPatrons } from '#graphql/getPatrons';
import { searchPlayers } from '#graphql/getPlayers';
import { searchQuests } from '#graphql/getQuests';
import { searchGuilds } from '#graphql/queries/guild';
import { Patron } from '#graphql/types';
import { useMounted, useUser, useWeb3 } from '#lib/hooks';
import { useProfileImageOnload } from '#lib/hooks/useProfileImageOnload';
import { menuIcons } from '#utils/menuIcons';
import { MenuSectionLinks } from '#utils/menuLinks';
import { authenticateWallet } from '#contexts/Web3Context';
import { useViemClients } from '#lib/hooks/useEthersProvider';

type LogoProps = {
  link: string;
} & BoxProps;

const Logo: React.FC<LogoProps> = ({ link, ...props }) => {
  const w = useBreakpointValue({ base: 9, lg: 10 }) ?? 9;
  const h = useBreakpointValue({ base: 12, lg: 14 }) ?? 12;

  return (
    <Box {...props}>
      <MetaLink
        href={link}
        _focus={{ outline: 'none', bg: 'transparent' }}
        _hover={{ bg: 'transparent' }}
        _active={{ bg: 'transparent' }}
      >
        <Image
          src={LogoImage}
          transition="0.25s"
          {...{ w, h }}
          _hover={{ transform: 'scale(1.1)' }}
        />
      </MetaLink>
    </Box>
  );
};

interface OptionProps {
  text: string;
  name: string;
  player?: Player;
  image?: string;
  onClick: () => void;
}

const Option = ({ onClick, name, player, text }: OptionProps) => {
  const imageURL = useProfileImageOnload({ player });

  return (
    <Box {...{ onClick }} as="li" role="option" sx={{ listStyleType: 'none' }}>
      <Flex
        _hover={{
          background: 'rgba(0,0,0,0.56)',
        }}
        align="center"
        px={3}
        py={2}
        cursor="pointer"
        rounded="lg"
      >
        <Avatar
          {...{ name }}
          src={imageURL}
          w={6}
          h={6}
          sx={{
            '& > div': {
              fontSize: 'xs',
            },
          }}
        />
        <Text
          px={2}
          color="white"
          fontFamily="Exo 2"
          fontWeight={400}
          textOverflow="ellipsis"
          overflow="hidden"
          whiteSpace="nowrap"
        >
          {text}
        </Text>
      </Flex>
    </Box>
  );
};

const ResultsTitle = ({ children }: { children: ReactNode }) => (
  <Text
    fontWeight={600}
    color="white"
    w="100%"
    px={6}
    pt={1}
    fontFamily="Exo 2"
    fontSize="1rem"
  >
    {children}
  </Text>
);

const SeeAllOption = ({
  type,
  onClick,
}: {
  type: string;
  onClick: () => void;
}) => (
  <Box {...{ onClick }} cursor="pointer">
    <Text
      fontFamily="Exo 2"
      fontWeight={600}
      color="landing450"
      px={3}
      fontSize="0.875rem"
    >
      See All {type}
    </Text>
  </Box>
);

const LIMIT = 3;

interface SearchResults {
  players: PlayerFragment[];
  guilds: GuildFragment[];
  patrons: Patron[];
  quests: SearchQuestsQuery['quest'];
}

const SearchModal = ({
  isOpen,
  onClose,
}: {
  isOpen: boolean;
  onClose: () => void;
}) => {
  const router = useRouter();
  const searchInputSubjectRef = useRef(new Subject<string>());
  const searchBarRef = useRef<HTMLInputElement>(null);
  const [query, setQuery] = useState<string>('');
  const [{ players, guilds, patrons, quests }, setSearchResults] =
    useState<SearchResults>({
      players: [],
      guilds: [],
      patrons: [],
      quests: [],
    });

  const resetResults = () => {
    setSearchResults({
      players: [],
      guilds: [],
      patrons: [],
      quests: [],
    });
  };

  const dropdown = useRef<Maybe<HTMLDivElement>>(null);
  const handleSubmit: FormEventHandler<HTMLFormElement> = (e) => {
    e.preventDefault();
    onClose();
    // Default Show Players Matching With Query
    router.push(`/search/players?q=${query}`);
  };

  useEffect(() => {
    searchInputSubjectRef.current.next(query);
  }, [query]);

  useEffect(() => {
    const searchSubscription = searchInputSubjectRef.current
      .pipe(
        filter((searchValue: string) => {
          if (searchValue.length >= 1) return true;

          resetResults();
          return false;
        }),
        debounceTime(300),
        distinctUntilChanged(),
        switchMap((queryString) =>
          forkJoin([
            from(searchPlayers(queryString)),
            from(searchGuilds({ search: queryString, limit: LIMIT })),
            from(searchPatrons(queryString, LIMIT)),
            from(searchQuests(queryString, LIMIT)),
          ]),
        ),
        shareReplay(1),
      )
      .subscribe(
        ([
          { players: p },
          { guilds: g },
          searchPatronsRes,
          searchQuestsRes,
        ]) => {
          setSearchResults({
            players: p,
            guilds: g,
            patrons: searchPatronsRes,
            quests: searchQuestsRes,
          });
        },
      );
    return () => searchSubscription?.unsubscribe();
  }, []);

  const isBodyEmpty = players.length + guilds.length === 0;
  return (
    <Modal {...{ isOpen }} {...{ onClose }} scrollBehavior="inside">
      <ModalOverlay />
      <ModalContent
        overflow="hidden"
        top="max(4rem, 8vh)"
        shadow="2xl"
        maxH="700px"
        maxW="500px"
        aria-expanded="true"
        marginTop={1}
        bgColor="transparent"
        p={0}
      >
        <Flex
          flexDirection="column"
          alignItems="center"
          minWidth={40}
          pos="relative"
          align="stretch"
          bg="transparent"
          backdropFilter="blur(10px)"
          ref={dropdown}
        >
          <Box as="form" onSubmit={handleSubmit} w="full" color="white">
            <InputGroup
              justifyContent="flex-start"
              h="fit-content"
              p={2}
              my="auto"
              bg={{
                base: '#FFFFFF25',
              }}
              border={{ base: '1px solid #2B2244' }}
              borderTopRadius={4}
              borderBottomRadius={query.length > 0 ? 0 : 4}
              overflow="hidden"
            >
              <InputLeftElement
                pointerEvents="none"
                children={
                  <Image src={SearchIcon} alt="search" height={4} width={4} />
                }
              />
              <Input
                variant="unstyled"
                color="white"
                w="100%"
                placeholder="Find Players or Guilds"
                _placeholder={{ color: 'whiteAlpha.700' }}
                value={query}
                onChange={({ target: { value } }) => setQuery(value)}
                size="sm"
                fontSize="md"
                ref={searchBarRef}
              />
            </InputGroup>
          </Box>
          <ModalBody
            sx={{
              '&::-webkit-scrollbar': {
                width: '8px',
              },
              bg: 'linear-gradient(180deg, rgba(42, 31, 71, 0.9) 6.18%, rgba(17, 3, 32, 0.86) 140%)',
            }}
            borderColor="#2B2244"
            backdropFilter="blur(10px)"
            w="100%"
            maxH="67vh"
            p={0}
          >
            {!isBodyEmpty && (
              <Box w="100%" color="white" py={4}>
                {players.length > 0 && <ResultsTitle>Players</ResultsTitle>}
                {players.length > 0 && (
                  <Box as="ul" role="listbox" mb={2} px={3} color="white">
                    {players?.map((player: PlayerFragment) => (
                      <Option
                        key={player.id}
                        onClick={() => {
                          router.push(getPlayerURL(player) as string);
                          onClose();
                        }}
                        name={getPlayerName(player) ?? 'Unknown'}
                        player={player as Player}
                        text={
                          (getPlayerUsername(player as Maybe<Player>) ||
                            getPlayerName(player)) ??
                          'Unknown'
                        }
                      />
                    ))}
                    {players.length >= LIMIT && (
                      <SeeAllOption
                        type="Players"
                        onClick={() => {
                          router.push(`/search/players?q=${encodeURI(query)}`);
                          onClose();
                        }}
                      />
                    )}
                  </Box>
                )}

                {guilds.length > 0 && <ResultsTitle>Guilds</ResultsTitle>}
                {guilds.length > 0 && (
                  <Box as="ul" role="listbox" mb={2} px={3} color="white">
                    {guilds?.map((guild: GuildFragment) => (
                      <Option
                        key={guild.id}
                        onClick={() => {
                          router.push(`/guild/${guild.guildname}`);
                          onClose();
                        }}
                        name={guild.name}
                        image={guild?.logo as string | undefined}
                        text={guild.name}
                      />
                    ))}
                    {guilds.length >= LIMIT && (
                      <SeeAllOption
                        type="Guilds"
                        onClick={() => {
                          router.push(`/search/guilds?q=${encodeURI(query)}`);
                          onClose();
                        }}
                      />
                    )}
                  </Box>
                )}

                {patrons.length > 0 && <ResultsTitle>Patrons</ResultsTitle>}
                {patrons.length > 0 && (
                  <Box as="ul" role="listbox" mb={2} px={3} color="white">
                    {patrons?.map((patron: Patron) => (
                      <Option
                        key={patron.id}
                        onClick={() => {
                          router.push(getPlayerURL(patron) as string);
                          onClose();
                        }}
                        name={getPlayerName(patron) ?? 'Unknown'}
                        player={patron as Player}
                        text={
                          (getPlayerUsername(patron as Maybe<Patron>) ||
                            getPlayerName(patron)) ??
                          'Unknown'
                        }
                      />
                    ))}
                    {patrons.length >= LIMIT && (
                      <SeeAllOption
                        type="Patrons"
                        onClick={() => {
                          router.push(`/search/patrons?q=${encodeURI(query)}`);
                          onClose();
                        }}
                      />
                    )}
                  </Box>
                )}

                {quests.length > 0 && <ResultsTitle>Quests</ResultsTitle>}
                {quests.length > 0 && (
                  <Box as="ul" role="listbox" mb={2} px={3} color="white">
                    {quests.map((quest: SearchQuestsQuery['quest'][number]) => (
                      <Option
                        key={quest.id}
                        onClick={() => {
                          router.push(`/quest/${quest.id}`);
                          onClose();
                        }}
                        name={quest.title}
                        image={quest.image ?? undefined}
                        text={quest.title}
                      />
                    ))}
                    {quests.length >= LIMIT && (
                      <SeeAllOption
                        type="Quests"
                        onClick={() => {
                          router.push(`/search/quests?q=${encodeURI(query)}`);
                          onClose();
                        }}
                      />
                    )}
                  </Box>
                )}
              </Box>
            )}
          </ModalBody>
        </Flex>
      </ModalContent>
    </Modal>
  );
};

type HeaderSearchBarProps = BoxProps & { onOpen: () => void };

const HeaderSearchBar = (props: HeaderSearchBarProps) => {
  const { onOpen, ...restProps } = props;
  return (
    <Tooltip label="Quick Search…   ⌘K">
      <Box
        cursor="pointer"
        display="flex"
        flexDirection="row"
        gap={2}
        h="fit-content"
        maxW="fit-content"
        alignItems="center"
        p={2}
        mx={2}
        my="auto"
        bg={{
          base: '#FFFFFF05',
        }}
        border={{ base: '1px solid #2B2244' }}
        borderRadius={8}
        pos="relative"
        onClick={onOpen}
        {...restProps}
      >
        <Image src={SearchIcon} alt="search" height={4} width={4} />
      </Box>
    </Tooltip>
  );
};

export const MegaMenuHeader: React.FC = () => {
  const router = useRouter();
  const { user, fetching } = useUser();
  const { isOpen, onOpen, onClose } = useDisclosure();
  const menuToggle = () => (isOpen ? onClose() : onOpen());

  const {
    isOpen: isSearchOpen,
    onOpen: onSearchOpen,
    onClose: onSearchClose,
  } = useDisclosure();

  // Toggle the menu when ⌘K is pressed
  React.useEffect(() => {
    const down = (e: KeyboardEvent) => {
      if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
        e.preventDefault();
        if (isSearchOpen) {
          onSearchClose();
        } else {
          onSearchOpen();
        }
      }
    };

    document.addEventListener('keydown', down);
    return () => document.removeEventListener('keydown', down);
  }, [isSearchOpen, onSearchClose, onSearchOpen]);

  return (
    <>
      <SearchModal isOpen={isSearchOpen} onClose={onSearchClose} />
      <Stack
        position={router.pathname === '/players' ? 'relative' : 'sticky'}
        top={0}
        id="MegaMenu"
        zIndex={11}
      >
        <Flex
          borderBottom="1px"
          bg="rgba(0, 0, 0, 0.5)"
          borderColor="#2B2244"
          backdropFilter="blur(10px)"
          px={4}
          py={1.5}
          pb="0"
          h={20}
          justify={{ base: 'space-between', lg: 'center' }}
          w="100%"
        >
          <Flex
            onClick={menuToggle}
            flexWrap="nowrap"
            alignItems="center"
            cursor="pointer"
            h={8}
            w={8}
            display={{ base: 'flex', lg: 'flex', xl: 'none' }}
            p={2}
            my="auto"
          >
            {isOpen ? (
              <CloseIcon fontSize="2xl" color="#FFF" />
            ) : (
              // max-width set in style attribute because the unstyled version
              // is flashing at 100% width on load
              <HamburgerIcon
                fontSize="3xl"
                color="#FFF"
                style={{ maxWidth: '2rem' }}
              />
            )}
          </Flex>
          <Flex
            w={{ base: 'auto', lg: '100%' }}
            align="center"
            justify="space-between"
            pos="relative"
            display={{
              base: 'none',
              sm: 'none',
              md: 'none',
              lg: 'none',
              xl: 'flex',
            }}
          >
            <HStack w="15%" flexShrink={1}>
              <Logo
                link={'/dashboard'}
                pos={{ base: 'initial', lg: 'relative' }}
                left={0}
                top="auto"
                bottom="auto"
              />
            </HStack>
            <HStack
              flex="0 1 auto"
              align="center"
              justify="center"
              alignSelf="center"
              spacing={0}
              height="100%"
            >
              <DesktopNavLinks />

              <HeaderSearchBar onOpen={onSearchOpen} />
            </HStack>
            <Box
              textAlign="right"
              display={{ base: 'none', lg: 'block' }}
              pos="relative"
              right="0"
              left="1"
              top="auto"
              bottom="auto"
              w="15%"
            >
              <ConnectKitButton.Custom>
                {({ isConnected, isConnecting, show }) => (
                  isConnected ? (
                    !!user ? (
                      <DesktopPlayerStats player={user} />
                    ) : (
                      <Button onClick={show}>Missing User Record</Button>
                    )
                  ) : (
                    <Button
                      w="100%"
                      size="md"
                      textTransform="uppercase"
                      fontWeight="600"
                      onClick={show}
                      isLoading={isConnecting || fetching}
                      colorScheme="pink"
                      fontSize="1rem"
                    >
                      Connect Wallet
                    </Button>
                  )
                )}
              </ConnectKitButton.Custom>
            </Box>
          </Flex>
          <Flex
            align="center"
            justify="end"
            pos="relative"
            w={{ base: '100%', xl: 'auto' }}
            display="flex"
          >
            <HeaderSearchBar
              display={{ sm: 'flex', xl: 'none' }}
              right={{ sm: 2, lg: 5 }}
              onOpen={onSearchOpen}
            />
            <Logo
              display={{ lg: 'flex', xl: 'none' }}
              link={user ? '/dashboard' : '/'}
              pos={{ base: 'initial', lg: 'relative' }}
              pt={1}
              right={4}
            />
          </Flex>
        </Flex>
        <Stack
          display={{ base: isOpen ? 'block' : 'none', xl: 'none' }}
          position="fixed"
          top="4.5rem"
          zIndex={1}
          overflowX="hidden"
          w="100vw"
          bg="alphaBlack.200"
          h="calc(100vh - 10rem)"
          backdropFilter="blur(10px)"
          p="1rem"
          border="none"
        >
          <HeaderSearchBar onOpen={onSearchOpen} />
          {MenuSectionLinks.map((section) => (
            <Stack pt={1} key={section.label}>
              <Link
                href={section?.url}
                target={section.type === 'external-link' ? '_blank' : ''}
                display={'flex'}
                flexDir={'row'}
                alignItems={'center'}
              >
                <Text
                  fontSize={18}
                  fontWeight={600}
                  textTransform="capitalize"
                  color={section.type === 'external-link' ? '#79F8FB' : 'white'}
                >
                  {section.label}
                </Text>
                {section.type === 'external-link' && (
                  <ExternalLinkIcon color="#79F8FB" ml="10px" />
                )}
              </Link>
              <SimpleGrid columns={2}>
                {section?.menuItems?.length &&
                  section.menuItems.map(({ title, icon, url }) => (
                    <Link
                      key={title}
                      display="flex"
                      alignItems="center"
                      href={url}
                      border="1px"
                      _odd={{ marginRight: '-1px' }}
                      marginBottom="-1px"
                      borderColor="purple.400"
                      bg="alphaBlack.50"
                      px={2}
                      py={1.5}
                      _hover={{
                        bg: 'alphaWhite.50',
                      }}
                      isExternal={/^https?:\/\//.test(url)}
                    >
                      <Avatar
                        name={title}
                        src={menuIcons[icon]}
                        p={0}
                        w={7}
                        h={7}
                        mr={1}
                        bg="linear-gradient(180deg, #170B23 0%, #350C58 100%)"
                      />
                      <Text fontSize={20}>{title}</Text>
                    </Link>
                  ))}
              </SimpleGrid>
            </Stack>
          ))}
        </Stack>
      </Stack>
    </>
  );
};
