import { useState } from 'react';
import { Meta, StoryObj } from '@storybook/react-webpack5';
import { MultiCurrency, Plus } from '@transferwise/icons';
import { lorem5, lorem10 } from '../../test-utils';
import Emphasis from '../../emphasis';
import Link from '../../link';
import List from '../../list';
import { ListItem, type ListItemProps } from '../ListItem';
import { LISTITEM_CQ } from '../constants';
import {
  SB_LIST_ITEM_CONTROLS as CONTROLS,
  SB_LIST_ITEM_ADDITIONAL_INFO as ADDITIONAL_INFO,
  SB_LIST_ITEM_PROMPTS as PROMPTS,
  SB_LIST_ITEM_MEDIA as MEDIA,
} from './subcomponents';
import { disableControls, storySourceWithoutNoise, withoutKey } from './helpers';

const hideControls = disableControls([
  'additionalInfo',
  'control',
  'prompt',
  'media',
  'className',
  'id',
  'as',
]);

/**
 * List Items let users review or select options from a dynamic list.<br />
 * For more details please refer to the [release notes](https://transferwise.atlassian.net/wiki/spaces/DS/pages/3647251055/List+Item+release+notes) and the [design spec](https://wise.design/components/list-item). <br />
 *
 * > This component replaces now deprecated `LegacyListItem`, `Summary` and all `*Option` components (run codemod to migrate: **`npx @wise/wds-codemods@latest info-prompt`**).
 */
export default {
  component: ListItem,
  tags: ['new'],
  subcomponents: {
    'ListItem.AdditionalInfo': ListItem.AdditionalInfo,
    'ListItem.Prompt': ListItem.Prompt,
    'ListItem.Button': ListItem.Button,
    'ListItem.IconButton': ListItem.IconButton,
    'ListItem.Navigation': ListItem.Navigation,
    'ListItem.Checkbox': ListItem.Checkbox,
    'ListItem.Radio': ListItem.Radio,
    'ListItem.Switch': ListItem.Switch,
    'ListItem.AvatarView': ListItem.AvatarView,
    'ListItem.AvatarLayout': ListItem.AvatarLayout,
    'ListItem.Image': ListItem.Image,
  },
  title: 'Content/ListItem',
  args: {
    title: 'List item title',
    subtitle: 'Subtitle goes here',
    valueTitle: '100 GBP',
    valueSubtitle: '100 USD',
    disabled: false,
    disabledPromptMessage: undefined,
    spotlight: undefined,
    inverted: false,
    valueColumnWidth: undefined,
  },
  argTypes: {
    spotlight: {
      control: 'radio',
      options: ['unset (undefined)', 'active', 'inactive'],
      mapping: {
        'unset (undefined)': undefined,
        active: 'active',
        inactive: 'inactive',
      },
    },
  },
} satisfies Meta<ListItemProps>;

type Story = StoryObj<ListItemProps>;

/**
 * Convenience controls for previewing rich markup,
 * not otherwise possible via Storybook
 */
type PreviewStoryArgs = ListItemProps & {
  previewInteractivity: 'non-interactive' | 'fully-interactive' | 'partially-interactive';
  previewControlFull: ListItemProps['control'];
  previewControlPartial: ListItemProps['control'];
  previewAdditionalInfoFull: ListItemProps['additionalInfo'];
  previewAdditionalInfoPartial: ListItemProps['additionalInfo'];
  previewPrompt: ListItemProps['prompt'];
  previewMedia: ListItemProps['media'];
};

const previewArgGroup = {
  category: 'Storybook Preview options',
  type: {
    summary: undefined,
  },
};

const previewArgTypes = {
  previewInteractivity: {
    name: 'Preview different interactivity',
    control: 'radio',
    options: ['non-interactive', 'fully-interactive', 'partially-interactive'],
    table: previewArgGroup,
  },

  previewControlFull: {
    name: 'Preview with `control`',
    if: {
      arg: 'previewInteractivity',
      eq: 'fully-interactive',
    },
    control: 'select',
    options: [
      'unset (undefined)',
      'Button',
      'Button as anchor',
      'IconButton',
      'IconButton as anchor',
      'Navigation',
      'Navigation as button',
      'Checkbox',
      'Radio',
      'Switch',
    ],
    mapping: {
      'unset (undefined)': undefined,
      Button: CONTROLS.button,
      'Button as anchor': CONTROLS.buttonAsLink,
      IconButton: CONTROLS.iconButton,
      'IconButton as anchor': CONTROLS.iconButtonAsLink,
      Navigation: CONTROLS.navigation,
      'Navigation as button': CONTROLS.navigationAsButton,
      Checkbox: CONTROLS.checkbox,
      Radio: CONTROLS.radio,
      Switch: CONTROLS.switch,
    },
    table: previewArgGroup,
  },

  previewControlPartial: {
    name: 'Preview with `control`',
    if: {
      arg: 'previewInteractivity',
      eq: 'partially-interactive',
    },
    control: 'select',
    options: [
      'unset (undefined)',
      'Button',
      'Button as anchor',
      'IconButton',
      'IconButton as anchor',
    ],
    mapping: {
      'unset (undefined)': undefined,
      Button: CONTROLS.partialButton,
      'Button as anchor': CONTROLS.partialButtonAsLink,
      IconButton: CONTROLS.partialIconButton,
      'IconButton as anchor': CONTROLS.partialIconButtonAsLink,
    },
    table: previewArgGroup,
  },

  previewAdditionalInfoFull: {
    name: 'Preview with `additionalInfo`',
    if: {
      arg: 'previewInteractivity',
      eq: 'fully-interactive',
    },
    control: 'radio',
    options: ['unset (undefined)', 'non-interactive'],
    mapping: {
      'unset (undefined)': undefined,
      'non-interactive': ADDITIONAL_INFO.nonInteractive,
    },
    table: previewArgGroup,
  },
  previewAdditionalInfoPartial: {
    name: 'Preview with `additionalInfo`',
    if: {
      arg: 'previewInteractivity',
      eq: 'partially-interactive',
    },
    control: 'radio',
    options: ['unset (undefined)', 'interactive', 'non-interactive'],
    mapping: {
      'unset (undefined)': undefined,
      interactive: ADDITIONAL_INFO.interactive,
      'non-interactive': ADDITIONAL_INFO.nonInteractive,
    },
    table: previewArgGroup,
  },

  previewPrompt: {
    name: 'Preview with `prompt`',
    control: 'radio',
    options: ['unset (undefined)', 'interactive', 'non-interactive'],
    mapping: {
      'unset (undefined)': undefined,
      interactive: PROMPTS.interactive,
      'non-interactive': PROMPTS.nonInteractive,
    },
    table: previewArgGroup,
  },

  previewMedia: {
    name: 'Preview with `media`',
    control: 'select',
    options: [
      'unset (undefined)',
      'image',
      'avatar: single',
      'avatar: double',
      'avatar: double-diagonal',
    ],
    mapping: {
      'unset (undefined)': undefined,
      image: MEDIA.image,
      'avatar: single': MEDIA.avatarSingle,
      'avatar: double': MEDIA.avatarDouble,
      'avatar: double-diagonal': MEDIA.avatarDiagonal,
    },
    table: previewArgGroup,
  },
} as const;

const getPropsForPreview = (args: PreviewStoryArgs): [ListItemProps, Partial<ListItemProps>] => {
  const {
    previewInteractivity,
    previewControlFull,
    previewControlPartial,
    previewAdditionalInfoFull,
    previewAdditionalInfoPartial,
    previewPrompt,
    previewMedia,
    ...props
  } = args;

  return [
    props,
    {
      control: previewControlFull || previewControlPartial,
      additionalInfo: previewAdditionalInfoFull || previewAdditionalInfoPartial,
      prompt: previewPrompt,
      media: previewMedia,
    },
  ];
};

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

    return (
      <List>
        <ListItem {...props} {...previewProps} />
      </List>
    );
  },
  argTypes: previewArgTypes,
  args: {
    previewInteractivity: 'fully-interactive',
    previewPrompt: 'interactive',
    previewMedia: 'avatar: single',
    previewControlFull: 'Button',
    previewControlPartial: 'Button',
    previewAdditionalInfoFull: 'non-interactive',
    previewAdditionalInfoPartial: 'non-interactive',
  },
  decorators: [withoutKey],
};

export const Responsiveness: StoryObj<PreviewStoryArgs> = {
  parameters: {
    docs: {
      canvas: {
        sourceState: 'hidden',
      },
      description: {
        story: `ListItem uses container queries under the hood, which means its layout self-adjusts based on the available space. At the 100% zoom level, these breakpoints correspond to containers under \`${LISTITEM_CQ.MIN}px\`, those between \`${LISTITEM_CQ.MIN + 1}px and ${LISTITEM_CQ.MAX}px\`, and finally everything including and above \`${LISTITEM_CQ.MAX + 1}px\`.\n\nThis also supports high levels of zoom for assistive tech users.`,
      },
    },
  },
  argTypes: {
    ...hideControls(),
    ...previewArgTypes,
  },
  args: {
    subtitle: lorem10,
    previewInteractivity: 'fully-interactive',
    previewPrompt: 'interactive',
    previewMedia: 'image',
    previewControlFull: 'Button',
    previewControlPartial: 'Button',
    previewAdditionalInfoFull: 'non-interactive',
    previewAdditionalInfoPartial: 'non-interactive',
  },
  decorators: [withoutKey],
  render: (args) => {
    const [props, previewProps] = getPropsForPreview(args);

    return (
      <List
        as="ol"
        className="list-unstyled"
        style={{
          display: 'grid',
          gridTemplateColumns: [
            LISTITEM_CQ.MIN,
            Math.round((LISTITEM_CQ.MAX - LISTITEM_CQ.MIN) / 2 + LISTITEM_CQ.MIN),
            510,
          ]
            .map((width) => `${width}px`)
            .join(' '),
          gap: 16,
        }}
      >
        <ListItem {...props} {...previewProps} />
        <ListItem {...props} {...previewProps} />
        <ListItem {...props} {...previewProps} />
      </List>
    );
  },
};

/**
 * Due to platform limitations, the component is unable to automagically adjust its content to
 * all possible configurations. <br />
 * By default, it'll follow a CSS grid `1fr max-content` ratio of left side content (`title` and
 * `subtitle`) to the right side content (`valueTitle` and `valueSubtitle`).
 *
 * To adjust the width of the right side content, you can use `valueColumnWidth` prop which accepts
 * a number between `0–100` which resolves to a `fr` value of a `grid-template-columns` declaration.
 * E.g. `valueColumnWidth={25}` will result in a `75fr 25fr`.
 *
 * **NB:** This behaviour is slightly different on mobile platforms as they have more control over
 * layout calculations.
 */
export const ContentRatio: StoryObj<PreviewStoryArgs> = {
  parameters: {
    docs: {
      canvas: {
        sourceState: 'hidden',
      },
    },
  },
  args: {
    valueColumnWidth: 70,
    subtitle: lorem5,
    valueTitle: `${lorem5} ${lorem5}`,
    valueSubtitle: lorem5,
    previewInteractivity: 'fully-interactive',
    previewPrompt: 'interactive',
    previewMedia: 'image',
    previewControlFull: 'Button',
    previewControlPartial: 'Button',
    previewAdditionalInfoFull: 'non-interactive',
    previewAdditionalInfoPartial: 'non-interactive',
    as: 'div',
  },
  argTypes: {
    ...hideControls(['disabled', 'spotlight']),
    ...previewArgTypes,
  },
  render: (args) => {
    const [props, previewProps] = getPropsForPreview(args);

    return <ListItem {...props} {...previewProps} />;
  },
};

/**
 * List item supports default and inverted title and description to allow for further flexibility
 * in the component. No other content elements are affected by this prop.
 *
 * Refer to the [design documentation](https://wise.design/components/list-item#content-hierarchy) for more details.
 */
export const ContentHierarchy: StoryObj<PreviewStoryArgs> = {
  args: {
    inverted: true,
    previewInteractivity: 'fully-interactive',
    previewPrompt: 'interactive',
    previewMedia: 'image',
    previewControlFull: 'Button',
    previewControlPartial: 'Button',
    previewAdditionalInfoFull: 'non-interactive',
    previewAdditionalInfoPartial: 'non-interactive',
    as: 'div',
  },
  argTypes: {
    ...hideControls(['valueColumnWidth', 'disabled']),
    ...previewArgTypes,
  },
  render: (args) => {
    const [props, previewProps] = getPropsForPreview(args);

    return <ListItem {...props} {...previewProps} />;
  },
};

/**
 * Interactive list items can alternatively be displayed with a spotlight.
 *
 * Refer to the [design documentation](https://wise.design/components/list-item#spotlight) for more details.
 */
export const Spotlight: StoryObj<PreviewStoryArgs> = {
  argTypes: {
    ...hideControls(['spotlight', 'valueColumnWidth']),
    ...previewArgTypes,
  },
  args: {
    subtitle: lorem10,
    previewInteractivity: 'fully-interactive',
    previewPrompt: 'interactive',
    previewMedia: 'image',
    previewControlFull: 'Button',
    previewControlPartial: 'Button',
    previewAdditionalInfoFull: 'non-interactive',
    previewAdditionalInfoPartial: 'non-interactive',
  },
  decorators: [withoutKey],
  render: (args) => {
    const [props, previewProps] = getPropsForPreview(args);

    return (
      <List>
        <ListItem {...props} {...previewProps} title="Inactive Spotlight" spotlight="inactive" />
        <ListItem {...props} {...previewProps} title="Active Spotlight" spotlight="active" />
        <ListItem {...props} {...previewProps} title="No Spotlight" />
      </List>
    );
  },
};

/**
 * By default, all list items are fully interactive, meaning that their whole surface triggers the
 * main action. The only 2 exceptions are those using `ListItem.Button` and `ListItem.IconButton`
 * as their controls, which can be made partially interactive by toggling `partiallyInteractive`
 * prop directly on them.
 *
 * Fully interactive list items **cannot contain any other interactive elements**, such as links or
 * buttons, inside them, with the only exception being the `ListItem.Prompt` subcomponent, as it's
 * rendered on a separate Accessibility Tree branch. <br/>
 * Partially interactive items can also have interactive `ListItem.AdditionalInfo` which allows
 * for a single, appended link or inline button.
 *
 * All of these limitations were put in place to ensure that the list item is compliant with
 * accessibility guidance and offers consistent experience across all engineering platforms.
 *
 * Please refer to the [design documentation](https://wise.design/components/list-item#interaction) for more details.
 */
export const Interactivity: StoryObj<PreviewStoryArgs> = storySourceWithoutNoise({
  argTypes: {
    ...hideControls([
      'title',
      'subtitle',
      'valueTitle',
      'valueSubtitle',
      'valueColumnWidth',
      'inverted',
    ]),
  },
  args: {
    subtitle: lorem10,
  },
  decorators: [withoutKey],
  render: function Render(args) {
    const [clicked, setClicked] = useState(0);

    const renderPrompt = () => (
      <ListItem.Prompt sentiment="positive">
        Finally, there is a prompt{' '}
        <Link href="https://wise.com" target="_blank" rel="noreferrer">
          referencing some details
        </Link>{' '}
        for your convenience.
      </ListItem.Prompt>
    );

    const renderInteractiveInfo = () => (
      <ListItem.AdditionalInfo
        action={{
          label: 'Details about the subject.',
          href: 'https://wise.com',
          target: '_blank',
        }}
      >
        Which is augmented with additional info.
      </ListItem.AdditionalInfo>
    );

    const renderStaticInfo = () => (
      <ListItem.AdditionalInfo>Which is augmented with additional info.</ListItem.AdditionalInfo>
    );

    return (
      <>
        <output>Count: {clicked}</output>
        <List>
          <ListItem
            {...args}
            title="Fully interactive"
            control={
              <ListItem.Button
                priority="secondary-neutral"
                onClick={() => setClicked((current) => current + 1)}
              >
                as Button
              </ListItem.Button>
            }
            additionalInfo={renderStaticInfo()}
            prompt={renderPrompt()}
          />
          <ListItem
            {...args}
            title="Partially interactive"
            control={
              <ListItem.Button
                priority="secondary-neutral"
                partiallyInteractive
                onClick={() => setClicked((current) => current + 1)}
              >
                as Button
              </ListItem.Button>
            }
            additionalInfo={renderInteractiveInfo()}
            prompt={renderPrompt()}
          />
          <ListItem
            {...args}
            title="Fully interactive"
            control={
              <ListItem.IconButton
                aria-label="Add something to me"
                onClick={() => setClicked((current) => current + 1)}
              >
                <Plus />
              </ListItem.IconButton>
            }
            additionalInfo={renderStaticInfo()}
            prompt={renderPrompt()}
          />
          <ListItem
            {...args}
            title="Partially interactive"
            control={
              <ListItem.IconButton
                aria-label="Add something to me"
                partiallyInteractive
                onClick={() => setClicked((current) => current + 1)}
              >
                <Plus />
              </ListItem.IconButton>
            }
            additionalInfo={renderInteractiveInfo()}
            prompt={renderPrompt()}
          />
        </List>
      </>
    );
  },
});

/**
 * For backwards compatibility, all of the ListItem's content elements accept `ReactNode`.
 * That said, most of them should be fed with plain text only. The exception is `valueTitle`
 * and `valueSubtitle` which can be augmented with sentiment colours, strikethrough or bold
 * styles. <br />
 *
 * Please refer to the [design documentation](https://wise.design/components/list-item#content) for more details.
 */
export const StylingLabels: Story = {
  argTypes: {
    ...hideControls(['spotlight', 'valueColumnWidth']),
  },
  args: {
    title: lorem5,
    subtitle: lorem10,
    media: MEDIA.image,
    control: CONTROLS.iconButton,
    prompt: PROMPTS.interactive,
  },
  decorators: [withoutKey],
  render: function Render(args) {
    return (
      <List>
        <ListItem
          {...args}
          valueTitle={<Emphasis text="<negative>100 GBP</negative>" />}
          valueSubtitle={<s>125 GBP</s>}
        />
        <ListItem
          {...args}
          valueTitle={<Emphasis text="<positive>100 GBP</positive>" />}
          valueSubtitle={<strong>125 GBP</strong>}
        />
      </List>
    );
  },
};
