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

const meta = {
  title: 'Primitives/Button/Tests',
  component: PrimitiveButton,
  tags: ['!manifest'],
  args: {
    children: 'Button text',
  },
} satisfies Meta<typeof PrimitiveButton>;

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 button = canvas.getByRole('button');

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

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

    return (
      <PrimitiveButton {...args} onClick={() => setClicked(true)}>
        {clicked ? 'Clicked!' : 'Button text'}
      </PrimitiveButton>
    );
  },
};

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

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

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

    return (
      <PrimitiveButton {...args} onFocus={() => setFocused(true)}>
        {focused ? 'Focused!' : 'Button text'}
      </PrimitiveButton>
    );
  },
};

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

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

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

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

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

    return (
      <PrimitiveButton {...args} onBlur={() => setBlurred(true)}>
        {blurred ? 'Blurred!' : 'Button text'}
      </PrimitiveButton>
    );
  },
};

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

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

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

    return (
      <PrimitiveButton {...args} onMouseEnter={() => setHovered(true)}>
        {hovered ? 'Hovered!' : 'Button text'}
      </PrimitiveButton>
    );
  },
};

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

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

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

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

    return (
      <PrimitiveButton {...args} onMouseLeave={() => setLeft(true)}>
        {left ? 'Left!' : 'Button text'}
      </PrimitiveButton>
    );
  },
};

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

    await step('Press Enter key on the button', async () => {
      await userEvent.type(button, '{enter}');
    });

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

    return (
      <PrimitiveButton {...args} onKeyDown={() => setKeyPressed(true)}>
        {keyPressed ? 'Key Pressed!' : 'Button text'}
      </PrimitiveButton>
    );
  },
};

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

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

    await step('Check if button text remains "Button text"', async () => {
      await waitFor(async () => expect(button).toHaveTextContent('Button text'));
    });
  },
  render: function Render(args) {
    return (
      <PrimitiveButton {...args} disabled>
        Button text
      </PrimitiveButton>
    );
  },
};

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

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

    await step('Check if button does not have focus', async () => {
      await waitFor(async () => expect(button).not.toHaveFocus());
    });
  },
  render: function Render(args) {
    return (
      <PrimitiveButton {...args} disabled>
        Button text
      </PrimitiveButton>
    );
  },
};

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

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

    await step('Check if button does not have focus', async () => {
      await waitFor(async () => expect(button).not.toHaveFocus());
    });
  },
  render: function Render(args) {
    return (
      <PrimitiveButton {...args} disabled>
        Button text
      </PrimitiveButton>
    );
  },
};

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

    await step('Press Enter key on the disabled button', async () => {
      await userEvent.type(button, '{enter}');
    });

    await step('Check if button text remains "Button text"', async () => {
      await waitFor(async () => expect(button).toHaveTextContent('Button text'));
    });
  },
  render: function Render(args) {
    return (
      <PrimitiveButton {...args} disabled>
        Button text
      </PrimitiveButton>
    );
  },
};

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

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

    await step('Check if button text remains "Button text"', async () => {
      await waitFor(async () => expect(button).toHaveTextContent('Button text'));
    });
  },
  render: function Render(args) {
    return (
      <PrimitiveButton {...args} disabled>
        Button text
      </PrimitiveButton>
    );
  },
};

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

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

    await step('Check if button text remains "Button text"', async () => {
      await waitFor(async () => expect(button).toHaveTextContent('Button text'));
    });
  },
  render: function Render(args) {
    return (
      <PrimitiveButton {...args} disabled>
        Button text
      </PrimitiveButton>
    );
  },
};
