import React from "react";
import gql from "graphql-tag";
import { useQuery } from "@apollo/client";
import { ClassNames } from "@emotion/react";
import {
  Box,
  Button,
  Flex,
  Popover,
  PopoverArrow,
  PopoverContent,
  PopoverTrigger,
  Portal,
  Tab,
  Tabs,
  TabList,
  TabPanel,
  TabPanels,
  VisuallyHidden,
  useBreakpointValue,
  useColorModeValue,
  useTheme,
  useToast,
  useToken,
} from "@chakra-ui/react";
import { ChevronDownIcon, WarningTwoIcon } from "@chakra-ui/icons";
import { loadable } from "../util";

import { petAppearanceFragment } from "../components/useOutfitAppearance";
import getVisibleLayers from "../components/getVisibleLayers";
import { OutfitLayers } from "../components/OutfitPreview";
import SupportOnly from "./support/SupportOnly";
import { useAltStylesForSpecies } from "../loaders/alt-styles";
import { useLocalStorage } from "../util";

// From https://twemoji.twitter.com/, thank you!
import twemojiSmile from "../images/twemoji/smile.svg";
import twemojiCry from "../images/twemoji/cry.svg";
import twemojiSick from "../images/twemoji/sick.svg";
import twemojiSunglasses from "../images/twemoji/sunglasses.svg";
import twemojiQuestion from "../images/twemoji/question.svg";
import twemojiMasc from "../images/twemoji/masc.svg";
import twemojiFem from "../images/twemoji/fem.svg";
import twemojiHourglass from "../images/twemoji/hourglass.svg";

const PosePickerSupport = loadable(() => import("./support/PosePickerSupport"));

const PosePickerSupportSwitch = loadable(() =>
  import("./support/PosePickerSupport").then((m) => m.PosePickerSupportSwitch),
);

/**
 * PosePicker shows the pet poses available on the current species/color, and
 * lets the user choose which want they want!
 *
 * NOTE: This component is memoized with React.memo. It's relatively expensive
 *       to re-render on every outfit change - the contents update even if the
 *       popover is closed! This makes wearing/unwearing items noticeably
 *       slower on lower-power devices.
 *
 *       So, instead of using `outfitState` like most components, we specify
 *       exactly which props we need, so that `React.memo` can see the changes
 *       that matter, and skip updates that don't.
 */
function PosePicker({
  speciesId,
  colorId,
  pose,
  altStyleId,
  appearanceId,
  dispatchToOutfit,
  onLockFocus,
  onUnlockFocus,
  ...props
}) {
  const initialFocusRef = React.useRef();
  const posesQuery = usePoses(speciesId, colorId, pose);
  const altStylesQuery = useAltStylesForSpecies(speciesId);
  const [isInSupportMode, setIsInSupportMode] = useLocalStorage(
    "DTIPosePickerIsInSupportMode",
    false,
  );
  const toast = useToast();

  const loading = posesQuery.loading || altStylesQuery.isLoading;
  const error = posesQuery.error ?? altStylesQuery.error;
  const poseInfos = posesQuery.poseInfos;
  const altStyles = altStylesQuery.data ?? [];

  const [isOpen, setIsOpen] = React.useState(false);
  const [tabIndex, setTabIndex] = React.useState(0);

  const altStyle = altStyles.find((s) => s.id === altStyleId);

  const placement = useBreakpointValue({ base: "bottom-end", md: "top-end" });

  React.useEffect(() => {
    // While the popover is open, don't change which tab is open.
    if (isOpen) {
      return;
    }

    // Otherwise, set the tab to Styles if we're wearing an Alt Style, or
    // Expressions if we're not.
    setTabIndex(altStyle != null ? 1 : 0);
  }, [altStyle, isOpen]);

  // Resize the Popover when we toggle support mode, because it probably will
  // affect the content size.
  React.useLayoutEffect(() => {
    // HACK: To trigger a Popover resize, we simulate a window resize event,
    //       because Popover listens for window resizes to reposition itself.
    //       I've also filed an issue requesting an official API!
    //       https://github.com/chakra-ui/chakra-ui/issues/1853
    window.dispatchEvent(new Event("resize"));
  }, [isInSupportMode]);

  // Generally, the app tries to never put us in an invalid pose state. But it
  // can happen with direct URL navigation, or pet loading when modeling isn't
  // updated! Let's do some recovery.
  const selectedPoseIsAvailable = Object.values(poseInfos).some(
    (pi) => pi.isSelected && pi.isAvailable,
  );
  const firstAvailablePose = Object.values(poseInfos).find(
    (pi) => pi.isAvailable,
  )?.pose;
  React.useEffect(() => {
    if (loading) {
      return;
    }

    if (!selectedPoseIsAvailable) {
      if (!firstAvailablePose) {
        // TODO: I suppose this error would fit better in SpeciesColorPicker!
        toast({
          status: "error",
          title: "Oops, we don't have data for this pet color!",
          description:
            "If it's new, this might be a modeling issue—try modeling it on " +
            "Classic DTI first. Sorry!",
          duration: null,
          isClosable: true,
        });
        return;
      }

      console.warn(
        `Pose ${pose} not found for speciesId=${speciesId}, ` +
          `colorId=${colorId}. Redirecting to pose ${firstAvailablePose}.`,
      );
      dispatchToOutfit({ type: "setPose", pose: firstAvailablePose });
    }
  }, [
    loading,
    selectedPoseIsAvailable,
    firstAvailablePose,
    speciesId,
    colorId,
    pose,
    toast,
    dispatchToOutfit,
  ]);

  // This is a low-stakes enough control, where enough pairs don't have data
  // anyway, that I think I want to just not draw attention to failures.
  if (error) {
    return null;
  }

  const numStandardPoses = Object.values(poseInfos).filter(
    (p) => p.isAvailable && STANDARD_POSES.includes(p.pose),
  ).length;

  const onChangePose = (e) => {
    dispatchToOutfit({ type: "setPose", pose: e.target.value });
  };

  const onChangeStyle = (altStyleId) => {
    dispatchToOutfit({ type: "setStyle", altStyleId });
  };

  return (
    <Popover
      placement={placement}
      returnFocusOnClose
      onOpen={() => {
        setIsOpen(true);
        onLockFocus();
      }}
      onClose={() => {
        setIsOpen(false);
        onUnlockFocus();
      }}
      initialFocusRef={initialFocusRef}
      isLazy
      lazyBehavior="keepMounted"
    >
      {() => (
        <>
          <PopoverTrigger>
            <PosePickerButton
              pose={pose}
              altStyle={altStyle}
              isOpen={isOpen}
              loading={loading}
              {...props}
            />
          </PopoverTrigger>
          <Portal>
            <PopoverContent>
              <Tabs
                size="sm"
                variant="soft-rounded"
                display="flex"
                flexDirection={{ base: "column", md: "column-reverse" }}
                paddingY="2"
                gap="2"
                index={tabIndex}
                onChange={setTabIndex}
              >
                <TabList paddingX="2" paddingY="0">
                  <Tab width="50%">Expressions</Tab>
                  <Tab width="50%">Styles</Tab>
                </TabList>
                <TabPanels position="relative">
                  <TabPanel paddingX="4" paddingY="0">
                    {isInSupportMode ? (
                      <PosePickerSupport
                        speciesId={speciesId}
                        colorId={colorId}
                        pose={pose}
                        appearanceId={appearanceId}
                        initialFocusRef={
                          tabIndex === 0 ? initialFocusRef : null
                        }
                        dispatchToOutfit={dispatchToOutfit}
                      />
                    ) : (
                      <>
                        <PosePickerTable
                          poseInfos={poseInfos}
                          onChange={onChangePose}
                          initialFocusRef={
                            tabIndex === 0 ? initialFocusRef : null
                          }
                        />
                        {numStandardPoses == 0 && (
                          <PosePickerEmptyExplanation />
                        )}
                      </>
                    )}
                    <SupportOnly>
                      <Box position="absolute" top="1" left="3">
                        <PosePickerSupportSwitch
                          isChecked={isInSupportMode}
                          onChange={(e) => setIsInSupportMode(e.target.checked)}
                        />
                      </Box>
                    </SupportOnly>
                  </TabPanel>
                  <TabPanel paddingX="4" paddingY="0">
                    <StyleSelect
                      selectedStyleId={altStyleId}
                      altStyles={altStyles}
                      onChange={onChangeStyle}
                      initialFocusRef={tabIndex === 1 ? initialFocusRef : null}
                    />
                    <StyleExplanation />
                  </TabPanel>
                </TabPanels>
              </Tabs>
              <PopoverArrow />
            </PopoverContent>
          </Portal>
        </>
      )}
    </Popover>
  );
}

const PosePickerButton = React.forwardRef(
  ({ pose, altStyle, isOpen, loading, ...props }, ref) => {
    const theme = useTheme();

    const icon = altStyle != null ? twemojiSunglasses : getIcon(pose);
    const label = altStyle != null ? altStyle.seriesName : getLabel(pose);

    return (
      <ClassNames>
        {({ css, cx }) => (
          <Button
            variant="unstyled"
            textShadow={`${theme.colors.blackAlpha["700"]} 0 1px 2px`}
            d="flex"
            alignItems="center"
            justifyContent="center"
            _focus={{ borderColor: "gray.50" }}
            _hover={{ borderColor: "gray.50" }}
            outline="initial"
            fontSize="sm"
            fontWeight="normal"
            minWidth="12ch"
            disabled={loading}
            className={cx(
              css`
              border: 1px solid transparent !important;
              color: ${theme.colors.gray["100"]};
              cursor: ${loading ? "wait" : "pointer"} !important;
              transition:
                color 0.2s,
                background: 0.2s,
                border-color 0.2s !important;
              padding-left: 0.75em;
              padding-right: 0.5em;

              &:hover,
              &.is-open {
                border-color: ${theme.colors.gray["50"]} !important;
                color: ${theme.colors.gray["50"]};
                background: ${theme.colors.blackAlpha["600"]};
                text-shadow: transparent 0 1px 2px;
              }

              &:focus {
                border-color: ${theme.colors.gray["50"]} !important;
                box-shadow: ${theme.shadows.outline};
              }

              &.is-open {
                border-width: 2px !important;
              }
            `,
              isOpen && "is-open",
            )}
            {...props}
            ref={ref}
          >
            <EmojiImage src={icon} alt="Style" />
            <Box width=".5em" />
            {label}
            <Box width=".5em" />
            <ChevronDownIcon />
          </Button>
        )}
      </ClassNames>
    );
  },
);

function PosePickerTable({ poseInfos, onChange, initialFocusRef }) {
  return (
    <Box display="flex" flexDirection="column" alignItems="center">
      <table width="100%">
        <thead>
          <tr>
            <th />
            <Cell as="th">
              <EmojiImage src={twemojiSmile} alt="Happy" />
            </Cell>
            <Cell as="th">
              <EmojiImage src={twemojiCry} alt="Sad" />
            </Cell>
            <Cell as="th">
              <EmojiImage src={twemojiSick} alt="Sick" />
            </Cell>
          </tr>
        </thead>
        <tbody>
          <tr>
            <Cell as="th">
              <EmojiImage src={twemojiMasc} alt="Masculine" />
            </Cell>
            <Cell as="td">
              <PoseOption
                poseInfo={poseInfos.happyMasc}
                onChange={onChange}
                inputRef={poseInfos.happyMasc.isSelected && initialFocusRef}
              />
            </Cell>
            <Cell as="td">
              <PoseOption
                poseInfo={poseInfos.sadMasc}
                onChange={onChange}
                inputRef={poseInfos.sadMasc.isSelected && initialFocusRef}
              />
            </Cell>
            <Cell as="td">
              <PoseOption
                poseInfo={poseInfos.sickMasc}
                onChange={onChange}
                inputRef={poseInfos.sickMasc.isSelected && initialFocusRef}
              />
            </Cell>
          </tr>
          <tr>
            <Cell as="th">
              <EmojiImage src={twemojiFem} alt="Feminine" />
            </Cell>
            <Cell as="td">
              <PoseOption
                poseInfo={poseInfos.happyFem}
                onChange={onChange}
                inputRef={poseInfos.happyFem.isSelected && initialFocusRef}
              />
            </Cell>
            <Cell as="td">
              <PoseOption
                poseInfo={poseInfos.sadFem}
                onChange={onChange}
                inputRef={poseInfos.sadFem.isSelected && initialFocusRef}
              />
            </Cell>
            <Cell as="td">
              <PoseOption
                poseInfo={poseInfos.sickFem}
                onChange={onChange}
                inputRef={poseInfos.sickFem.isSelected && initialFocusRef}
              />
            </Cell>
          </tr>
        </tbody>
      </table>
      {poseInfos.unconverted.isAvailable && (
        <Flex
          align="center"
          justify="center"
          gap="1"
          marginTop="2"
          marginBottom="2"
        >
          <PoseOption
            poseInfo={poseInfos.unconverted}
            onChange={onChange}
            inputRef={poseInfos.unconverted.isSelected && initialFocusRef}
            size="sm"
            label="Retired UC"
          />
          <RetiredUCWarning isSelected={poseInfos.unconverted.isSelected} />
        </Flex>
      )}
    </Box>
  );
}

function Cell({ children, as }) {
  const Tag = as;
  return (
    <Tag>
      <Flex justify="center" p="1">
        {children}
      </Flex>
    </Tag>
  );
}

const EMOTION_STRINGS = {
  HAPPY_MASC: "Happy",
  HAPPY_FEM: "Happy",
  SAD_MASC: "Sad",
  SAD_FEM: "Sad",
  SICK_MASC: "Sick",
  SICK_FEM: "Sick",
};

const GENDER_PRESENTATION_STRINGS = {
  HAPPY_MASC: "Masculine",
  SAD_MASC: "Masculine",
  SICK_MASC: "Masculine",
  HAPPY_FEM: "Feminine",
  SAD_FEM: "Feminine",
  SICK_FEM: "Feminine",
};

const STANDARD_POSES = Object.keys(EMOTION_STRINGS);

function PoseOption({
  poseInfo,
  onChange,
  inputRef,
  size = "md",
  label,
  ...otherProps
}) {
  const theme = useTheme();
  const genderPresentationStr = GENDER_PRESENTATION_STRINGS[poseInfo.pose];
  const emotionStr = EMOTION_STRINGS[poseInfo.pose];

  let poseName =
    poseInfo.pose === "UNCONVERTED"
      ? "Unconverted"
      : `${emotionStr} and ${genderPresentationStr}`;
  if (!poseInfo.isAvailable) {
    poseName += ` (not modeled yet)`;
  }

  const borderColor = useColorModeValue(
    theme.colors.green["600"],
    theme.colors.green["300"],
  );

  return (
    <ClassNames>
      {({ css, cx }) => (
        <Box
          as="label"
          cursor="pointer"
          display="flex"
          alignItems="center"
          borderColor={poseInfo.isSelected ? borderColor : "gray.400"}
          boxShadow={label ? "md" : "none"}
          borderWidth={label ? "1px" : "0"}
          borderRadius={label ? "full" : "0"}
          paddingRight={label ? "3" : "0"}
          onClick={(e) => {
            // HACK: We need the timeout to beat the popover's focus stealing!
            const input = e.currentTarget.querySelector("input");
            setTimeout(() => input.focus(), 0);
          }}
          {...otherProps}
        >
          <VisuallyHidden
            as="input"
            type="radio"
            aria-label={poseName}
            name="pose"
            value={poseInfo.pose}
            checked={poseInfo.isSelected}
            disabled={!poseInfo.isAvailable}
            onChange={onChange}
            ref={inputRef || null}
          />
          <Box
            aria-hidden
            borderRadius="full"
            boxShadow="md"
            overflow="hidden"
            width={size === "sm" ? "30px" : "50px"}
            height={size === "sm" ? "30px" : "50px"}
            title={
              poseInfo.isAvailable
                ? // A lil debug output, so that we can quickly identify glitched
                  // PetStates and manually mark them as glitched!
                  window.location.hostname.includes("localhost") &&
                  `#${poseInfo.id}`
                : "Not modeled yet"
            }
            position="relative"
            className={css`
              transform: scale(0.8);
              opacity: 0.8;
              transition: all 0.2s;

              input:checked + & {
                transform: scale(1);
                opacity: 1;
              }
            `}
          >
            <Box
              borderRadius="full"
              position="absolute"
              top="0"
              bottom="0"
              left="0"
              right="0"
              zIndex="2"
              className={cx(
                css`
                  border: 0px solid ${borderColor};
                  transition: border-width 0.2s;

                  &.not-available {
                    border-color: ${theme.colors.gray["500"]};
                    border-width: 1px;
                  }

                  input:checked + * & {
                    border-width: 1px;
                  }

                  input:focus + * & {
                    border-width: 3px;
                  }
                `,
                !poseInfo.isAvailable && "not-available",
              )}
            />
            {poseInfo.isAvailable ? (
              <Box
                width="100%"
                height="100%"
                transform={getTransform(poseInfo)}
              >
                <OutfitLayers visibleLayers={getVisibleLayers(poseInfo, [])} />
              </Box>
            ) : (
              <Flex align="center" justify="center" width="100%" height="100%">
                <EmojiImage src={twemojiQuestion} boxSize={24} />
              </Flex>
            )}
          </Box>
          {label && (
            <Box
              marginLeft="2"
              fontSize="xs"
              fontWeight={poseInfo.isSelected ? "bold" : "normal"}
            >
              {label}
            </Box>
          )}
        </Box>
      )}
    </ClassNames>
  );
}

function PosePickerEmptyExplanation() {
  return (
    <Box
      fontSize="xs"
      fontStyle="italic"
      textAlign="center"
      opacity="0.7"
      marginTop="2"
    >
      We're still working on labeling these! For now, we're just giving you one
      of the expressions we have.
    </Box>
  );
}

function RetiredUCWarning({ isSelected }) {
  return (
    <Popover placement="right" trigger="hover">
      <PopoverTrigger>
        <Box
          as="button"
          tabIndex="0"
          aria-label="Warning"
          cursor="help"
          lineHeight="1"
          opacity={isSelected ? "1" : "0.75"}
          transform={isSelected ? "scale(1)" : "scale(0.8)"}
          color={isSelected ? "yellow.500" : "inherit"}
          transition="all 0.2s"
          padding="1"
        >
          <WarningTwoIcon />
        </Box>
      </PopoverTrigger>
      <PopoverContent
        background="blackAlpha.800"
        borderColor="blackAlpha.900"
        color="white"
        padding="2"
        fontSize="sm"
      >
        "Unconverted" pets are no longer available on Neopets.com, and have been
        replaced with the very similar Styles feature. We're just keeping this
        as an archive!
      </PopoverContent>
    </Popover>
  );
}

function StyleSelect({
  selectedStyleId,
  altStyles,
  onChange,
  initialFocusRef,
}) {
  const defaultStyle = { id: null, adjectiveName: "Default" };

  const styles = [defaultStyle, ...altStyles];

  return (
    <Flex
      as="form"
      direction="column"
      gap="2"
      maxHeight={{ base: "30vh", md: "10rem" }}
      padding="4px"
      overflow="auto"
    >
      {styles.map((altStyle) => (
        <StyleOption
          key={altStyle.id}
          altStyle={altStyle}
          checked={selectedStyleId === altStyle.id}
          onChange={onChange}
          inputRef={selectedStyleId === altStyle.id ? initialFocusRef : null}
        />
      ))}
    </Flex>
  );
}

function StyleOption({ altStyle, checked, onChange, inputRef }) {
  const theme = useTheme();
  const selectedBorderColor = useColorModeValue(
    theme.colors.green["600"],
    theme.colors.green["300"],
  );
  const outlineShadow = useToken("shadows", "outline");

  return (
    <ClassNames>
      {({ css, cx }) => (
        <Box
          as="label"
          cursor="pointer"
          onClick={(e) => {
            // HACK: We need the timeout to beat the popover's focus stealing!
            const input = e.currentTarget.querySelector("input");
            setTimeout(() => input.focus(), 0);
          }}
        >
          <VisuallyHidden
            as="input"
            type="radio"
            name="style"
            value={altStyle.id}
            checked={checked}
            onChange={(e) => onChange(altStyle.id)}
            ref={inputRef}
          />
          <Flex
            alignItems="center"
            gap="2"
            borderRadius="md"
            // HACK: Don't let the thumbnail image overlap the border
            paddingLeft="2px"
            paddingY="2px"
            className={cx(css`
              border: 2px solid transparent;
              opacity: 0.8;
              transition: all 0.2s;

              input:focus + & {
                box-shadow: ${outlineShadow};
              }

              input:checked + & {
                border-color: ${selectedBorderColor};
                opacity: 1;
                font-weight: bold;
              }
            `)}
          >
            {altStyle.thumbnailUrl ? (
              <Box
                as="img"
                src={altStyle.thumbnailUrl}
                alt="Item thumbnail"
                width="40px"
                height="40px"
                loading="lazy"
              />
            ) : (
              <Box width="40px" height="40px" />
            )}
            <Box width="2" />
            {altStyle.adjectiveName}
          </Flex>
        </Box>
      )}
    </ClassNames>
  );
}

function StyleExplanation() {
  return (
    <Box
      fontSize="xs"
      fontStyle="italic"
      textAlign="center"
      opacity="0.7"
      marginTop="2"
    >
      <Box as="a" href="/alt-styles" target="_blank" textDecoration="underline">
        Alt Styles
      </Box>{" "}
      are NC items that override the pet's appearance via the{" "}
      <Box
        as="a"
        href="https://www.neopets.com/stylingchamber/"
        target="_blank"
        textDecoration="underline"
      >
        Styling Chamber
      </Box>
      . Not all items fit Alt Style pets. The pet's color doesn't have to match.
    </Box>
  );
}

function EmojiImage({ src, alt, boxSize = 16 }) {
  return <img src={src} alt={alt} width={boxSize} height={boxSize} />;
}

function usePoses(speciesId, colorId, selectedPose) {
  const { loading, error, data } = useQuery(
    gql`
      query PosePicker($speciesId: ID!, $colorId: ID!) {
        happyMasc: petAppearance(
          speciesId: $speciesId
          colorId: $colorId
          pose: HAPPY_MASC
        ) {
          ...PetAppearanceForPosePicker
        }
        sadMasc: petAppearance(
          speciesId: $speciesId
          colorId: $colorId
          pose: SAD_MASC
        ) {
          ...PetAppearanceForPosePicker
        }
        sickMasc: petAppearance(
          speciesId: $speciesId
          colorId: $colorId
          pose: SICK_MASC
        ) {
          ...PetAppearanceForPosePicker
        }
        happyFem: petAppearance(
          speciesId: $speciesId
          colorId: $colorId
          pose: HAPPY_FEM
        ) {
          ...PetAppearanceForPosePicker
        }
        sadFem: petAppearance(
          speciesId: $speciesId
          colorId: $colorId
          pose: SAD_FEM
        ) {
          ...PetAppearanceForPosePicker
        }
        sickFem: petAppearance(
          speciesId: $speciesId
          colorId: $colorId
          pose: SICK_FEM
        ) {
          ...PetAppearanceForPosePicker
        }
        unconverted: petAppearance(
          speciesId: $speciesId
          colorId: $colorId
          pose: UNCONVERTED
        ) {
          ...PetAppearanceForPosePicker
        }
        unknown: petAppearance(
          speciesId: $speciesId
          colorId: $colorId
          pose: UNKNOWN
        ) {
          ...PetAppearanceForPosePicker
        }
      }

      ${petAppearanceForPosePickerFragment}
    `,
    { variables: { speciesId, colorId }, onError: (e) => console.error(e) },
  );

  const poseInfos = {
    happyMasc: {
      ...data?.happyMasc,
      pose: "HAPPY_MASC",
      isAvailable: Boolean(data?.happyMasc),
      isSelected: selectedPose === "HAPPY_MASC",
    },
    sadMasc: {
      ...data?.sadMasc,
      pose: "SAD_MASC",
      isAvailable: Boolean(data?.sadMasc),
      isSelected: selectedPose === "SAD_MASC",
    },
    sickMasc: {
      ...data?.sickMasc,
      pose: "SICK_MASC",
      isAvailable: Boolean(data?.sickMasc),
      isSelected: selectedPose === "SICK_MASC",
    },
    happyFem: {
      ...data?.happyFem,
      pose: "HAPPY_FEM",
      isAvailable: Boolean(data?.happyFem),
      isSelected: selectedPose === "HAPPY_FEM",
    },
    sadFem: {
      ...data?.sadFem,
      pose: "SAD_FEM",
      isAvailable: Boolean(data?.sadFem),
      isSelected: selectedPose === "SAD_FEM",
    },
    sickFem: {
      ...data?.sickFem,
      pose: "SICK_FEM",
      isAvailable: Boolean(data?.sickFem),
      isSelected: selectedPose === "SICK_FEM",
    },
    unconverted: {
      ...data?.unconverted,
      pose: "UNCONVERTED",
      isAvailable: Boolean(data?.unconverted),
      isSelected: selectedPose === "UNCONVERTED",
    },
    unknown: {
      ...data?.unknown,
      pose: "UNKNOWN",
      isAvailable: Boolean(data?.unknown),
      isSelected: selectedPose === "UNKNOWN",
    },
  };

  return { loading, error, poseInfos };
}

function getIcon(pose) {
  if (["HAPPY_MASC", "HAPPY_FEM"].includes(pose)) {
    return twemojiSmile;
  } else if (["SAD_MASC", "SAD_FEM"].includes(pose)) {
    return twemojiCry;
  } else if (["SICK_MASC", "SICK_FEM"].includes(pose)) {
    return twemojiSick;
  } else if (pose === "UNCONVERTED") {
    return twemojiHourglass;
  } else {
    return twemojiSmile;
  }
}

function getLabel(pose) {
  if (pose === "HAPPY_MASC" || pose === "HAPPY_FEM") {
    return "Happy";
  } else if (pose === "SAD_MASC" || pose === "SAD_FEM") {
    return "Sad";
  } else if (pose === "SICK_MASC" || pose === "SICK_FEM") {
    return "Sick";
  } else if (pose === "UNCONVERTED") {
    return "Retired UC";
  } else {
    return "Default";
  }
}

function getTransform(poseInfo) {
  const { pose, bodyId } = poseInfo;
  if (pose === "UNCONVERTED") {
    return transformsByBodyId.default;
  }
  if (bodyId in transformsByBodyId) {
    return transformsByBodyId[bodyId];
  }
  return transformsByBodyId.default;
}

export const petAppearanceForPosePickerFragment = gql`
  fragment PetAppearanceForPosePicker on PetAppearance {
    id
    bodyId
    pose
    ...PetAppearanceForOutfitPreview
  }
  ${petAppearanceFragment}
`;

const transformsByBodyId = {
  93: "translate(-5px, 10px) scale(2.8)",
  106: "translate(-8px, 8px) scale(2.9)",
  47: "translate(-1px, 17px) scale(3)",
  84: "translate(-21px, 22px) scale(3.2)",
  146: "translate(2px, 15px) scale(3.3)",
  250: "translate(-14px, 28px) scale(3.4)",
  212: "translate(-4px, 8px) scale(2.9)",
  74: "translate(-26px, 30px) scale(3.0)",
  94: "translate(-4px, 8px) scale(3.1)",
  132: "translate(-14px, 18px) scale(3.0)",
  56: "translate(-7px, 24px) scale(2.9)",
  90: "translate(-16px, 20px) scale(3.5)",
  136: "translate(-11px, 18px) scale(3.0)",
  138: "translate(-14px, 26px) scale(3.5)",
  166: "translate(-13px, 24px) scale(3.1)",
  119: "translate(-6px, 29px) scale(3.1)",
  126: "translate(3px, 13px) scale(3.1)",
  67: "translate(2px, 27px) scale(3.4)",
  163: "translate(-7px, 16px) scale(3.1)",
  147: "translate(-2px, 15px) scale(3.0)",
  80: "translate(-2px, -17px) scale(3.0)",
  117: "translate(-14px, 16px) scale(3.6)",
  201: "translate(-16px, 16px) scale(3.2)",
  51: "translate(-2px, 6px) scale(3.2)",
  208: "translate(-3px, 6px) scale(3.7)",
  196: "translate(-7px, 19px) scale(5.2)",
  143: "translate(-16px, 20px) scale(3.5)",
  150: "translate(-3px, 24px) scale(3.2)",
  175: "translate(-9px, 15px) scale(3.4)",
  173: "translate(3px, 57px) scale(4.4)",
  199: "translate(-28px, 35px) scale(3.8)",
  52: "translate(-8px, 33px) scale(3.5)",
  109: "translate(-8px, -6px) scale(3.2)",
  134: "translate(-14px, 14px) scale(3.1)",
  95: "translate(-12px, 0px) scale(3.4)",
  96: "translate(6px, 23px) scale(3.3)",
  154: "translate(-20px, 25px) scale(3.6)",
  55: "translate(-16px, 28px) scale(4.0)",
  76: "translate(-8px, 11px) scale(3.0)",
  156: "translate(2px, 12px) scale(3.5)",
  78: "translate(-3px, 18px) scale(3.0)",
  191: "translate(-18px, 46px) scale(4.4)",
  187: "translate(-6px, 22px) scale(3.2)",
  46: "translate(-2px, 19px) scale(3.4)",
  178: "translate(-11px, 32px) scale(3.3)",
  100: "translate(-13px, 23px) scale(3.3)",
  130: "translate(-14px, 4px) scale(3.1)",
  188: "translate(-9px, 24px) scale(3.5)",
  257: "translate(-14px, 25px) scale(3.4)",
  206: "translate(-7px, 4px) scale(3.6)",
  101: "translate(-13px, 16px) scale(3.2)",
  68: "translate(-2px, 13px) scale(3.2)",
  182: "translate(-6px, 4px) scale(3.1)",
  180: "translate(-15px, 22px) scale(3.6)",
  306: "translate(1px, 14px) scale(3.1)",
  default: "scale(2.5)",
};

export default React.memo(PosePicker);