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);