import React from 'react';
import { mount } from 'enzyme';
import { act } from '@testing-library/react-hooks';

import Tutorial from '.';
import { TutorialCard, TutorialModal, TutorialObstacle } from '@ui/compositions';
import { Toggle } from '@ui/components';
import { DEFAULT_TUTORIAL } from '@ui/compositions/Tutorials/Modal/constants/stepDefaults';

import * as ApiActions from './apiActions';
import fetchMock from 'fetch-mock';
import 'isomorphic-fetch';

const tutorials = [
  {
    _id: '222',
    backgroundColor: '#fff',
    description: 'First title card desc',
    emoji: '',
    hero: true,
    published: true,
    title: 'First title',
  },
  {
    _id: '443',
    backgroundColor: '#fff',
    description: 'Second title card desc',
    emoji: '',
    hero: false,
    published: false,
    title: 'Second Title',
  },
];

const getClientRectSpy = jest.fn(() => ({ width: 100 }));
Range.prototype.getBoundingClientRect = getClientRectSpy;
Range.prototype.getClientRects = getClientRectSpy;

describe('Tutorial', () => {
  describe('Defaults', () => {
    it('should display empty state by default', async () => {
      fetchMock.get('http://localhost:3000/tutorials/data', { tutorials: [], hero: {}, moduleEnabled: true });
      const node = mount(
        <Tutorial baseUrl={'http://localhost:3000'} confirmationTarget={'body'} isAdmin={true} modalTarget={'body'} />
      );
      await act(async () => {
        await Promise.resolve(node);
        await new Promise(resolve => setImmediate(resolve));
        node.update();
      });

      node.update();
      expect(node.find(TutorialObstacle)).toHaveLength(1);
      fetchMock.done();
    });
  });

  describe('View and Data', () => {
    let node;

    beforeAll(() => {
      fetchMock.get(
        'http://localhost:3000/tutorials/data',
        { tutorials, hero: tutorials[0], moduleEnabled: true },
        { overwriteRoutes: true }
      );
      fetchMock.get('http://localhost:3000/tutorials/endpoints', {
        categoryId: 'id',
        children: [],
        title: 'page title',
      });
    });

    beforeEach(() => {
      node = mount(
        <Tutorial baseUrl={'http://localhost:3000'} confirmationTarget={'body'} isAdmin={true} modalTarget={'body'} />
      );
    });

    afterAll(() => {
      fetchMock.restore();
      fetchMock.reset();
    });

    it('should not display button bar contents if not admin', async () => {
      const n2 = mount(
        <Tutorial baseUrl={'http://localhost:3000'} confirmationTarget={'body'} isAdmin={false} modalTarget={'body'} />
      );
      await act(async () => {
        await Promise.resolve(n2);
        await new Promise(resolve => setImmediate(resolve));
        n2.update();
      });
      const buttonBar = n2.find(TutorialCard).find('CardButtonBar');
      expect(buttonBar.find('.TutorialCard-Toolbar-Arrow')).toHaveLength(0);
    });

    it('should call getTutorials on init', async () => {
      const n2 = mount(
        <Tutorial baseUrl={'http://localhost:3000'} confirmationTarget={'body'} isAdmin={false} modalTarget={'body'} />
      );
      await act(async () => {
        await Promise.resolve(n2);
        await new Promise(resolve => setImmediate(resolve));
        n2.update();
      });

      const cards = n2.find(TutorialCard).find('CardButtonBar');
      expect(cards).toHaveLength(2);
    });

    describe('confirmation modal', () => {
      let n2;
      let card;

      beforeEach(async () => {
        n2 = mount(
          <Tutorial baseUrl={'http://localhost:3000'} confirmationTarget={'body'} isAdmin={true} modalTarget={'body'} />
        );
        await act(async () => {
          await Promise.resolve(n2);
          await new Promise(resolve => setImmediate(resolve));
          n2.update();
        });

        card = n2.find('TutorialCard').first();
      });

      it('should open on publish', async () => {
        card.prop('publish')({ _id: '123' });
        await act(() => n2.update());

        const modal = n2.find('.TutorialConfirmation');
        const title = modal.find('h4').first();
        expect(title.text()).toStrictEqual('Are you sure you want to unpublish this tutorial?');
      });

      it('should open on delete', async () => {
        card.prop('deleteTutorial')({ _id: '123' });
        await act(() => n2.update());

        const modal = n2.find('.TutorialConfirmation');
        const title = modal.find('h4').first();
        expect(title.text()).toStrictEqual('Are you sure you want to delete this tutorial?');
      });

      it('should open on makeHero', async () => {
        card.prop('makeHero')({ _id: '123', published: false });
        await act(() => n2.update());

        const modal = n2.find('.TutorialConfirmation');
        const title = modal.find('h4').first();
        expect(title.text()).toStrictEqual('Are you sure you want to feature an unpublished tutorial?');
      });

      it('should call api if tutorial is published', async () => {
        fetchMock.post('http://localhost:3000/tutorials/123/makeHero', { success: true });
        card.prop('makeHero')({ _id: '123', published: true });
        await act(() => n2.update());

        const modal = n2.find('.TutorialConfirmation');
        const title = modal.find('h4').first();
        expect(title.text()).toStrictEqual('Title');
      });
    });

    it('should handle module toggle', () => {
      fetchMock.post('http://localhost:3000/tutorials/toggleModule', { success: true });
      const toggle = node.find(Toggle).find('input');
      const spy = jest.spyOn(ApiActions, 'toggleModule');

      toggle.simulate('change', { test: true });
      expect(spy).toHaveBeenCalledTimes(1);
    });

    it('should set state when modal is opened from creation', () => {
      const button = node.find('.TutorialToolbar').find('button').at(1);
      button.simulate('click');

      const modal = node.find(TutorialModal);
      expect(modal.prop('action')).toStrictEqual('Create');
    });

    it('should call closeTutorialModal correctly', () => {
      const modal = node.find(TutorialModal);
      modal.prop('closeTutorialModal')();
      const m2 = node.find(TutorialModal);
      expect(m2.prop('open')).toStrictEqual(false);
    });

    it('should call for tutorial data when a slug is passed', () => {
      fetchMock.get('http://localhost:3000/tutorials/test/data', { tutorial: DEFAULT_TUTORIAL });
      const spy = jest.spyOn(ApiActions, 'getTutorialBySlug');
      mount(
        <Tutorial
          baseUrl={'http://localhost:3000'}
          confirmationTarget={'body'}
          isAdmin={false}
          modalTarget={'body'}
          slug={'test'}
        />
      );

      expect(spy).toHaveBeenCalledTimes(1);
    });
  });
});
