import { Meta, StoryObj } from '@storybook/react-webpack5';
import { fn } from 'storybook/test';
import { Freeze, ArrowRight } from '@transferwise/icons';
import List from '../../list';
import { ListItem } from '../ListItem';
import {
  SB_LIST_ITEM_ADDITIONAL_INFO as INFO,
  SB_LIST_ITEM_MEDIA as MEDIA,
} from '../_stories/subcomponents';
import { disableControls } from '../_stories/helpers';
import type { ListItemButtonProps } from './ListItemButton';

const hideControls = disableControls([
  'onClick',
  'children',
  'className',
  'addonStart',
  'addonEnd',
]);

/**
 * Convenience controls for previewing rich markup,
 * not otherwise possible via Storybook
 */
type PreviewStoryArgs = ListItemButtonProps & {
  previewAddonStart: boolean | ListItemButtonProps['addonStart'];
  previewAddonEnd: boolean | ListItemButtonProps['addonEnd'];
};

const previewArgTypes = {
  previewAddonStart: {
    control: 'boolean',
    name: 'Preview with `addonStart`',
    mapping: {
      true: { type: 'icon', value: <Freeze /> },
    },
    description: '**NB:** ListItem.Button only supports icon addons.',
    table: {
      category: 'Preview options',
      type: {
        summary: undefined,
      },
    },
  },
  previewAddonEnd: {
    control: 'boolean',
    name: 'Preview with `addonEnd`',
    mapping: {
      true: { type: 'icon', value: <ArrowRight /> },
    },
    description: '**NB:** ListItem.Button only supports icon addons.',
    table: {
      category: 'Preview options',
      type: {
        summary: undefined,
      },
    },
  },
} as const;

const getPropsForPreview = (args: PreviewStoryArgs) => {
  const { previewAddonStart, previewAddonEnd, ...props } = args as {
    previewAddonStart: ListItemButtonProps['addonStart'];
    previewAddonEnd: ListItemButtonProps['addonEnd'];
    [key: string]: any;
  };

  return [
    props,
    {
      addonStart: previewAddonStart,
      addonEnd: previewAddonEnd,
    },
  ] as const;
};

const meta: Meta<typeof ListItem.Button> = {
  component: ListItem.Button,
  title: 'Content/ListItem/ListItem.Button',
  args: {
    partiallyInteractive: false,
    priority: 'secondary-neutral',
    sentiment: undefined,
    loading: undefined,
    href: undefined,
    target: undefined,
    as: undefined,
    addonStart: undefined,
    addonEnd: undefined,
    className: undefined,
    children: 'Click me',
    onClick: fn(),
  },
  argTypes: {
    children: {
      table: {
        type: { summary: 'ReactNode' },
      },
    },
    loading: {
      control: 'boolean',
      description: 'Toggles the loading state',
    },
    priority: {
      control: 'radio',
      options: ['unset (undefined)', 'primary', 'secondary', 'secondary-neutral', 'tertiary'],
      mapping: {
        'unset (undefined)': undefined,
      },
      description: 'Sets a visual hierarchy amongst the buttons displayed on the screen',
    },
    sentiment: {
      options: ['unset (undefined)', 'default', 'negative'],
      mapping: {
        'unset (undefined)': undefined,
      },
      description:
        'Sets a visual hierarchy amongst the buttons displayed on the screen. <br /> **NB:** `negative` variant only affects the `primary` and `secondary` priorities.',
    },
    href: {
      control: 'text',
      description: 'If set, the component will render as an HTML anchor.',
    },
    target: {
      control: 'select',
      options: [undefined, '_blank', '_self', '_parent', '_top'],
      description:
        'The `target` attribute for HTML anchor. If set to `_blank`, `rel="noopener noreferrer"` is automatically added to the rendered node.',
    },
    addonStart: {
      description: 'Icon accessory to be displayed before the label',
    },
    addonEnd: {
      description: 'Icon accessory to be displayed after the label',
    },
    onClick: {
      table: {
        type: { summary: '(event: MouseEvent<HTMLButtonElement | HTMLAnchorElement>) => void' },
      },
    },
  },
} satisfies Meta<ListItemButtonProps>;

export default meta;

type Story = StoryObj<ListItemButtonProps>;

export const Playground: StoryObj<PreviewStoryArgs> = {
  render: (args: PreviewStoryArgs) => {
    const [props, previewProps] = getPropsForPreview(args);

    return (
      <List>
        <ListItem
          title="List item title"
          subtitle="Subtitle goes here"
          media={MEDIA.avatarSingle}
          control={<ListItem.Button {...props} {...previewProps} />}
          additionalInfo={INFO.nonInteractive}
        />
      </List>
    );
  },
  args: {
    previewAddonStart: false,
    previewAddonEnd: false,
  },
  argTypes: {
    ...previewArgTypes,
  },
};

/**
 * By default, ListItem is fully interactive, which means its whole surface is clickable
 * and triggers the control. To remain WCAG-compliant there can be no nested interactive
 * elements inside the item.<br />
 *
 * To work around this limitation, this control also allows for a partially interactive mode,
 * which can be enabled via `partiallyInteractive` prop. Once set, only the control will
 * remain interactive, which allows for interactive element to be attached to the
 * `ListItem.AdditionalInfo`. This allows for more complex layouts while still providing a
 * clear, accessible action point for users. <br />
 *
 * If you still require a fully interactive ListItem, you can alternatively use `ListItem.Prompt`
 * which allows for a single instance of a link or an inline button.
 *
 * Refer to the [design documentation](https://wise.design/components/list-item#interaction) for details.
 */
export const Interactivity: StoryObj<PreviewStoryArgs> = {
  args: {
    children: 'Action',
    previewAddonStart: false,
    previewAddonEnd: false,
  },
  argTypes: {
    ...hideControls(['partiallyInteractive']),
    ...previewArgTypes,
  },
  render: (args: PreviewStoryArgs) => {
    const [props, previewProps] = getPropsForPreview(args);

    return (
      <List>
        <ListItem
          title="Fully interactive ListItem"
          subtitle="Button in fully interactive context"
          media={MEDIA.avatarSingle}
          control={<ListItem.Button {...props} {...previewProps} />}
        />

        <ListItem
          title="Partially interactive ListItem"
          subtitle="Button in partially interactive context"
          media={MEDIA.avatarSingle}
          control={<ListItem.Button {...props} {...previewProps} partiallyInteractive />}
        />
      </List>
    );
  },
};

/**
 * If `href` prop is set, the component will be automatically rendered as an HTML anchor element.
 */
export const AsAnchor: StoryObj<PreviewStoryArgs> = {
  args: {
    children: 'Visit link',
    href: 'https://wise.com',
    target: '_blank',
    previewAddonStart: false,
    previewAddonEnd: false,
  },
  argTypes: {
    ...hideControls(['partiallyInteractive']),
    ...previewArgTypes,
  },
  render: (args: PreviewStoryArgs) => {
    const [props, previewProps] = getPropsForPreview(args);

    return (
      <List>
        <ListItem
          title="List item with a Button control"
          subtitle="But it renders as HTML anchor element"
          media={MEDIA.avatarSingle}
          control={<ListItem.Button {...props} {...previewProps} />}
        />
      </List>
    );
  },
};

/**
 * There are two different types of button – default and negative – designed to emphasise the nature of the action. <br />
 * **NB:** Sentiment only applies to `primary` and `secondary` priorities. <br />
 * [Design documentation](https://wise.design/components/button#types)
 */
export const Sentiment: StoryObj<PreviewStoryArgs> = {
  args: {
    children: 'Button text',
    previewAddonStart: false,
    previewAddonEnd: false,
  },
  argTypes: {
    ...hideControls(['sentiment', 'priority']),
    ...previewArgTypes,
  },
  render: (args: PreviewStoryArgs) => {
    const [props, previewProps] = getPropsForPreview(args);

    return (
      <List>
        <ListItem
          title="Default Sentiment - Primary"
          subtitle="Default sentiment with primary priority"
          media={MEDIA.avatarSingle}
          control={
            <ListItem.Button {...props} {...previewProps} priority="primary">
              Default Sentiment
            </ListItem.Button>
          }
        />
        <ListItem
          title="Negative Sentiment - Primary"
          subtitle="Negative sentiment with primary priority"
          media={MEDIA.avatarSingle}
          control={
            <ListItem.Button {...props} {...previewProps} priority="primary" sentiment="negative">
              Negative Sentiment
            </ListItem.Button>
          }
        />
        <ListItem
          title="Default Sentiment - Secondary"
          subtitle="Default sentiment with secondary priority"
          media={MEDIA.avatarSingle}
          control={
            <ListItem.Button {...props} {...previewProps} priority="secondary">
              Default Sentiment
            </ListItem.Button>
          }
        />
        <ListItem
          title="Negative Sentiment - Secondary"
          subtitle="Negative sentiment with secondary priority"
          media={MEDIA.avatarSingle}
          control={
            <ListItem.Button {...props} {...previewProps} priority="secondary" sentiment="negative">
              Negative Sentiment
            </ListItem.Button>
          }
        />
      </List>
    );
  },
};

/**
 * Priorities set a visual hierarchy amongst the buttons displayed on the
 * screen to help more important buttons to take precedence over others. <br />
 * [Design documentation](https://wise.design/components/button#priorities)
 */
export const Priority: StoryObj<PreviewStoryArgs> = {
  args: {
    children: 'Button text',
    previewAddonStart: false,
    previewAddonEnd: false,
  },
  argTypes: {
    ...hideControls(['priority']),
    ...previewArgTypes,
  },
  render: (args: PreviewStoryArgs) => {
    const [props, previewProps] = getPropsForPreview(args);

    return (
      <List>
        <ListItem
          title="Primary Priority"
          subtitle="Primary priority (default)"
          media={MEDIA.avatarSingle}
          control={
            <ListItem.Button {...props} {...previewProps} priority="primary">
              Primary Priority
            </ListItem.Button>
          }
        />
        <ListItem
          title="Secondary Priority"
          subtitle="Secondary priority"
          media={MEDIA.avatarSingle}
          control={
            <ListItem.Button {...props} {...previewProps} priority="secondary">
              Secondary Priority
            </ListItem.Button>
          }
        />
        <ListItem
          title="Secondary Neutral Priority"
          subtitle="Secondary neutral priority"
          media={MEDIA.avatarSingle}
          control={
            <ListItem.Button {...props} {...previewProps} priority="secondary-neutral">
              Secondary Neutral Priority
            </ListItem.Button>
          }
        />
        <ListItem
          title="Tertiary Priority"
          subtitle="Tertiary priority"
          media={MEDIA.avatarSingle}
          control={
            <ListItem.Button {...props} {...previewProps} priority="tertiary">
              Tertiary Priority
            </ListItem.Button>
          }
        />
      </List>
    );
  },
};

/**
 * Button in loading state.
 */
export const Loading: StoryObj<PreviewStoryArgs> = {
  args: {
    loading: true,
    previewAddonStart: false,
    previewAddonEnd: false,
  },
  argTypes: {
    ...hideControls(['loading']),
    ...previewArgTypes,
  },
  render: (args: PreviewStoryArgs) => {
    const [props, previewProps] = getPropsForPreview(args);

    return (
      <List>
        <ListItem
          title="Loading Button"
          subtitle="Button in loading state"
          media={MEDIA.avatarSingle}
          control={
            <ListItem.Button {...props} {...previewProps}>
              Loading Button
            </ListItem.Button>
          }
          additionalInfo={INFO.nonInteractive}
        />
      </List>
    );
  },
};

const addonProps = {
  addonStart: { type: 'icon', value: <Freeze /> },
  addonEnd: { type: 'icon', value: <ArrowRight /> },
} as const;

/**
 * Icons can be displayed before and after the button label. <br />
 * **NB:** ListItem.Button only supports icon addons.
 */
export const WithIcons: Story = {
  args: {
    children: 'Button text',
  },
  argTypes: hideControls(),
  render: (args: ListItemButtonProps) => {
    return (
      <List>
        <ListItem
          title="With Start Icon"
          subtitle="Button with icon before the label"
          media={MEDIA.avatarSingle}
          control={
            <ListItem.Button {...args} addonStart={addonProps.addonStart}>
              With start icon
            </ListItem.Button>
          }
        />
        <ListItem
          title="With End Icon"
          subtitle="Button with icon after the label"
          media={MEDIA.avatarSingle}
          control={
            <ListItem.Button {...args} addonEnd={addonProps.addonEnd}>
              With end icon
            </ListItem.Button>
          }
        />
        <ListItem
          title="With Both Icons"
          subtitle="Button with icons before and after the label"
          media={MEDIA.avatarSingle}
          control={
            <ListItem.Button {...args} {...addonProps}>
              With both icons
            </ListItem.Button>
          }
        />
      </List>
    );
  },
};
