import {
  kServiceOfflineWeather, kServiceTime, kServiceUptime, kServiceWeather,
  kWidgetUptime, kWidgetWeather,
} from '@bthlabs/homehub-core';
import {Icon, IconBasicGear} from '@bthlabs/homehub-icons';
import Popup from '@bthlabs/react-custom-popup';
import {shallow} from 'enzyme';
import React from 'react';

import {AboutModal} from 'src/main/components/AboutModal';
import * as StartMenu from 'src/main/components/StartMenu';
import {SERVICES} from 'src/services';
import {WIDGETS} from 'src/widgets';

import {SettingsFactory} from 'tests/__fixtures__/settings';

describe('src/main/components/StartMenu', () => {
  describe('ControlledStartMenu', () => {
    let mockOnAddDashboard = null;
    let mockOnAddService = null;
    let fakeSettings = null;

    beforeEach(() => {
      mockOnAddService = jasmine.createSpy();
      mockOnAddDashboard = jasmine.createSpy();
      fakeSettings = SettingsFactory();
    });

    it('renders as read-only', () => {
      // Given
      fakeSettings.READ_ONLY = true;

      // When
      const component = shallow(
        <StartMenu.ControlledStartMenu
          hasDashboards={true}
          offlineMode={false}
          services={SERVICES}
          settings={fakeSettings}
          widgets={WIDGETS}
          onAddDashboard={mockOnAddDashboard}
          onAddService={mockOnAddService}
        />
      );

      // Then
      expect(component.find('div[_hint="StartMenu"]').hasClass('read-only')).toBe(true);
      expect(component.find('div[_hint="AddDashboard"]').exists()).toBe(false);
      expect(component.find('div[_hint="Widgets"]').exists()).toBe(false);
    });

    it('renders the start button', () => {
      // Given
      const component = shallow(
        <StartMenu.ControlledStartMenu
          hasDashboards={true}
          offlineMode={false}
          services={SERVICES}
          settings={fakeSettings}
          widgets={WIDGETS}
          onAddDashboard={mockOnAddDashboard}
          onAddService={mockOnAddService}
        />
      );

      // When
      const startButton = component.find('a[_hint="StartButton"]').at(0);

      // Then
      expect(startButton.exists()).toBe(true);
    });

    it('displays the start menu popup when the start button is clicked', () => {
      // Given
      const component = shallow(
        <StartMenu.ControlledStartMenu
          hasDashboards={true}
          offlineMode={false}
          services={SERVICES}
          settings={fakeSettings}
          widgets={WIDGETS}
          onAddDashboard={mockOnAddDashboard}
          onAddService={mockOnAddService}
        />
      );

      // When
      component.find('a[_hint="StartButton"]').at(0).simulate('click');

      // Then
      const popup = component.find(Popup).at(0);
      expect(popup.prop('visible')).toBe(true);
    });

    it('configures and renders the start menu popup', () => {
      // Given
      const component = shallow(
        <StartMenu.ControlledStartMenu
          hasDashboards={true}
          offlineMode={false}
          services={SERVICES}
          settings={fakeSettings}
          widgets={WIDGETS}
          onAddDashboard={mockOnAddDashboard}
          onAddService={mockOnAddService}
        />
      );

      // When
      const popup = component.find(Popup).at(0);

      // Then
      expect(popup.exists()).toBe(true);
      expect(popup.prop('visible')).toBe(false);
    });

    it('hides the start menu popup when the overlay is clicked', () => {
      // Given
      const component = shallow(
        <StartMenu.ControlledStartMenu
          hasDashboards={true}
          offlineMode={false}
          services={SERVICES}
          settings={fakeSettings}
          widgets={WIDGETS}
          onAddDashboard={mockOnAddDashboard}
          onAddService={mockOnAddService}
        />
      );

      // When
      component.find('a[_hint="StartButton"]').at(0).simulate('click');
      component.find(Popup).at(0).invoke('onOverlayClick')();

      // Then
      const popup = component.find(Popup).at(0);
      expect(popup.prop('visible')).toBe(false);
    });

    it('renders the start menu view', () => {
      // Given
      const component = shallow(
        <StartMenu.ControlledStartMenu
          hasDashboards={true}
          offlineMode={false}
          services={SERVICES}
          settings={fakeSettings}
          widgets={WIDGETS}
          onAddDashboard={mockOnAddDashboard}
          onAddService={mockOnAddService}
        />
      );

      // When
      const startMenu = component.find('div[_hint="StartMenu"]').at(0);

      // Then
      expect(startMenu.exists()).toBe(true);
    });

    it('hides the start menu popup when the start menu is clicked', () => {
      // Given
      const component = shallow(
        <StartMenu.ControlledStartMenu
          hasDashboards={true}
          offlineMode={false}
          services={SERVICES}
          settings={fakeSettings}
          widgets={WIDGETS}
          onAddDashboard={mockOnAddDashboard}
          onAddService={mockOnAddService}
        />
      );

      // When
      component.find('a[_hint="StartButton"]').at(0).simulate('click');
      component.find('div[_hint="StartMenu"]').at(0).simulate('click');

      // Then
      const popup = component.find(Popup).at(0);
      expect(popup.prop('visible')).toBe(false);
    });

    it('renders the about toolbar item', () => {
      // Given
      const component = shallow(
        <StartMenu.ControlledStartMenu
          hasDashboards={true}
          offlineMode={false}
          services={SERVICES}
          settings={fakeSettings}
          widgets={WIDGETS}
          onAddDashboard={mockOnAddDashboard}
          onAddService={mockOnAddService}
        />
      );

      // When
      const aboutItem = component.find('div[_hint="About"]').at(0);

      // Then
      expect(aboutItem.exists()).toBe(true);
    });

    it('displays the about modal when the about toolbar item is clicked', () => {
      // Given
      const component = shallow(
        <StartMenu.ControlledStartMenu
          hasDashboards={true}
          offlineMode={false}
          services={SERVICES}
          settings={fakeSettings}
          widgets={WIDGETS}
          onAddDashboard={mockOnAddDashboard}
          onAddService={mockOnAddService}
        />
      );

      // When
      component.find('div[_hint="About"]').at(0).simulate('click');

      // Then
      const aboutModal = component.find(AboutModal).at(0);
      expect(aboutModal.prop('show')).toBe(true);
    });

    it('configures and renders the add dashboard toolbar item', () => {
      // Given
      const component = shallow(
        <StartMenu.ControlledStartMenu
          hasDashboards={true}
          offlineMode={false}
          services={SERVICES}
          settings={fakeSettings}
          widgets={WIDGETS}
          onAddDashboard={mockOnAddDashboard}
          onAddService={mockOnAddService}
        />
      );

      // When
      const addDashboardItem = component.find('div[_hint="AddDashboard"]').at(0);

      // Then
      expect(addDashboardItem.exists()).toBe(true);
      expect(addDashboardItem.prop('onClick')).toEqual(mockOnAddDashboard);
    });

    it('renders the reload toolbar item', () => {
      // Given
      const component = shallow(
        <StartMenu.ControlledStartMenu
          hasDashboards={true}
          offlineMode={false}
          services={SERVICES}
          settings={fakeSettings}
          widgets={WIDGETS}
          onAddDashboard={mockOnAddDashboard}
          onAddService={mockOnAddService}
        />
      );

      // When
      const addDashboardItem = component.find('div[_hint="Reload"]').at(0);

      // Then
      expect(addDashboardItem.exists()).toBe(true);
    });

    xit('reloads the page when the reload button is clicked', () => {
      // Given
      spyOn(window.location, 'reload');
      const component = shallow(
        <StartMenu.ControlledStartMenu
          hasDashboards={true}
          offlineMode={false}
          services={SERVICES}
          settings={fakeSettings}
          widgets={WIDGETS}
          onAddDashboard={mockOnAddDashboard}
          onAddService={mockOnAddService}
        />
      );

      // When
      component.find('div[_hint="Reload"]').at(0).simulate('click');

      // Then
      expect(window.location.reload).toHaveBeenCalled();
    });

    describe('Widgets', () => {
      let fakeServices = null;
      let fakeWidgets = null;

      beforeEach(() => {
        fakeServices = {
          [kServiceOfflineWeather]: SERVICES.kServiceOfflineWeather,
          [kServiceTime]: SERVICES.kServiceTime,
          [kServiceUptime]: SERVICES.kServiceUptime,
          [kServiceWeather]: SERVICES.kServiceWeather,
        };
        fakeWidgets = {
          [kWidgetUptime]: WIDGETS.kWidgetUptime,
          [kWidgetWeather]: WIDGETS.kWidgetWeather,
        };
      });

      it('renders the list of widgets available in online mode', () => {
        // Given
        const component = shallow(
          <StartMenu.ControlledStartMenu
            hasDashboards={true}
            offlineMode={false}
            services={fakeServices}
            settings={fakeSettings}
            widgets={fakeWidgets}
            onAddDashboard={mockOnAddDashboard}
            onAddService={mockOnAddService}
          />
        );

        // When
        const widgets = component.find('div[_hint="Widgets"]').at(0);

        // Then
        const serviceKeys = widgets.find('div[_hint="Widget"]').map((wrapper) => {
          return wrapper.prop('data-service');
        });
        expect(serviceKeys.length).toEqual(3);
        expect(serviceKeys).not.toContain(kServiceOfflineWeather);
        expect(serviceKeys).toContain(kServiceTime);
        expect(serviceKeys).toContain(kServiceUptime);
        expect(serviceKeys).toContain(kServiceWeather);
      });

      it('renders the list of widgets available in offline mode', () => {
        // Given
        const component = shallow(
          <StartMenu.ControlledStartMenu
            hasDashboards={true}
            offlineMode={true}
            services={fakeServices}
            settings={fakeSettings}
            widgets={fakeWidgets}
            onAddDashboard={mockOnAddDashboard}
            onAddService={mockOnAddService}
          />
        );

        // When
        const widgets = component.find('div[_hint="Widgets"]').at(0);

        // Then
        const serviceKeys = widgets.find('div[_hint="Widget"]').map((wrapper) => {
          return wrapper.prop('data-service');
        });
        expect(serviceKeys.length).toEqual(2);
        expect(serviceKeys).toContain(kServiceOfflineWeather);
        expect(serviceKeys).toContain(kServiceTime);
        expect(serviceKeys).not.toContain(kServiceUptime);
        expect(serviceKeys).not.toContain(kServiceWeather);
      });

      it('configures and renders a widget item for a known widget', () => {
        // Given
        const component = shallow(
          <StartMenu.ControlledStartMenu
            hasDashboards={true}
            offlineMode={false}
            services={fakeServices}
            settings={fakeSettings}
            widgets={fakeWidgets}
            onAddDashboard={mockOnAddDashboard}
            onAddService={mockOnAddService}
          />
        );

        // When
        const widgetItem = component.find(
          `div[data-service="${kServiceWeather}"]`
        ).at(
          0
        );
        const icon = widgetItem.find(Icon).at(0);
        const title = widgetItem.find('p').at(0);

        // Then
        expect(widgetItem.hasClass('disabled')).toBe(false);
        expect(icon.prop('icon')).toEqual(fakeWidgets[kWidgetWeather].icon);
        expect(title.text()).toEqual(fakeWidgets[kWidgetWeather].title);
      });

      it('configures and renders a widget item for an unknown widget', () => {
        // Given
        const component = shallow(
          <StartMenu.ControlledStartMenu
            hasDashboards={true}
            offlineMode={false}
            services={fakeServices}
            settings={fakeSettings}
            widgets={fakeWidgets}
            onAddDashboard={mockOnAddDashboard}
            onAddService={mockOnAddService}
          />
        );

        // When
        const widgetItem = component.find(
          `div[data-service="${kServiceTime}"]`
        ).at(
          0
        );
        const icon = widgetItem.find(Icon).at(0);
        const title = widgetItem.find('p').at(0);

        // Then
        expect(icon.prop('icon')).toEqual(IconBasicGear);
        expect(title.text()).toEqual('Unnamed Widget');
      });

      it('renders widgets as disabled if it has no dashboards', () => {
        // Given
        const component = shallow(
          <StartMenu.ControlledStartMenu
            hasDashboards={false}
            offlineMode={false}
            services={fakeServices}
            settings={fakeSettings}
            widgets={fakeWidgets}
            onAddDashboard={mockOnAddDashboard}
            onAddService={mockOnAddService}
          />
        );

        // When
        const widgetItem = component.find(
          `div[data-service="${kServiceWeather}"]`
        ).at(
          0
        );

        // Then
        expect(widgetItem.hasClass('disabled')).toBe(true);
      });

      it('handles a widget item click', () => {
        // Given
        const mockEventPreventDefault = jasmine.createSpy();
        const mockEventStopPropagation = jasmine.createSpy();
        const component = shallow(
          <StartMenu.ControlledStartMenu
            hasDashboards={true}
            offlineMode={false}
            services={fakeServices}
            settings={fakeSettings}
            widgets={fakeWidgets}
            onAddDashboard={mockOnAddDashboard}
            onAddService={mockOnAddService}
          />
        );

        // When
        const widgetItem = component.find(
          `div[data-service="${kServiceTime}"]`
        ).at(
          0
        );
        widgetItem.simulate('click', {
          currentTarget: {
            dataset: {service: kServiceTime},
          },
          preventDefault: mockEventPreventDefault,
          stopPropagation: mockEventStopPropagation,
        });

        // Then
        expect(mockOnAddService).toHaveBeenCalledWith(kServiceTime);
        expect(mockEventPreventDefault).not.toHaveBeenCalled();
        expect(mockEventStopPropagation).not.toHaveBeenCalled();
      });

      it('cancels the event when it does not have dashboards', () => {
        // Given
        const mockEventPreventDefault = jasmine.createSpy();
        const mockEventStopPropagation = jasmine.createSpy();
        const component = shallow(
          <StartMenu.ControlledStartMenu
            hasDashboards={false}
            offlineMode={false}
            services={fakeServices}
            settings={fakeSettings}
            widgets={fakeWidgets}
            onAddDashboard={mockOnAddDashboard}
            onAddService={mockOnAddService}
          />
        );

        // When
        const widgetItem = component.find(
          `div[data-service="${kServiceTime}"]`
        ).at(
          0
        );
        widgetItem.simulate('click', {
          currentTarget: {
            dataset: {service: kServiceTime},
          },
          preventDefault: mockEventPreventDefault,
          stopPropagation: mockEventStopPropagation,
        });

        // Then
        expect(mockOnAddService).not.toHaveBeenCalled();
        expect(mockEventPreventDefault).toHaveBeenCalled();
        expect(mockEventStopPropagation).toHaveBeenCalled();
      });
    });

    it('configures and renders the about modal', () => {
      // Given
      const component = shallow(
        <StartMenu.ControlledStartMenu
          hasDashboards={true}
          offlineMode={false}
          services={SERVICES}
          settings={fakeSettings}
          widgets={WIDGETS}
          onAddDashboard={mockOnAddDashboard}
          onAddService={mockOnAddService}
        />
      );

      // When
      const aboutModal = component.find(AboutModal).at(0);

      // Then
      expect(aboutModal.exists()).toBe(true);
      expect(aboutModal.prop('build')).toEqual(123);
      expect(aboutModal.prop('show')).toBe(false);
      expect(aboutModal.prop('version')).toEqual('1.0');
    });

    it('displays the about modal when it closes', () => {
      // Given
      const component = shallow(
        <StartMenu.ControlledStartMenu
          hasDashboards={true}
          offlineMode={false}
          services={SERVICES}
          settings={fakeSettings}
          widgets={WIDGETS}
          onAddDashboard={mockOnAddDashboard}
          onAddService={mockOnAddService}
        />
      );

      // When
      component.find('div[_hint="About"]').at(0).simulate('click');
      component.find(AboutModal).at(0).invoke('onClose')();

      // Then
      const aboutModal = component.find(AboutModal).at(0);
      expect(aboutModal.prop('show')).toBe(false);
    });
  });
});
