import React from 'react';
import { mount } from 'enzyme';
import { ApplicationLauncher, DropdownItem, Dropdown, DropdownToggle } from '@patternfly/react-core';
import {
  capitalize,
  getUniqueId,
  debounce,
  isElementInView,
  sideElementIsOutOfView,
  fillTemplate,
  pluralize,
  formatBreakpointMods
} from '../util';
import { KEY_CODES, SIDE } from '../constants';
import styles from '@patternfly/react-styles/css/layouts/Flex/flex';

const createMockHtmlElement = bounds => ({
  getBoundingClientRect: () => bounds
});

test('capitalize', () => {
  expect(capitalize('foo')).toBe('Foo');
});

test('getUniqueId', () => {
  expect(getUniqueId()).not.toBe(getUniqueId());
});

test('getUniqueId prefixed', () => {
  expect(getUniqueId().substring(0, 3)).toBe('pf-');
  expect(getUniqueId('pf-switch').substring(0, 10)).toBe('pf-switch-');
});

test('debounce', () => {
  jest.useFakeTimers();
  const callback = jest.fn();
  const debouncedFunction = debounce(callback, 50);

  debouncedFunction();
  // At this point in time, the callback should not have been called yet
  expect(callback).toHaveBeenCalledTimes(0);

  for (let i = 0; i < 10; i++) {
    jest.advanceTimersByTime(50);
    debouncedFunction();
  }

  expect(callback).toBeCalledTimes(10);
});

test('isElementInView should be true when partial out of view and with partial true', () => {
  const container = createMockHtmlElement({ left: 0, right: 200 });
  const element = createMockHtmlElement({ left: 10, right: 210 });
  expect(isElementInView(container, element, true)).toBe(true);
});

test('isElementInView should be false when partial out of view and with partial false ', () => {
  const container = createMockHtmlElement({ left: 0, right: 200 });
  const element = createMockHtmlElement({ left: 10, right: 210 });
  expect(isElementInView(container, element, false)).toBe(false);
});

test('isElementInView should be false completely out of view ', () => {
  const container = createMockHtmlElement({ left: 0, right: 200 });
  const element = createMockHtmlElement({ left: 200, right: 300 });
  expect(isElementInView(container, element, true)).toBe(false);
});

test('isElementInView should be false completely out of view when partial false ', () => {
  const container = createMockHtmlElement({ left: 0, right: 200 });
  const element = createMockHtmlElement({ left: 200, right: 300 });
  expect(isElementInView(container, element, false)).toBe(false);
});

test('sideElementIsOutOfView Returns left when off on left side', () => {
  const container = createMockHtmlElement({ left: 20, right: 220 });
  const element = createMockHtmlElement({ left: 10, right: 210 });
  expect(sideElementIsOutOfView(container, element)).toBe(SIDE.LEFT);
});

test('sideElementIsOutOfView Returns right when off on right side', () => {
  const container = createMockHtmlElement({ left: 0, right: 200 });
  const element = createMockHtmlElement({ left: 210, right: 410 });
  expect(sideElementIsOutOfView(container, element)).toBe(SIDE.RIGHT);
});

test('sideElementIsOutOfView Returns NONE when in view', () => {
  const container = createMockHtmlElement({ left: 0, right: 200 });
  const element = createMockHtmlElement({ left: 10, right: 110 });
  expect(sideElementIsOutOfView(container, element)).toBe(SIDE.NONE);
});

describe('keyHandler works on ApplicationLauncher', () => {
  document.body.innerHTML = '<!doctype html><html><body></body></html>';
  const dropdownItems = [
    <DropdownItem key=" 1" id="first" componentID="first-button" component="button">
      Action 1
    </DropdownItem>,
    <DropdownItem key="2" id="second" componentID="second-button" component="button">
      Action 2
    </DropdownItem>,
    <DropdownItem key="3" id="third" componentID="third-button" component="button" isDisabled>
      Disabled Link
    </DropdownItem>
  ];
  const view = mount(<ApplicationLauncher items={dropdownItems} isOpen />, {
    attachTo: document.getElementsByName('div')[0]
  });

  const firstDropdownItem = view.find('#first button').first();
  const secondDropdownItem = view.find('#second button').first();

  test('keyHandler advances forward', () => {
    firstDropdownItem.simulate('keydown', {
      key: 'ArrowDown',
      keyCode: KEY_CODES.ARROW_DOWN,
      which: KEY_CODES.ARROW_DOWN
    });
    const focusedElement = document.activeElement;
    expect(focusedElement.getAttribute('id')).toEqual('second-button');
  });

  test('keyHandler regresses backward', () => {
    secondDropdownItem.simulate('keydown', {
      key: 'ArrowUp',
      keyCode: KEY_CODES.ARROW_UP,
      which: KEY_CODES.ARROW_UP
    });
    const focusedElement = document.activeElement;
    expect(focusedElement.getAttribute('id')).toEqual('first-button');
  });

  test('keyHandler loops top to bottom', () => {
    firstDropdownItem.simulate('keydown', {
      key: 'ArrowUp',
      keyCode: KEY_CODES.ARROW_UP,
      which: KEY_CODES.ARROW_UP
    });
    const focusedElement = document.activeElement;
    expect(focusedElement.getAttribute('id')).toEqual('third-button');
  });
});

describe('keyHandler works on Dropdown', () => {
  document.body.innerHTML = '<!doctype html><html><body></body></html>';
  const dropdownItems = [
    <DropdownItem key="1" id="first" componentID="first-button" component="button">
      Action 1
    </DropdownItem>,
    <DropdownItem key="2" id="second" componentID="second-button" component="button">
      Action 2
    </DropdownItem>,
    <DropdownItem key="3" id="third" componentID="third-button" component="button" isDisabled>
      Disabled Link
    </DropdownItem>
  ];
  const view = mount(
    <Dropdown dropdownItems={dropdownItems} isOpen toggle={<DropdownToggle>Expanded Dropdown</DropdownToggle>} />,
    {
      attachTo: document.getElementsByName('div')[0]
    }
  );
  const firstDropdownItem = view.find('#first button').first();
  const secondDropdownItem = view.find('#second button').first();
  const thirdDropdownItem = view.find('#third button').first();

  test('keyHandler advances forward', () => {
    firstDropdownItem.simulate('keydown', {
      key: 'ArrowDown',
      keyCode: KEY_CODES.ARROW_DOWN,
      which: KEY_CODES.ARROW_DOWN
    });
    const focusedElement = document.activeElement;
    expect(focusedElement.getAttribute('id')).toEqual('second-button');
  });

  test('keyHandler regresses backward', () => {
    secondDropdownItem.simulate('keydown', {
      key: 'ArrowUp',
      keyCode: KEY_CODES.ARROW_UP,
      which: KEY_CODES.ARROW_UP
    });
    const focusedElement = document.activeElement;
    expect(focusedElement.getAttribute('id')).toEqual('first-button');
  });

  test('keyHandler loops top to bottom', () => {
    firstDropdownItem.simulate('keydown', {
      key: 'ArrowUp',
      keyCode: KEY_CODES.ARROW_UP,
      which: KEY_CODES.ARROW_UP
    });
    const focusedElement = document.activeElement;
    expect(focusedElement.getAttribute('id')).toEqual('third-button');
  });

  test('keyHandler loops down to top', () => {
    thirdDropdownItem.simulate('keydown', {
      key: 'ArrowDown',
      keyCode: KEY_CODES.ARROW_DOWN,
      which: KEY_CODES.ARROW_DOWN
    });
    const focusedElement = document.activeElement;
    expect(focusedElement.getAttribute('id')).toEqual('first-button');
  });
});

test('fillTemplate interpolates strings correctly', () => {
  // eslint-disable-next-line no-template-curly-in-string
  const templateString = 'My name is ${firstName} ${lastName}';
  const expected = 'My name is Jon Dough';
  const templatVars = {
    firstName: 'Jon',
    lastName: 'Dough'
  };
  const actual = fillTemplate(templateString, templatVars);
  expect(actual).toEqual(expected);
});

test('text pluralize', () => {
  expect(pluralize(1, 'dog')).toEqual('1 dog');
  expect(pluralize(2, 'dog')).toEqual('2 dogs');
  expect(pluralize(2, 'finch', 'finches')).toEqual('2 finches');
});

test('formatBreakpointMods', () => {
  expect(formatBreakpointMods({ default: 'spacerNone' }, styles)).toEqual('pf-m-spacer-none');
  expect(formatBreakpointMods({ md: 'spacerNone' }, styles)).toEqual(
    'pf-m-spacer-none-on-md'
  );
  expect(formatBreakpointMods({ default: 'column', lg: 'row' }, styles)).toEqual(
    'pf-m-column pf-m-row-on-lg'
  );
});
