import debounce from 'debounce';
import { shallow } from 'enzyme';
import Porthole from 'porthole-proxy';
import React from 'react';

import { ATTACHMENT_FIXTURE } from '../../../tests/fixtures/scorm-attachment';
import { VALUES_FIXTURE } from '../../../tests/fixtures/scorm-values';
import { Site } from '../../../tests/storyshare';
import { mapStateToProps, MediaScorm } from './MediaScorm';
import DefaultItem from '../Item/Item';

jest.mock('debounce');

describe('mapStateToProps', () => {
  const site = {
    getResponseByRoute: jest.fn().mockReturnValue({ body: VALUES_FIXTURE }),
  };

  it('retrieves items from the DataLayer cache', () => {
    mapStateToProps(null, { site, id: 4 });

    expect(site.getResponseByRoute).toHaveBeenCalled();
    expect(site.getResponseByRoute)
      .toHaveBeenCalledWith('storyshare/v1/scorm/state/4');
  });

  it('passes retrieved items to the component', () => {
    expect(mapStateToProps(null, { site, id: 4 })).toEqual({
      scormState: VALUES_FIXTURE,
    });
  });
});

describe('MediaScorm component', () => {
  let wrapper;
  let WindowProxy;
  const site = new Site();

  beforeAll(() => {
    debounce.mockImplementation((f) => () => f());

    WindowProxy = {
      addEventListener: jest.fn(),
      post: jest.fn(),
    };

    Porthole.WindowProxy = () => WindowProxy;
  });

  beforeEach(() => {
    jest.spyOn(site, 'objectToResponse');
    jest.spyOn(site, 'getResponseByRoute').mockReturnValue(VALUES_FIXTURE);
    site.objectToResponse.mockClear();

    const request = {
      read: jest.fn(),
    };

    jest.spyOn(site, 'req').mockReturnValue(request);
    site.req.mockClear();

    WindowProxy.addEventListener.mockClear();
    WindowProxy.post.mockClear();

    wrapper = shallow(
      <MediaScorm
        id={ 4 }
        site={ site }
        scormState={ VALUES_FIXTURE }
        onScormEvent={ () => {
        } }
      />
    );
  });

  const getIframeItem = () => {
    site.objectToResponse.mockReturnValue({
      offline: jest.fn().mockReturnValueOnce(ATTACHMENT_FIXTURE),
    });
    const children = wrapper.find(DefaultItem).prop('children');
    return shallow(<div>{ children(ATTACHMENT_FIXTURE) }</div>).find('iframe');
  };

  it('renders', () => {
    expect(wrapper).toExist();
  });

  it('renders an Item to retrieve medium', () => {
    const scormItem = wrapper.find(DefaultItem);

    expect(scormItem).toExist();
    expect(scormItem).toHaveProp('id', 4);
    expect(scormItem).toHaveProp('typeSlug', 'media');
  });

  it('requests data from the server', () => {
    expect(site.req).toHaveBeenCalledWith('storyshare/v1/scorm/state/4');
  });

  it('renders an iframe', () => {
    expect(getIframeItem().find('iframe')).toExist();
  });

  it('passes unknown props through to the iframe', () => {
    wrapper.setProps({ unknown: 'value' });
    expect(getIframeItem().find('iframe'))
      .toHaveProp('unknown', 'value');
  });

  it('avoids passing internal props through to the iframe', () => {
    const frame = getIframeItem().find('iframe');

    for (const prop in [
      'onScormEvent',
      'scormState',
      'site',
    ]) {
      expect(frame).not.toHaveProp(prop);
    }
  });

  it('avoids passing react-redux dispatch to the iframe', () => {
    wrapper.setProps({ dispatch: () => null });
    expect(getIframeItem().find('Select')).not.toHaveProp('dispatch');
  });

  it('suppresses rendering completely if the supplied ID is 0', () => {
    wrapper.setProps({ id: 0 });
    expect(wrapper).toBeEmptyRender();
  });

  it('renders custom Placeholder if provided', () => {
    const placeholder = (
      <div>Custom placeholder</div>
    );
    wrapper.setProps({ placeholder });

    const item = wrapper.find(DefaultItem);
    expect(item).toHaveProp('placeholder', placeholder);
  });

  it('uses a generated frame id', () => {
    const frame = getIframeItem();
    expect(frame).toHaveProp('name');
    expect(frame.prop('name')).toMatch(/^scorm/);
  });

  it('renders nothing in case of an error', () => {
    const frame = getIframeItem();
    frame.simulate('error');
    expect(wrapper.type()).toBe(null);
  });

  it('does have src attribute', () => {
    expect(getIframeItem()).toHaveProp('src');
  });

  it('loads the iframe', () => {
    getIframeItem().simulate('load');
    expect(WindowProxy.post).toHaveBeenCalledWith({
      'message': {
        'post_id': 4,
        // eslint-disable-next-line max-len
        'proxy_url': '//storyshare-cdn.example.com/uploads/static/scorm/scorm.html?ver=1558384483',
        // eslint-disable-next-line max-len
        'scorm_content_url': '//storyshare-cdn.example.com/uploads/cache/scorm/430-d6a7ce2b05231e34605ca40e19ec742e/shared/launchpage.html',
        'values': { 'cmi.location': 4, 'cmi.score': 400 },
      },
      'type': 'config',
    });
  });

  it('sends scorm events', () => {
    const request = {
      update: jest.fn(),
      setParams: jest.fn().mockReturnThis(),
    };

    jest.spyOn(site, 'req').mockReturnValue(request);

    wrapper.rerender();
    getIframeItem().simulate('load');

    WindowProxy.addEventListener.mock.calls[0][0]({
      data: {
        type: 'SetValue',
        message: {
          key: 'cmi.position',
          value: 99,
        },
      },
    });

    expect(site.req).toHaveBeenCalledWith('storyshare/v1/scorm/state/4');
    expect(request.setParams).toHaveBeenCalledWith({ 'cmi.position': 99 });
  });

  it('sends scorm events to its parent', () => {
    const onScormEvent = jest.fn();

    const request = {
      update: jest.fn(),
      setParams: jest.fn().mockReturnThis(),
    };

    jest.spyOn(site, 'req').mockReturnValue(request);

    wrapper.setProps({
      onScormEvent,
    });

    getIframeItem().simulate('load');

    WindowProxy.addEventListener.mock.calls[0][0]({
      data: {
        type: 'SetValue',
        message: {
          key: 'cmi.position',
          value: 99,
        },
      },
    });

    expect(onScormEvent).toHaveBeenCalledWith({
      message: {
        key: 'cmi.position',
        value: 99,
      },
      type: 'SetValue',
    });
  });

  it('does contain correct value in src', () => {
    expect(getIframeItem()).toHaveProp(
      'src',
      // eslint-disable-next-line max-len
      '//storyshare-cdn.example.com/uploads/static/scorm/scorm.html?ver=1558384483'
    );
  });
});
