import { Meta, StoryObj } from '@storybook/react-webpack5';
import { fn } from 'storybook/test';
import { Freeze, ArrowRight, ChevronRight } from '@transferwise/icons';
import { Flag } from '@wise/art';
import { lorem10, lorem20 } from '../../test-utils';
import SentimentSurface from '../../sentimentSurface';
import type { ButtonProps } from '../Button.types';
import Button from '../Button.resolver';

const withContainer = (Story: any) => (
  <div style={{ display: 'flex', justifyContent: 'center' }}>
    <Story />
  </div>
);

/**
 * Used for showing multiple components within a Canvas.
 * @decorator
 */
const withComponentGrid =
  (maxWidth = 'auto') =>
  (Story: any) => (
    <div
      style={{
        width: '100%',
        placeItems: 'center',
        justifyContent: 'center',
        display: 'flex',
        flexWrap: 'wrap',
        gap: '2rem',
        maxWidth,
      }}
    >
      <Story />
    </div>
  );

/**
 * Not all stories need access to all controls as it causes unnecessary UI noise.
 */
const hideControls = (args: string[]) => {
  const hidden = [
    'addonStart',
    'addonEnd',
    'onClick',
    'onBlur',
    'onFocus',
    'onKeyDown',
    'onMouseEnter',
    'onMouseLeave',
    ...args,
  ];

  return Object.fromEntries(hidden.map((item) => [item, { table: { disable: true } }]));
};

/**
 * Convenience controls for previewing rich markup,
 * not otherwise possible via Storybook
 */
type PreviewStoryArgs = Parameters<typeof Button>[0] & {
  previewAddonStart: boolean | ButtonProps['addonStart'];
  previewAddonEnd: boolean | ButtonProps['addonEnd'];
};
const previewArgTypes = {
  previewAddonStart: {
    control: 'select',
    options: [
      'undefined',
      'icon',
      'avatar: flag',
      'avatar: initials',
      'avatar: icon',
      'avatar: image',
      'avatar: double',
    ],
    name: 'Preview with `addonStart`',
    mapping: {
      undefined,
      icon: {
        type: 'icon',
        value: <Freeze />,
      },
      'avatar: flag': {
        type: 'avatar',
        value: [{ asset: <Flag code="pl" /> }],
      },
      'avatar: initials': {
        type: 'avatar',
        value: [{ profileName: 'Jay Jay' }],
      },
      'avatar: icon': {
        type: 'avatar',
        value: [{ asset: <Freeze /> }],
      },
      'avatar: image': {
        type: 'avatar',
        value: [{ imgSrc: '../avatar-square-dude.webp' }],
      },
      'avatar: double': {
        type: 'avatar',
        value: [{ asset: <Flag code="gb" /> }, { imgSrc: '../avatar-square-dude.webp' }],
      },
    },
    table: {
      category: 'Preview options',
      type: {
        summary: undefined,
      },
    },
    description:
      '**NB:** The `lg` button does not support any addons. `md` button accepts either icons or avatars, while `sm` is restricted to icons only.',
  },
  previewAddonEnd: {
    control: 'boolean',
    name: 'Preview with `addonEnd`',
    mapping: {
      true: { type: 'icon', value: <ArrowRight /> },
    },
    description:
      '**NB:** The `lg` button does not support any addons. `md` and `sm` allow for the use of icons only.',
    table: {
      category: 'Preview options',
      type: {
        summary: undefined,
      },
    },
  },
} as const;

const getPropsForPreview = (args: PreviewStoryArgs) => {
  const { previewAddonStart, previewAddonEnd, ...props } = args as {
    previewAddonStart: ButtonProps['addonStart'];
    previewAddonEnd: ButtonProps['addonEnd'];
    props: typeof Button;
  };

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

/**
 * The stories below document the new Button API, which requires `v2` prop to function.  <br />For more details please refer to the [release notes](https://transferwise.atlassian.net/wiki/spaces/DS/pages/3542158737/Button+Updates+New+sizes+Loader+and+Cue) and the [design spec](https://wise.design/components/button). <br />
 * You can still find the old Button documentation under [Legacy Button](?path=/docs/actions-button-legacy--docs)
 *
 *
 */
const meta: Meta<typeof Button> = {
  component: Button,
  title: 'Actions/Button',
  argTypes: {
    v2: {
      table: {
        readonly: true,
      },
    },
    size: {
      type: {
        name: 'enum',
        value: ['lg', 'md', 'sm'],
      },
      table: {
        type: {
          summary: 'ButtonSize',
        },
      },
    },
    priority: {
      type: {
        name: 'enum',
        value: ['primary', 'secondary', 'secondary-neutral', 'tertiary'],
      },
      table: {
        type: {
          summary: 'ButtonPriority',
        },
        defaultValue: { summary: '"primary"' },
      },
    },
    sentiment: {
      type: {
        name: 'enum',
        value: ['default', 'negative'],
      },
      table: {
        type: {
          summary: 'ButtonSentiment',
        },
      },
    },
    disabled: {
      table: {
        defaultValue: { summary: 'false' },
      },
    },
    loading: {
      table: {
        defaultValue: { summary: 'false' },
      },
    },
    block: {
      table: {
        defaultValue: { summary: 'false' },
      },
    },
    href: {
      type: {
        name: 'string',
      },
    },
    target: {
      type: {
        name: 'string',
      },
      table: {
        type: {
          summary: 'string',
        },
      },
    },
    addonStart: {
      control: 'object',
    },
    addonEnd: {
      control: 'object',
    },
    type: {
      control: 'text',
      table: {
        type: {
          summary: 'string',
        },
      },
    },
    htmlType: {
      table: {
        disable: true,
      },
    },
    children: {
      table: {
        type: {
          summary: 'ReactNode',
        },
      },
    },
    id: {
      table: {
        category: 'Common',
      },
    },
    className: {
      table: {
        category: 'Common',
      },
    },
    onClick: {
      table: {
        category: 'Common',
      },
    },
  },
  args: {
    v2: true,
    size: undefined,
    priority: undefined,
    sentiment: undefined,
    disabled: false,
    loading: false,
    block: false,
    href: undefined,
    target: undefined,
    as: undefined,
    type: undefined,
    addonStart: undefined,
    addonEnd: undefined,
    className: undefined,
    children: 'Button text',
    onClick: fn(),
  },

  decorators: [withContainer],
};

export default meta;

type Story = StoryObj<typeof Button>;

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

    return <Button {...props} {...previewProps} v2 />;
  },
  args: {
    onBlur: fn(),
    onFocus: fn(),
    onKeyDown: fn(),
    onMouseEnter: fn(),
    onMouseLeave: fn(),
    previewAddonStart: false,
    previewAddonEnd: false,
  },
  argTypes: {
    onClick: { table: { disable: true } },
    onBlur: { table: { disable: true } },
    onFocus: { table: { disable: true } },
    onKeyDown: { table: { disable: true } },
    onMouseEnter: { table: { disable: true } },
    onMouseLeave: { table: { disable: true } },
    ...previewArgTypes,
  },
};

/**
 * 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> = {
  render: function Render(args: PreviewStoryArgs) {
    const [props, previewProps] = getPropsForPreview(args);

    return (
      <>
        <Button {...props} {...previewProps} v2>
          Default Sentiment
        </Button>
        <Button {...props} {...previewProps} v2 sentiment="negative">
          Negative Sentiment
        </Button>
        <Button {...props} {...previewProps} v2 priority="secondary">
          Default Sentiment
        </Button>
        <Button {...props} {...previewProps} v2 priority="secondary" sentiment="negative">
          Negative Sentiment
        </Button>
      </>
    );
  },
  argTypes: {
    ...hideControls([
      'sentiment',
      'priority',
      'block',
      'href',
      'target',
      'children',
      'type',
      'className',
    ]),
    ...previewArgTypes,
  },
  args: {
    previewAddonStart: false,
    previewAddonEnd: false,
  },
  decorators: [withComponentGrid('30rem')],
};

/**
 * 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> = {
  render: function Render(args: PreviewStoryArgs) {
    const [props, previewProps] = getPropsForPreview(args);

    return (
      <>
        <Button {...props} {...previewProps} v2>
          Primary Priority (default)
        </Button>
        <Button {...props} {...previewProps} v2 priority="secondary">
          Secondary Priority
        </Button>
        <Button {...props} {...previewProps} v2 priority="secondary-neutral">
          Secondary neutral Priority
        </Button>
        <Button {...props} {...previewProps} v2 priority="tertiary">
          Tertiary Priority
        </Button>
      </>
    );
  },
  argTypes: {
    ...hideControls(['priority', 'block', 'href', 'target', 'children', 'type', 'className']),
    ...previewArgTypes,
  },
  args: {
    previewAddonStart: false,
    previewAddonEnd: false,
  },
  decorators: [withComponentGrid()],
};

/**
 * There are three different button sizes – small (`sm`), medium (`md`) and large (`lg`) – each used for different purposes. <br />
 * [Design documentation](https://wise.design/components/button#sizes)
 */
export const Size: StoryObj<PreviewStoryArgs> = {
  render: function Render(args: PreviewStoryArgs) {
    const [props, previewProps] = getPropsForPreview(args);

    return (
      <>
        <Button {...props} {...previewProps}>
          Large button (default)
        </Button>
        <Button {...props} {...previewProps} size="md">
          Medium button
        </Button>
        <Button {...props} {...previewProps} size="sm">
          Small button
        </Button>
      </>
    );
  },
  argTypes: {
    ...hideControls(['size', 'block', 'href', 'target', 'children', 'type', 'className']),
    ...previewArgTypes,
  },
  args: {
    previewAddonStart: false,
    previewAddonEnd: false,
  },
  decorators: [withComponentGrid()],
};

/**
 * If `href` prop is set, the component will be automatically rendered as an HTML anchor element.
 */
export const AsAnchor: StoryObj<PreviewStoryArgs> = {
  render: function Render(args: PreviewStoryArgs) {
    const [props, previewProps] = getPropsForPreview(args);

    return (
      <Button {...props} {...previewProps}>
        Button as anchor
      </Button>
    );
  },
  argTypes: {
    ...hideControls(['block']),
    ...previewArgTypes,
  },
  args: {
    href: '#',
    previewAddonStart: false,
    previewAddonEnd: false,
    onClick: undefined,
  },
};

/**
 * Buttons can be in `disabled` or `loading` states to indicate they're not currently actionable.
 */
export const States: StoryObj<PreviewStoryArgs> = {
  render: function Render(args: PreviewStoryArgs) {
    const [props, previewProps] = getPropsForPreview(args);

    return (
      <>
        <Button {...props} {...previewProps} v2>
          Default
        </Button>
        <Button {...props} {...previewProps} v2 disabled>
          Disabled
        </Button>
        <Button {...props} {...previewProps} v2 loading>
          Loading
        </Button>
      </>
    );
  },
  argTypes: {
    ...hideControls(['disabled', 'loading', 'block']),
    ...previewArgTypes,
  },
  args: {
    previewAddonStart: false,
    previewAddonEnd: false,
  },
  decorators: [withComponentGrid()],
};

/**
 * A Button that takes up the full width of its container (`display: block`).
 */
export const DisplayBlock: StoryObj<PreviewStoryArgs> = {
  render: function Render(args: PreviewStoryArgs) {
    const [props, previewProps] = getPropsForPreview(args);

    return (
      <Button {...props} {...previewProps}>
        Full-width button
      </Button>
    );
  },
  argTypes: {
    ...hideControls([
      'href',
      'target',
      'priority',
      'sentiment',
      'disabled',
      'children',
      'type',
      'className',
    ]),
    ...previewArgTypes,
  },
  args: {
    block: true,
    previewAddonStart: false,
    previewAddonEnd: false,
  },
};

/**
 * Icons are only supported for `sm` and `md` size Buttons. <br />
 * [Design documentation](https://wise.design/components/button#accessories)
 */
export const WithIcons: StoryObj<PreviewStoryArgs> = {
  render: function Render(args: PreviewStoryArgs) {
    const [props] = getPropsForPreview(args);

    return (
      <>
        <Button {...props} v2 addonStart={{ type: 'icon', value: <Freeze /> }}>
          With start icon
        </Button>

        <Button {...props} v2 addonEnd={{ type: 'icon', value: <ArrowRight /> }}>
          With end icon
        </Button>

        <Button
          {...props}
          v2
          addonStart={{ type: 'icon', value: <Freeze /> }}
          addonEnd={{ type: 'icon', value: <ArrowRight /> }}
        >
          With both icons
        </Button>
      </>
    );
  },
  argTypes: {
    ...hideControls(['href', 'target', 'sentiment', 'disabled', 'children', 'type', 'className']),
  },
  args: {
    size: 'md',
  },
  parameters: {
    docs: {
      source: {
        code: `
<>
  <Button v2 size="md" addonStart={{ type: 'icon', value: <Freeze /> }}>
    With start icon
  </Button>
  
  <Button v2 size="md" addonEnd={{ type: 'icon', value: <ArrowRight /> }}>
    With end icon
  </Button>
  
  <Button v2 size="md" addonStart={{ type: 'icon', value: <Freeze /> }} addonEnd={{ type: 'icon', value: <ArrowRight /> }}>
    With both icons
  </Button>
</>
        `,
      },
    },
  },
  decorators: [withComponentGrid()],
};

/**
 * Avatars are only supported by the `md` size Buttons and are only allowed before the label. <br />
 * [Design documentation](https://wise.design/components/button#accessories)
 */
export const WithAvatars: StoryObj<PreviewStoryArgs> = {
  render: function Render(args: PreviewStoryArgs) {
    const [props] = getPropsForPreview(args);

    return (
      <>
        <Button {...props} v2 addonStart={{ type: 'avatar', value: [{ asset: <Freeze /> }] }}>
          With single avatar
        </Button>

        <Button
          {...props}
          v2
          addonStart={{
            type: 'avatar',
            value: [{ asset: <Flag code="br" /> }, { asset: <Flag code="jp" /> }],
          }}
        >
          With double avatar
        </Button>

        <Button {...props} v2 addonStart={{ type: 'avatar', value: [{ profileName: 'John Doe' }] }}>
          With initials
        </Button>

        <Button
          {...props}
          v2
          addonStart={{ type: 'avatar', value: [{ imgSrc: '../avatar-square-dude.webp' }] }}
        >
          With an image
        </Button>
      </>
    );
  },
  argTypes: hideControls([
    'href',
    'target',
    'sentiment',
    'disabled',
    'children',
    'type',
    'className',
  ]),
  args: {
    size: 'md',
  },
  parameters: {
    docs: {
      source: {
        code: `
<>
  <Button 
    v2 
    size="md" 
    addonStart={{ 
      type: 'avatar', 
      value: [{ asset: <Freeze /> }]
    }}
  >
    With single avatar
  </Button>

  <Button 
    v2 
    size="md" 
    addonStart={{ 
      type: 'avatar', 
      value: [{ asset: <Flag code="br" /> }, { asset: <Flag code="jp" /> }]
    }}
  >
    With double avatar
  </Button>

  <Button 
    v2 
    size="md" 
    addonStart={{ 
      type: 'avatar', 
      value: [{ profileName: 'John Doe' }]
    }}
  >
    With initials
  </Button>

  <Button 
    v2 
    size="md" 
    addonStart={{ 
      type: 'avatar', 
      value: [{ imgSrc: '../avatar-square-dude.webp' }]
    }}
  >
    With image Avatar
  </Button>
</>
        `,
      },
    },
  },
  decorators: [withComponentGrid()],
};

/**
 * **NB:** The button doesn't know how long its label is, how many words it consists of and
 * how zoomed in it is. That is likely to lead to scenarios in which the text will overflow
 * its container. This is being investigated.
 */
export const DealingWithLongText: StoryObj<PreviewStoryArgs> = {
  render: function Render(args: PreviewStoryArgs) {
    const [props, previewProps] = getPropsForPreview(args);

    return (
      <div>
        <div>
          <div>
            <Button {...props} {...previewProps}>
              {lorem10}
            </Button>
          </div>
          <div className="m-y-2">
            <Button {...props} {...previewProps}>
              Forderungsabtretungsvereinbarung
            </Button>
          </div>
          <div
            className="d-flex"
            style={{
              width: '100%',
              justifyContent: 'space-between',
              alignItems: 'center',
            }}
          >
            <span>{lorem20}</span>
            <div>
              <Button {...props} {...previewProps}>
                Register interest
              </Button>
            </div>
          </div>
        </div>
        <div style={{ width: 260 }} className="m-t-2">
          <div>
            <Button {...props} {...previewProps}>
              {lorem10}
            </Button>
          </div>
          <div className="m-y-2">
            <Button {...props} {...previewProps}>
              Forderungsabtretungsvereinbarung
            </Button>
          </div>
          <div
            className="d-flex"
            style={{
              width: '100%',
              justifyContent: 'space-between',
              alignItems: 'center',
            }}
          >
            <span>{lorem20}</span>
            <div>
              <Button {...props} {...previewProps}>
                Register interest
              </Button>
            </div>
          </div>
        </div>
      </div>
    );
  },
  argTypes: {
    ...hideControls([
      'href',
      'target',
      'priority',
      'sentiment',
      'disabled',
      'loading',
      'children',
      'as',
      'type',
      'className',
    ]),
    ...previewArgTypes,
  },
  args: {
    block: true,
    previewAddonStart: false,
    previewAddonEnd: false,
  },
};

/**
 * `Button` is sentiment-aware and will automatically adjust its colours if wrapped inside
 * the [SentimentSurface](?path=/docs/foundations-sentimentsurface--docs) component
 */
export const SentimentAwareness: Story = {
  render: (args) => {
    return (
      <>
        {(['success', 'warning', 'negative', 'neutral', 'proposition'] as const).map(
          (sentiment) => (
            <SentimentSurface
              key={sentiment}
              sentiment={sentiment}
              className="p-a-1 d-flex align-items-center"
              style={{ gap: 'var(--size-8)' }}
            >
              <Button
                v2
                size="md"
                addonEnd={{ type: 'icon', value: <ChevronRight /> }}
                addonStart={{ type: 'avatar', value: [{ asset: <Freeze /> }] }}
              >
                Primary
              </Button>
              <Button
                v2
                size="md"
                priority="secondary"
                addonEnd={{ type: 'icon', value: <ChevronRight /> }}
                addonStart={{ type: 'avatar', value: [{ asset: <Freeze /> }] }}
              >
                Secondary
              </Button>
              <Button
                v2
                size="md"
                priority="secondary-neutral"
                addonEnd={{ type: 'icon', value: <ChevronRight /> }}
                addonStart={{ type: 'avatar', value: [{ asset: <Freeze /> }] }}
              >
                Secondary Neutral
              </Button>
              <Button
                v2
                size="md"
                disabled
                addonEnd={{ type: 'icon', value: <ChevronRight /> }}
                addonStart={{ type: 'avatar', value: [{ asset: <Freeze /> }] }}
              >
                Disabled
              </Button>
            </SentimentSurface>
          ),
        )}
      </>
    );
  },
  parameters: {
    docs: {
      source: { type: 'dynamic' },
      canvas: {
        sourceState: 'hidden',
      },
    },
  },
  decorators: [
    (Story) => (
      <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
        <Story />
      </div>
    ),
  ],
};
