import { Meta, StoryObj } from '@storybook/react-webpack5';
import Link from '../../link';
import List from '../../list';
import { ListItem, type ListItemProps } from '../ListItem';
import { expect, userEvent, within, waitFor } from 'storybook/test';

const waitForFocus = async (assertion: () => Promise<void>, timeout = 3000) => {
  await waitFor(assertion, { timeout });
};

const waitForListItem = async (canvas: ReturnType<typeof within>, timeout = 3000) => {
  await waitFor(
    async () => {
      // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
      await expect(canvas.getByRole('listitem')).toBeInTheDocument();
    },
    { timeout },
  );
};

export default {
  component: ListItem,
  title: 'Content/ListItem/Tests/focus',
  tags: ['!autodocs', '!manifest'],
  parameters: {
    controls: { disable: true },
    actions: { disable: true },
    a11y: { disable: true },
    knobs: { disable: true },
  },
} satisfies Meta<ListItemProps>;
type Story = StoryObj<ListItemProps>;

const title = {
  full: 'Fully interactive',
  partial: 'Partially interactive',
};

const subtitle = {
  full: 'Whole item should be focusable, control should not.',
  partial: 'Only control should be focusable, not whole item.',
};

const additionalInfo = {
  static: (
    <ListItem.AdditionalInfo>
      Fully interactive ListItems don&apos;t allow any nested interactive elements like links or
      buttons within AdditionalInfo.
    </ListItem.AdditionalInfo>
  ),
  interactive: (
    <ListItem.AdditionalInfo
      action={{ label: 'appended to the end.', href: 'https://wise.com', target: '_blank' }}
    >
      This additional info has a focusable link
    </ListItem.AdditionalInfo>
  ),
};

const prompt = {
  static: <ListItem.Prompt sentiment="positive">Non-interactive prompt.</ListItem.Prompt>,
  interactive: (
    <ListItem.Prompt sentiment="positive">
      This prompt has a{' '}
      <Link href="https://wise.com" target="_blank" rel="noreferrer">
        single interactive element
      </Link>{' '}
      that spreads across the whole prompt area.
    </ListItem.Prompt>
  ),
};

const button = {
  full: (
    <ListItem.Button priority="secondary-neutral" onClick={() => {}}>
      Click me
    </ListItem.Button>
  ),
  partial: (
    <ListItem.Button partiallyInteractive priority="secondary-neutral" onClick={() => {}}>
      Click me
    </ListItem.Button>
  ),
};

export const FullyInteractive: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    await waitForListItem(canvas);
    await userEvent.tab();
    await waitForFocus(async () => {
      await expect(canvas.getByRole('button')).toHaveFocus();
    });
  },
  render: () => (
    <List>
      <ListItem
        title={title.full}
        subtitle={subtitle.full}
        additionalInfo={additionalInfo.static}
        control={button.full}
        prompt={prompt.static}
      />
    </List>
  ),
};

export const FullyInteractiveFocusedOnPrompt: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    await waitForListItem(canvas);

    await userEvent.tab();
    await waitForFocus(async () => {
      await expect(canvas.getByRole('button')).toHaveFocus();
    });

    await userEvent.tab();
    await waitForFocus(async () => {
      await expect(canvas.getByRole('link', { name: /^single interactive element/ })).toHaveFocus();
    });
  },
  render: () => (
    <List>
      <ListItem
        title={title.full}
        subtitle={subtitle.full}
        additionalInfo={additionalInfo.static}
        control={button.full}
        prompt={prompt.interactive}
      />
    </List>
  ),
};

export const PartiallyInteractiveFocusedOnControl: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    await waitForListItem(canvas);
    await userEvent.tab();
    await waitForFocus(async () => {
      await expect(canvas.getByRole('button')).toHaveFocus();
    });
  },
  render: () => (
    <List>
      <ListItem
        title={title.partial}
        subtitle={subtitle.partial}
        additionalInfo={additionalInfo.interactive}
        control={button.partial}
        prompt={prompt.interactive}
      />
    </List>
  ),
};

export const PartiallyInteractiveFocusedOnAdditionInfo: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    await waitForListItem(canvas);

    await userEvent.tab();
    await waitForFocus(async () => {
      await expect(canvas.getByRole('button')).toHaveFocus();
    });

    await userEvent.tab();
    await waitForFocus(async () => {
      await expect(canvas.getByRole('link', { name: /^appended to the end/ })).toHaveFocus();
    });
  },
  render: () => (
    <List>
      <ListItem
        title={title.partial}
        subtitle={subtitle.partial}
        additionalInfo={additionalInfo.interactive}
        control={button.partial}
        prompt={prompt.interactive}
      />
    </List>
  ),
};

export const PartiallyInteractiveFocusedOnPrompt: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    await waitForListItem(canvas);

    await userEvent.tab();
    await waitForFocus(async () => {
      await expect(canvas.getByRole('button')).toHaveFocus();
    });

    await userEvent.tab();
    await waitForFocus(async () => {
      await expect(canvas.getByRole('link', { name: /^appended to the end/ })).toHaveFocus();
    });

    await userEvent.tab();
    await waitForFocus(async () => {
      await expect(canvas.getByRole('link', { name: /^single interactive element/ })).toHaveFocus();
    });
  },
  render: () => (
    <List>
      <ListItem
        title={title.partial}
        subtitle={subtitle.partial}
        additionalInfo={additionalInfo.interactive}
        control={button.partial}
        prompt={prompt.interactive}
      />
    </List>
  ),
};

export const FullyInteractiveDisabled: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    await waitForListItem(canvas);

    await userEvent.tab();
    await waitForFocus(async () => {
      await expect(canvas.getByRole('button')).not.toHaveFocus();
    });
  },
  render: () => (
    <List>
      <ListItem
        disabled
        title={title.full}
        subtitle={subtitle.full}
        additionalInfo={additionalInfo.static}
        control={button.full}
        prompt={prompt.static}
      />
    </List>
  ),
};

export const PartiallyInteractiveDisabled: Story = {
  play: async ({ canvasElement }) => {
    const canvas = within(canvasElement);
    await waitForListItem(canvas);

    await userEvent.tab();
    await waitForFocus(async () => {
      await expect(canvas.getByRole('button')).not.toHaveFocus();
    });
  },
  render: () => (
    <List>
      <ListItem
        disabled
        title={title.partial}
        subtitle={subtitle.partial}
        additionalInfo={additionalInfo.interactive}
        control={button.partial}
        prompt={prompt.interactive}
      />
    </List>
  ),
};
