import React from "react";
import { ClassNames } from "@emotion/react";
import {
  Box,
  Flex,
  IconButton,
  Skeleton,
  Tooltip,
  useColorModeValue,
  useTheme,
} from "@chakra-ui/react";
import { EditIcon, DeleteIcon, InfoIcon } from "@chakra-ui/icons";
import { loadable } from "../util";

import {
  ItemCardContent,
  ItemBadgeList,
  ItemKindBadge,
  MaybeAnimatedBadge,
  YouOwnThisBadge,
  YouWantThisBadge,
  getZoneBadges,
} from "../components/ItemCard";
import SupportOnly from "./support/SupportOnly";
import useSupport from "./support/useSupport";

const LoadableItemSupportDrawer = loadable(() =>
  import("./support/ItemSupportDrawer"),
);

/**
 * Item show a basic summary of an item, in the context of the current outfit!
 *
 * It also responds to the focus state of an `input` as its previous sibling.
 * This will be an invisible radio/checkbox that controls the actual wear
 * state.
 *
 * In fact, this component can't trigger wear or unwear events! When you click
 * it in the app, you're actually clicking a <label> that wraps the radio or
 * checkbox. Similarly, the parent provides the `onRemove` callback for the
 * Remove button.
 *
 * NOTE: This component is memoized with React.memo. It's surpisingly expensive
 *       to re-render, because Chakra components are a lil bit expensive from
 *       their internal complexity, and we have a lot of them here. And it can
 *       add up when there's a lot of Items in the list. This contributes to
 *       wearing/unwearing items being noticeably slower on lower-power
 *       devices.
 */
function Item({
  item,
  itemNameId,
  isWorn,
  isInOutfit,
  onRemove,
  isDisabled = false,
}) {
  const [supportDrawerIsOpen, setSupportDrawerIsOpen] = React.useState(false);

  return (
    <>
      <ItemContainer isDisabled={isDisabled}>
        <Box flex="1 1 0" minWidth="0">
          <ItemCardContent
            item={item}
            badges={<ItemBadges item={item} />}
            itemNameId={itemNameId}
            isWorn={isWorn}
            isDiabled={isDisabled}
            focusSelector={containerHasFocus}
          />
        </Box>
        <Box flex="0 0 auto" marginTop="5px">
          {isInOutfit && (
            <ItemActionButton
              icon={<DeleteIcon />}
              label="Remove"
              onClick={(e) => {
                onRemove(item.id);
                e.preventDefault();
              }}
            />
          )}
          <SupportOnly>
            <ItemActionButton
              icon={<EditIcon />}
              label="Support"
              onClick={(e) => {
                setSupportDrawerIsOpen(true);
                e.preventDefault();
              }}
            />
          </SupportOnly>
          <ItemActionButton
            icon={<InfoIcon />}
            label="More info"
            to={`/items/${item.id}`}
            target="_blank"
          />
        </Box>
      </ItemContainer>
      <SupportOnly>
        <LoadableItemSupportDrawer
          item={item}
          isOpen={supportDrawerIsOpen}
          onClose={() => setSupportDrawerIsOpen(false)}
        />
      </SupportOnly>
    </>
  );
}

/**
 * ItemSkeleton is a placeholder for when an Item is loading.
 */
function ItemSkeleton() {
  return (
    <ItemContainer isDisabled>
      <Skeleton width="50px" height="50px" />
      <Box width="3" />
      <Skeleton height="1.5rem" width="12rem" alignSelf="center" />
    </ItemContainer>
  );
}

/**
 * ItemContainer is the outermost element of an `Item`.
 *
 * It provides spacing, but also is responsible for a number of hover/focus/etc
 * styles - including for its children, who sometimes reference it as an
 * .item-container parent!
 */
function ItemContainer({ children, isDisabled = false }) {
  const theme = useTheme();

  const focusBackgroundColor = useColorModeValue(
    theme.colors.gray["100"],
    theme.colors.gray["700"],
  );

  const activeBorderColor = useColorModeValue(
    theme.colors.green["400"],
    theme.colors.green["500"],
  );

  const focusCheckedBorderColor = useColorModeValue(
    theme.colors.green["800"],
    theme.colors.green["300"],
  );

  return (
    <ClassNames>
      {({ css, cx }) => (
        <Box
          p="1"
          my="1"
          borderRadius="lg"
          d="flex"
          cursor={isDisabled ? undefined : "pointer"}
          border="1px"
          borderColor="transparent"
          className={cx([
            "item-container",
            !isDisabled &&
              css`
                &:hover,
                input:focus + & {
                  background-color: ${focusBackgroundColor};
                }

                input:active + & {
                  border-color: ${activeBorderColor};
                }

                input:checked:focus + & {
                  border-color: ${focusCheckedBorderColor};
                }
              `,
          ])}
        >
          {children}
        </Box>
      )}
    </ClassNames>
  );
}

function ItemBadges({ item }) {
  const { isSupportUser } = useSupport();
  const occupiedZones = item.appearanceOn.layers.map((l) => l.zone);
  const restrictedZones = item.appearanceOn.restrictedZones.filter(
    (z) => z.isCommonlyUsedByItems,
  );
  const isMaybeAnimated = item.appearanceOn.layers.some(
    (l) => l.canvasMovieLibraryUrl,
  );

  return (
    <ItemBadgeList>
      <ItemKindBadge isNc={item.isNc} isPb={item.isPb} />
      {
        // This badge is unreliable, but it's helpful for looking for animated
        // items to test, so we show it only to support. We use this form
        // instead of <SupportOnly />, to avoid adding extra badge list spacing
        // on the additional empty child.
        isMaybeAnimated && isSupportUser && <MaybeAnimatedBadge />
      }
      {getZoneBadges(occupiedZones, { variant: "occupies" })}
      {getZoneBadges(restrictedZones, { variant: "restricts" })}
      {item.currentUserOwnsThis && <YouOwnThisBadge variant="medium" />}
      {item.currentUserWantsThis && <YouWantThisBadge variant="medium" />}
    </ItemBadgeList>
  );
}

/**
 * ItemActionButton is one of a list of actions a user can take for this item.
 */
function ItemActionButton({ icon, label, to, onClick, ...props }) {
  const theme = useTheme();

  const focusBackgroundColor = useColorModeValue(
    theme.colors.gray["300"],
    theme.colors.gray["800"],
  );
  const focusColor = useColorModeValue(
    theme.colors.gray["700"],
    theme.colors.gray["200"],
  );

  return (
    <ClassNames>
      {({ css }) => (
        <Tooltip label={label} placement="top">
          <LinkOrButton
            {...props}
            component={IconButton}
            href={to}
            icon={icon}
            aria-label={label}
            variant="ghost"
            color="gray.400"
            onClick={onClick}
            className={css`
              opacity: 0;
              transition: all 0.2s;

              ${containerHasFocus} {
                opacity: 1;
              }

              &:focus,
              &:hover {
                opacity: 1;
                background-color: ${focusBackgroundColor};
                color: ${focusColor};
              }

              /* On touch devices, always show the buttons! This avoids having to
           * tap to reveal them (which toggles the item), or worse,
           * accidentally tapping a hidden button without realizing! */
              @media (hover: none) {
                opacity: 1;
              }
            `}
          />
        </Tooltip>
      )}
    </ClassNames>
  );
}

function LinkOrButton({ href, component, ...props }) {
  const ButtonComponent = component;
  if (href != null) {
    return <ButtonComponent as="a" href={href} {...props} />;
  } else {
    return <ButtonComponent {...props} />;
  }
}

/**
 * ItemListContainer is a container for Item components! Wrap your Item
 * components in this to ensure a consistent list layout.
 */
export function ItemListContainer({ children, ...props }) {
  return (
    <Flex direction="column" {...props}>
      {children}
    </Flex>
  );
}

/**
 * ItemListSkeleton is a placeholder for when an ItemListContainer and its
 * Items are loading.
 */
export function ItemListSkeleton({ count, ...props }) {
  return (
    <ItemListContainer {...props}>
      {Array.from({ length: count }).map((_, i) => (
        <ItemSkeleton key={i} />
      ))}
    </ItemListContainer>
  );
}

/**
 * containerHasFocus is a common CSS selector, for the case where our parent
 * .item-container is hovered or the adjacent hidden radio/checkbox is
 * focused.
 */
const containerHasFocus =
  ".item-container:hover &, input:focus + .item-container &";

export default React.memo(Item);