import { useState } from 'react';
import { userEvent, within, expect, waitFor } from 'storybook/test';
import { Meta, StoryObj } from '@storybook/react-webpack5';
import PrimitiveAnchor from '..';

const meta = {
  title: 'Primitives/Anchor/Tests',
  component: PrimitiveAnchor,
  tags: ['!manifest'],
  args: {
    children: 'Click me',
    href: 'https://example.com',
  },
} satisfies Meta<typeof PrimitiveAnchor>;

export default meta;

type Story = StoryObj<typeof meta>;

const wait = async (duration = 500) =>
  new Promise<void>((resolve) => {
    setTimeout(resolve, duration);
  });

export const ClickInteraction: Story = {
  play: async ({ canvasElement, step }) => {
    const canvas = within(canvasElement);
    const link = canvas.getByRole('link');

    await step('Click the link', async () => {
      await userEvent.click(link);
    });

    await step('Check if link text changes to "Clicked!"', async () => {
      await waitFor(async () => expect(link).toHaveTextContent('Clicked!'));
    });
  },
  render: function Render(args) {
    const [clicked, setClicked] = useState(false);

    return (
      <PrimitiveAnchor
        {...args}
        onClick={(event) => {
          event.preventDefault();
          setClicked(true);
        }}
      >
        {clicked ? 'Clicked!' : 'Click me'}
      </PrimitiveAnchor>
    );
  },
};

export const FocusInteraction: Story = {
  play: async ({ canvasElement, step }) => {
    const canvas = within(canvasElement);
    const link = canvas.getByRole('link');

    await step('Tab to the link', async () => {
      await userEvent.tab();
    });

    await step('Check if link has focus and text changes to "Focused!"', async () => {
      await waitFor(async () => expect(link).toHaveFocus());
      await waitFor(async () => expect(link).toHaveTextContent('Focused!'));
    });
  },
  render: function Render(args) {
    const [focused, setFocused] = useState(false);

    return (
      <PrimitiveAnchor {...args} onFocus={() => setFocused(true)}>
        {focused ? 'Focused!' : 'Click me'}
      </PrimitiveAnchor>
    );
  },
};

export const BlurInteraction: Story = {
  play: async ({ canvasElement, step }) => {
    const canvas = within(canvasElement);
    const link = canvas.getByRole('link');

    await step('Tab to the link', async () => {
      await userEvent.tab();
    });

    await step('Check if link has focus', async () => {
      await waitFor(async () => expect(link).toHaveFocus());
    });

    await step('Tab away from the link', async () => {
      await userEvent.tab();
    });

    await step('Check if link loses focus and text changes to "Blurred!"', async () => {
      await waitFor(async () => expect(link).not.toHaveFocus());
      await waitFor(async () => expect(link).toHaveTextContent('Blurred!'));
    });
  },
  render: function Render(args) {
    const [blurred, setBlurred] = useState(false);

    return (
      <PrimitiveAnchor {...args} onBlur={() => setBlurred(true)}>
        {blurred ? 'Blurred!' : 'Click me'}
      </PrimitiveAnchor>
    );
  },
};

export const MouseEnterInteraction: Story = {
  play: async ({ canvasElement, step }) => {
    const canvas = within(canvasElement);
    const link = canvas.getByRole('link');

    await step('Hover over the link', async () => {
      await userEvent.hover(link);
    });

    await step('Check if link text changes to "Hovered!"', async () => {
      await waitFor(async () => expect(link).toHaveTextContent('Hovered!'));
    });
  },
  render: function Render(args) {
    const [hovered, setHovered] = useState(false);

    return (
      <PrimitiveAnchor {...args} onMouseEnter={() => setHovered(true)}>
        {hovered ? 'Hovered!' : 'Click me'}
      </PrimitiveAnchor>
    );
  },
};

export const MouseLeaveInteraction: Story = {
  play: async ({ canvasElement, step }) => {
    const canvas = within(canvasElement);
    const link = canvas.getByRole('link');

    await step('Hover over the link', async () => {
      await userEvent.hover(link);
    });

    await step('Unhover the link', async () => {
      await userEvent.unhover(link);
    });

    await step('Check if link text changes to "Left!"', async () => {
      await waitFor(async () => expect(link).toHaveTextContent('Left!'));
    });
  },
  render: function Render(args) {
    const [left, setLeft] = useState(false);

    return (
      <PrimitiveAnchor {...args} onMouseLeave={() => setLeft(true)}>
        {left ? 'Left!' : 'Click me'}
      </PrimitiveAnchor>
    );
  },
};

export const KeyDownInteraction: Story = {
  play: async ({ canvasElement, step }) => {
    const canvas = within(canvasElement);
    const link = canvas.getByRole('link');

    await step('Press ArrowDown key on the link', async () => {
      await userEvent.tab();
      await wait();
      await userEvent.keyboard('{ArrowDown}');
    });

    await step('Check if link text changes to "Key Pressed!"', async () => {
      await waitFor(async () => expect(link).toHaveTextContent('Key Pressed!'));
    });
  },
  render: function Render(args) {
    const [keyPressed, setKeyPressed] = useState(false);

    return (
      <PrimitiveAnchor
        {...args}
        onKeyDown={(event) => {
          if (event.key === 'ArrowDown') {
            event.preventDefault();
            setKeyPressed(true);
          }
        }}
      >
        {keyPressed ? 'Key Pressed!' : 'Click me'}
      </PrimitiveAnchor>
    );
  },
};

export const TabIndexBehavior: Story = {
  play: async ({ canvasElement, step }) => {
    const canvas = within(canvasElement);
    const link = canvas.getByRole('link');

    await step('Tab to the link', async () => {
      await userEvent.tab();
    });

    await step('Check if link does not have focus', async () => {
      await waitFor(async () => expect(link).not.toHaveFocus());
    });
  },
  render: function Render(args) {
    return (
      <PrimitiveAnchor {...args} disabled tabIndex={-1}>
        Click me
      </PrimitiveAnchor>
    );
  },
};
