import { Freeze } from '@transferwise/icons';
import { mockMatchMedia, render, screen, userEvent } from '../test-utils';
import Button, { ButtonProps } from './Button.resolver';
import { CommonProps } from './Button.types';

mockMatchMedia();

describe('Button', () => {
  const legacyProps: ButtonProps = {
    as: 'button',
    priority: 'primary',
    size: 'md',
    type: 'accent',
  };

  const newProps: ButtonProps = {
    as: 'button',
    v2: true,
    priority: 'primary',
    size: 'md',
  };

  const testId = 'new-button';

  it('renders LegacyButton when v2 is false', () => {
    render(<Button {...legacyProps}>Legacy Button</Button>);
    const button = screen.getByText('Legacy Button');
    expect(button).toBeInTheDocument();
    expect(button).toHaveClass('btn btn-md np-btn np-btn-md btn-accent btn-priority-1');
  });

  it('renders the new Button when v2 is true', () => {
    render(
      <Button data-testid={testId} {...newProps}>
        New Button
      </Button>,
    );
    const button = screen.getByTestId('new-button');
    expect(button).toBeInTheDocument();
    expect(button).toHaveClass('wds-Button wds-Button--medium wds-Button--primary');
  });

  describe('render as HTML anchor', () => {
    const href = 'https://example.com';
    const name = 'Button as link';

    describe('v2', () => {
      describe('rendered HTMLS element', () => {
        it('should render as button by default', () => {
          render(<Button v2>{name}</Button>);
          expect(screen.getByRole('button', { name })).toBeInTheDocument();
        });

        it('should render as link if href is provided', () => {
          render(
            <Button v2 href={href}>
              {name}
            </Button>,
          );
          expect(screen.getByRole('link', { name })).toHaveAttribute('href', href);
        });

        it('should render a HTML anchor if `as="a"` is provided', () => {
          render(
            <Button v2 as="a">
              {name}
            </Button>,
          );
          expect(screen.getByText(name).closest('a')).toBeInTheDocument();
        });

        it('should render as a button if `href` is set together with `as="button"`', () => {
          render(
            <Button v2 href={href} as="button">
              {name}
            </Button>,
          );
          expect(screen.getByRole('button', { name })).toBeInTheDocument();
        });
      });

      describe('HTML attributes', () => {
        it('should not set `type` if rendered as HTML anchor', () => {
          render(
            <Button v2 type="submit" href="wise.com">
              {name}
            </Button>,
          );
          expect(screen.getByRole('link')).not.toHaveAttribute('type');
        });

        it('should not set `href` or `target` if rendered as HTML button', () => {
          render(
            <Button v2 as="button" href="wise.com" target="_blank">
              {name}
            </Button>,
          );
          const button = screen.getByRole('button');
          expect(button).not.toHaveAttribute('href');
          expect(button).not.toHaveAttribute('target');
        });
      });

      describe('disabled mode', () => {
        describe('button', () => {
          it('should not be disabled by default', () => {
            render(<Button v2>{name}</Button>);
            const button = screen.getByRole('button');
            expect(button).toBeEnabled();
            expect(button).not.toHaveAttribute('aria-disabled');
          });

          it('should respect disabled mode and set it only via the `disabled` attribute', () => {
            render(
              <Button v2 disabled>
                {name}
              </Button>,
            );
            const button = screen.getByRole('button');
            expect(button).toBeDisabled();
            expect(button).not.toHaveAttribute('aria-disabled');
          });
        });

        describe('anchor', () => {
          it('should not be disabled by default', () => {
            render(
              <Button v2 href="wise.com">
                {name}
              </Button>,
            );
            const button = screen.getByRole('link');
            expect(button).toBeEnabled();
            expect(button).not.toHaveAttribute('aria-disabled');
          });

          it('should respect disabled mode', () => {
            render(
              <Button v2 href="wise.com" disabled>
                {name}
              </Button>,
            );
            const button = screen.getByRole('link');
            expect(button).toHaveAttribute('aria-disabled');
            expect(button).toBeEnabled();
            expect(button).not.toHaveAttribute('href');
          });
        });
      });

      describe('loading mode', () => {
        describe('button', () => {
          it('should not be loading by default', () => {
            render(<Button v2>{name}</Button>);
            const button = screen.getByRole('button');
            expect(button).toBeEnabled();
            expect(button).not.toHaveAttribute('aria-busy');
          });

          it('should respect loading mode', () => {
            render(
              <Button v2 loading>
                {name}
              </Button>,
            );
            const button = screen.getByRole('button');
            expect(button).toHaveAttribute('aria-busy');
            // the `disabled` attribute is not set to keep the button
            // focusable but aria attribute is defined to make it
            // announced properly by the assistive tech
            expect(button).toBeEnabled();
            expect(button).toHaveAttribute('aria-disabled');
          });
        });

        describe('anchor', () => {
          it('should not be loading by default', () => {
            render(
              <Button v2 href="wise.com">
                {name}
              </Button>,
            );
            const button = screen.getByRole('link');
            expect(button).toBeEnabled();
            expect(button).not.toHaveAttribute('aria-busy');
            expect(button).not.toHaveAttribute('aria-disabled');
          });

          it('should respect loading mode', () => {
            render(
              <Button v2 loading href="wise.com">
                {name}
              </Button>,
            );
            const button = screen.getByRole('link');
            expect(button).toHaveAttribute('aria-busy');
            // the `disabled` attribute is not set to keep the button
            // focusable but aria attribute is defined to make it
            // announced properly by the assistive tech
            expect(button).toBeEnabled();
            expect(button).toHaveAttribute('aria-disabled');
            expect(button).not.toHaveAttribute('href');
          });
        });

        describe('content id', () => {
          it('should not set id for content by default', () => {
            const { container } = render(<Button v2>clickMe</Button>);

            expect(container.querySelector('[id$="_content"]')).not.toBeInTheDocument();
          });

          it('should set id for content by default if `id` is passed as a button prop', () => {
            const { container } = render(
              <Button v2 id="myId">
                clickMe
              </Button>,
            );

            expect(container.querySelector('#myId_content')).toBeInTheDocument();
          });
        });
      });
    });
  });

  it('does not set type when type is in LegacyButtonType', () => {
    // @ts-expect-error here we intentionally set wrong value to test `type` attr in DOM
    const legacyTypeProps: ButtonProps = { ...newProps, type: 'accent' };
    render(
      <Button data-testid={testId} {...legacyTypeProps}>
        Button Type
      </Button>,
    );
    const button = screen.getByTestId('new-button');
    expect(button).toBeInTheDocument();
    expect(button).not.toHaveAttribute('type');
  });

  it('sets type when type is not in LegacyButtonType', () => {
    const buttonTypeProps: ButtonProps = {
      ...newProps,
      type: 'submit',
    };
    render(
      <Button data-testid={testId} {...buttonTypeProps}>
        Submit Button
      </Button>,
    );
    const button = screen.getByTestId('new-button');
    expect(button).toBeInTheDocument();
    expect(button).toHaveAttribute('type', 'submit');
  });

  describe('addons', () => {
    const avatarProp: CommonProps['addonStart'] = {
      type: 'avatar',
      value: [{ asset: <Freeze /> }],
    };
    const iconProp: CommonProps['addonStart'] = { type: 'icon', value: <Freeze /> };

    const renderAllPriorities = (props: CommonProps) =>
      render(
        <>
          <Button {...props} priority="primary">
            primary
          </Button>
          <Button {...props} priority="secondary">
            secondary
          </Button>
          <Button {...props} priority="secondary-neutral">
            secondary-neutral
          </Button>
          <Button {...props} priority="tertiary">
            tertiary
          </Button>
        </>,
      );

    describe('addonsStart', () => {
      describe('large', () => {
        it('should not support avatars', () => {
          renderAllPriorities({
            v2: true,
            size: 'lg',
            addonStart: avatarProp,
          });
          expect(screen.queryAllByTestId('freeze-icon')).toHaveLength(0);
        });

        it('should not support icons', () => {
          renderAllPriorities({
            v2: true,
            size: 'lg',
            addonStart: iconProp,
          });
          expect(screen.queryAllByTestId('freeze-icon')).toHaveLength(0);
        });
      });

      describe('medium', () => {
        it('should support avatar', () => {
          renderAllPriorities({
            v2: true,
            size: 'md',
            addonStart: avatarProp,
          });
          expect(screen.getAllByTestId('freeze-icon')).toHaveLength(4);
        });

        it('should support icon', () => {
          renderAllPriorities({
            v2: true,
            size: 'md',
            addonStart: iconProp,
          });
          expect(screen.getAllByTestId('freeze-icon')).toHaveLength(4);
        });
      });

      describe('small', () => {
        it('should not support avatar', () => {
          renderAllPriorities({
            v2: true,
            size: 'sm',
            addonStart: avatarProp,
          });
          expect(screen.queryAllByTestId('freeze-icon')).toHaveLength(0);
        });

        it('should support icon', () => {
          renderAllPriorities({
            v2: true,
            size: 'sm',
            addonStart: iconProp,
          });
          expect(screen.getAllByTestId('freeze-icon')).toHaveLength(4);
        });
      });
    });

    describe('addonEnd', () => {
      describe('large', () => {
        it('should support icon', () => {
          renderAllPriorities({
            v2: true,
            size: 'lg',
            addonEnd: iconProp,
          });
          expect(screen.queryAllByTestId('freeze-icon')).toHaveLength(0);
        });
      });

      describe('medium', () => {
        it('should support icon', () => {
          renderAllPriorities({
            v2: true,
            size: 'sm',
            addonEnd: iconProp,
          });
          expect(screen.getAllByTestId('freeze-icon')).toHaveLength(4);
        });
      });

      describe('small', () => {
        it('should support icon', () => {
          renderAllPriorities({
            v2: true,
            size: 'md',
            addonEnd: iconProp,
          });
          expect(screen.getAllByTestId('freeze-icon')).toHaveLength(4);
        });
      });
    });
  });

  describe('onClick', () => {
    it('should call the handler when rendered as HTML button', async () => {
      const onClick = jest.fn();
      render(
        <Button v2 onClick={onClick}>
          Button
        </Button>,
      );
      await userEvent.click(screen.getByRole('button'));
      expect(onClick).toHaveBeenCalledTimes(1);
    });

    it('should call the handler when rendered as HTML anchor', async () => {
      const onClick = jest.fn();
      render(
        <Button v2 href="https://wise.com" onClick={onClick}>
          Anchor
        </Button>,
      );
      await userEvent.click(screen.getByRole('link'));
      expect(onClick).toHaveBeenCalledTimes(1);
    });
  });
});
