import { useState, useEffect } from "react";
import { Meta, Canvas, ArgsTable, Story } from "@storybook/blocks";
import { Accessibility, ComponentHeading } from "../../storybook-components";
import { tokens } from "../../style-dictionary/dist/tokens";
import { Icon, ICON_TYPE } from "../Icon";
import { Box } from "../Box";
import { Flex } from "../Flex";
import { Button, BUTTON_VARIANT } from "../Button";
import { Heading } from "../Heading";
import { Stack } from "../Stack";
import { Link } from "../Link";
import { Text } from "../Text";
import { Tooltip } from "./Tooltip";
import { TooltipLink } from "./TooltipLink";
import { useTooltipContext } from "./context";

<Meta title="Components/Popovers/Tooltip" component={Tooltip} />

<ComponentHeading
  componentName="Tooltip"
  description="A tooltip is a popover used to show a small amount of information when the mouse hovers over a reference element."
  sourcePath="src/components/Tooltip/Tooltip.tsx"
  waiAriaPath="https://www.w3.org/TR/wai-aria-practices/#tooltip"
/>

## Installation

```jsx
import { Tooltip } from "@conveyorhq/arrow-ds";
```

## Basic usage

<Canvas>
  <Story name="Tooltip Uncontrolled">
    <Box className="px-8">
      <Tooltip
        placement="top"
        referenceElement={
          <Stack className="inline-flex p-1" spacing={1}>
            <Icon icon={ICON_TYPE.USER} />
            Hover me
          </Stack>
        }
      >
        This is the text in the Tooltip
      </Tooltip>
    </Box>
  </Story>
</Canvas>

## API

<ArgsTable of={Tooltip} />

## Events

The following events are are placed on the reference element in order to control
the visibility of the tooltip popover.

- `onMouseEnter` (show)
- `onMouseLeave` (hide)
- `onFocus` (show)
- `onBlur` (hide)

If the reference element includes any one of these events, it’ll be passed on
and merged with the actions listed above. See the demo “Passing custom event
handlers” below.

<Accessibility
  description={
    <>
      Adheres to the{" "}
      <Link
        href="https://www.w3.org/TR/wai-aria-practices/#tooltip"
        target="_blank"
      >
        tooltip widget requirements
      </Link>
      .
    </>
  }
  keyboardInteractions={[
    {
      key: "Escape",
      description: "Dismisses the tooltip",
    },
  ]}
/>

## Demos

### Controlled

<Canvas>
  <Story name="Tooltip Controlled">
    <Box className="px-32 pt-10">
      <Tooltip
        placement="left"
        referenceElement={<Icon className="ml-10" icon={ICON_TYPE.USER} />}
        isVisible
      >
        Left
      </Tooltip>
      <Tooltip
        referenceElement={<Icon className="ml-10" icon={ICON_TYPE.USER} />}
        isVisible
      >
        Bottom (default)
      </Tooltip>
      <Tooltip
        placement="top"
        referenceElement={<Icon className="ml-10" icon={ICON_TYPE.USER} />}
        isVisible
      >
        Top
      </Tooltip>
      <Tooltip
        placement="right"
        referenceElement={<Icon className="ml-10" icon={ICON_TYPE.USER} />}
        isVisible
      >
        Right
      </Tooltip>
    </Box>
  </Story>
</Canvas>

### Tooltip with style attribute

<Canvas>
  <Story name="Tooltip with style attribute">
    <Box className="px-32 pt-10">
      <Tooltip
        placement="right"
        referenceElement={<Icon className="ml-10" icon={ICON_TYPE.USER} />}
        style={{
          color: tokens.color.gold["300"],
          zIndex: tokens.zIndex.modal,
        }}
        isVisible
      >
        With gold text and a custom z-index
      </Tooltip>
    </Box>
  </Story>
</Canvas>

### Updating children

<Canvas>
  <Story name="Tooltip with updating children">
    {() => {
      const defaultText =
        "This tooltip will reposition itself when its children change.";
      const [text, setText] = useState(defaultText);
      useEffect(() => {
        setTimeout(() => {
          if (text === defaultText) {
            setText("Like this");
          } else {
            setText(defaultText);
          }
        }, 3000);
      }, [text]);
      return (
        <Box className="px-32 pt-10">
          <Tooltip
            placement="top"
            referenceElement={
              <Box className="inline-flex">This is the reference element.</Box>
            }
            isVisible
          >
            {text}
          </Tooltip>
        </Box>
      );
    }}
  </Story>
</Canvas>

### Wrapping content with custom formatting

The tooltip component has a default max-width of 256px, so the content will
automatically wrap if it’s too long. The max-width can be overridden by passing
it through the `style` attribute.

<Canvas>
  <Story name="Tooltip with wrapping content with custom formatting">
    <Box className="px-32 pt-10">
      <Tooltip
        placement="top"
        referenceElement={
          <Button variant={BUTTON_VARIANT.SECONDARY} icon={ICON_TYPE.PLAY}>
            Play
          </Button>
        }
      >
        <Stack className="leading-normal" spacing={3}>
          <Heading.H4 className="text-white">
            Watch your risk profile change
          </Heading.H4>
          <Text className="text-gray-500">
            Once you’ve got more than one version of your Risk Registry, you’ll
            be able to click this button to see how it’s changed over time!
          </Text>
        </Stack>
      </Tooltip>
    </Box>
  </Story>
</Canvas>

### Leaving children empty

If the tooltip content (i.e., `children` prop) is empty for any reason, the
component will return the `referenceElement` instead.

<Canvas>
  <Story name="Tooltip empty content">
    {() => {
      const errors = [];
      return (
        <Tooltip
          placement="top"
          referenceElement={<Button>Save Changes</Button>}
        >
          {errors.length > 0 ? "You have errors" : ""}
        </Tooltip>
      );
    }}
  </Story>
</Canvas>

### Passing custom event handlers

<Canvas>
  <Story name="Tooltip passing custom event handlers">
    <Tooltip
      placement="top"
      referenceElement={
        <Button
          onMouseEnter={() => console.log("Do something fun")}
          onMouseLeave={() => console.log("Bye")}
        >
          Save Changes
        </Button>
      }
    >
      This will save your changes
    </Tooltip>
  </Story>
</Canvas>

### Accessing internal state

If you need to access the internal `isOpen` state, you can use the
context in a custom component to access it. Keep in mind that your
component must accept a ref and include a spread of any remaining props.

export const MyButton = React.forwardRef((props, ref) => {
  const { isOpen } = useTooltipContext();
  return (
    <Button
      variant={BUTTON_VARIANT.SECONDARY}
      icon={isOpen ? ICON_TYPE.MINUS : ICON_TYPE.CHECK}
      // forwarded ref used in the reference element
      ref={ref}
      // spreading props to allow the event handlers for controlling the popover
      {...props}
    />
  );
});

<Canvas>
  <Story name="Tooltip internal state">
    {() => {
      return (
        <Tooltip placement="top" referenceElement={<MyButton />}>
          Remove item
        </Tooltip>
      );
    }}
  </Story>
</Canvas>

## Light theme

<Canvas>
  <Story name="Tooltip light theme">
    <Box className="px-8">
      <Tooltip
        placement="top"
        theme="light"
        referenceElement={
          <Stack className="inline-flex p-1" spacing={1}>
            <Icon icon={ICON_TYPE.USER} />
            Hover me
          </Stack>
        }
      >
        This is the text in the Tooltip
      </Tooltip>
    </Box>
  </Story>
</Canvas>

## Hide arrow

<Canvas>
  <Story name="Tooltip hide arrow">
    <Box className="px-8">
      <Tooltip
        hideArrow
        placement="right"
        referenceElement={
          <Flex className="w-8 h-8 border rounded items-center justify-center">
            <Icon icon={ICON_TYPE.USERS} />
          </Flex>
        }
      >
        Connections
      </Tooltip>
    </Box>
  </Story>
</Canvas>

## Delay

The default delay is 200ms. The delay is necessary to keep the popover open when
the mouse moves from the reference element to the popover. This is important if
the popover has a link or some other content that can be interacted with.

The delay can be a number in ms, or it can be an object with `show` and `hide`
keys. Both object keys are optional, and if either are omitted, then the default
delay will be used.

<Canvas>
  <Story name="Tooltip delay">
    <Box className="px-8">
      <Tooltip
        placement="right"
        delay={{
          show: 100,
          hide: 1000,
        }}
        referenceElement={<Stack className="inline-flex p-1">Hover me</Stack>}
      >
        <Flex className="flex-col" style={{ width: "150px" }}>
          <Flex>I have a 100ms show delay and 1000ms hide delay</Flex>
          <TooltipLink href="/">This is a Tooltip link</TooltipLink>
        </Flex>
      </Tooltip>
    </Box>
  </Story>
</Canvas>
