import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import fetchMock from 'fetch-mock';

import * as actions from '../../../pages/homepage/actions/posts.js';
import * as errorActions from '../../../pages/homepage/actions/error.js';
import * as constants from '../../../pages/homepage/constants.js';

const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);

describe('post actions', () => {
  afterEach(() => {
    fetchMock.restore();
  });

  it('should create an action to request posts', () => {
    const expectedAction = { type: actions.REQUEST_POSTS };

    expect(actions.requestPosts()).toEqual(expectedAction);
  });

  it('should create an action to mark a post read', () => {
    const expectedAction = { type: actions.MARKING_POST };

    expect(actions.markingPostRead()).toEqual(expectedAction);
  });

  it('should create an action to toggle post saved state', () => {
    const expectedAction = { type: actions.TOGGLING_POST };

    expect(actions.togglingPost()).toEqual(expectedAction);
  });

  it('should create an action receive a post', () => {
    const post = {
      id: 2067,
      remoteIdentifier: 'https://arstechnica.com/?p=1648607',
      title:
        'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge',
      body:
        '"Stale-reference manipulation," 300-character file names, and a clash between worlds.',
      author: 'Kyle Orland',
      publicationDate: '2020-01-24T19:50:12Z',
      url: 'https://arstechnica.com/?p=1648607',
      rule: 5,
      read: false,
      saved: false,
    };

    const expectedAction = {
      type: actions.RECEIVE_POST,
      post,
    };

    expect(actions.receivePost(post)).toEqual(expectedAction);
  });

  it('should create an action to select a post', () => {
    const post = {
      id: 2067,
      remoteIdentifier: 'https://arstechnica.com/?p=1648607',
      title:
        'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge',
      body:
        '"Stale-reference manipulation," 300-character file names, and a clash between worlds.',
      author: 'Kyle Orland',
      publicationDate: '2020-01-24T19:50:12Z',
      url: 'https://arstechnica.com/?p=1648607',
      rule: 5,
      read: false,
      saved: false,
    };

    const expectedAction = {
      type: actions.SELECT_POST,
      post,
    };

    expect(actions.selectPost(post)).toEqual(expectedAction);
  });

  it('should create an action to unselect a post', () => {
    const expectedAction = { type: actions.UNSELECT_POST };

    expect(actions.unSelectPost()).toEqual(expectedAction);
  });

  it('should create an action mark a post read', () => {
    const post = {
      id: 2067,
      remoteIdentifier: 'https://arstechnica.com/?p=1648607',
      title:
        'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge',
      body:
        '"Stale-reference manipulation," 300-character file names, and a clash between worlds.',
      author: 'Kyle Orland',
      publicationDate: '2020-01-24T19:50:12Z',
      url: 'https://arstechnica.com/?p=1648607',
      rule: 5,
      read: false,
      saved: false,
    };

    const rule = {
      id: 4,
      name: 'Ars Technica',
      unread: 100,
      category: 1,
      url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
      favicon: 'https://cdn.arstechnica.net/favicon.ico',
    };

    const expectedAction = {
      type: actions.MARK_POST_READ,
      section: rule,
      post,
    };

    expect(actions.postRead(post, rule)).toEqual(expectedAction);
  });

  it('should create an action toggling post saved', () => {
    const post = {
      id: 2067,
      remoteIdentifier: 'https://arstechnica.com/?p=1648607',
      title:
        'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge',
      body:
        '"Stale-reference manipulation," 300-character file names, and a clash between worlds.',
      author: 'Kyle Orland',
      publicationDate: '2020-01-24T19:50:12Z',
      url: 'https://arstechnica.com/?p=1648607',
      rule: 5,
      read: false,
      saved: false,
    };

    const expectedAction = {
      type: actions.TOGGLED_POST,
      post,
    };

    expect(actions.postToggled(post)).toEqual(expectedAction);
  });

  it('should create multiple actions to mark post read', () => {
    const post = {
      id: 2067,
      remoteIdentifier: 'https://arstechnica.com/?p=1648607',
      title:
        'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge',
      body:
        '"Stale-reference manipulation," 300-character file names, and a clash between worlds.',
      author: 'Kyle Orland',
      publicationDate: '2020-01-24T19:50:12Z',
      url: 'https://arstechnica.com/?p=1648607',
      rule: 5,
      read: false,
      saved: false,
    };

    const rule = {
      id: 1,
      name: 'Test rule',
      unread: 100,
      category: 1,
      url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
      favicon: 'https://cdn.arstechnica.net/favicon.ico',
    };

    fetchMock.patchOnce('/api/posts/2067/', {
      body: { ...post, read: true },
      headers: { 'content-type': 'application/json' },
    });

    const store = mockStore({
      categories: { items: {}, isFetching: false },
      rules: { items: {}, isFetching: false },
      posts: { items: {}, isFetching: false, isUpdating: false },
      selected: {
        item: rule,
        next: false,
        lastReached: false,
        post: {},
      },
    });

    const expectedActions = [
      { type: actions.MARKING_POST },
      {
        type: actions.RECEIVE_POST,
        post: { ...post, read: true },
      },
      {
        type: actions.MARK_POST_READ,
        post: { ...post, read: true },
        section: rule,
      },
    ];

    return store.dispatch(actions.markPostRead(post, 'TOKEN')).then(() => {
      expect(store.getActions()).toEqual(expectedActions);
    });
  });

  it('should create multiple actions to toggle a post saved', () => {
    const post = {
      id: 2067,
      remoteIdentifier: 'https://arstechnica.com/?p=1648607',
      title:
        'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge',
      body:
        '"Stale-reference manipulation," 300-character file names, and a clash between worlds.',
      author: 'Kyle Orland',
      publicationDate: '2020-01-24T19:50:12Z',
      url: 'https://arstechnica.com/?p=1648607',
      rule: 5,
      read: false,
      saved: false,
    };

    const rule = {
      id: 1,
      name: 'Test rule',
      unread: 100,
      category: 1,
      url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
      favicon: 'https://cdn.arstechnica.net/favicon.ico',
    };

    fetchMock.patchOnce('/api/posts/2067/', {
      body: { ...post, saved: true },
      headers: { 'content-type': 'application/json' },
    });

    const store = mockStore({
      categories: { items: {}, isFetching: false },
      rules: { items: {}, isFetching: false },
      posts: { items: {}, isFetching: false, isUpdating: false },
      selected: {
        item: rule,
        next: false,
        lastReached: false,
        post: {},
      },
    });

    const expectedActions = [
      { type: actions.TOGGLING_POST },
      {
        type: actions.RECEIVE_POST,
        post: { ...post, saved: true },
      },
      {
        type: actions.TOGGLED_POST,
        post: { ...post, saved: true },
      },
    ];

    return store.dispatch(actions.toggleSaved(post, 'TOKEN')).then(() => {
      expect(store.getActions()).toEqual(expectedActions);
    });
  });

  it('should create multiple actions to fetch posts by rule', () => {
    const posts = [
      {
        id: 2067,
        remoteIdentifier: 'https://arstechnica.com/?p=1648607',
        title:
          'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge',
        body:
          '"Stale-reference manipulation," 300-character file names, and a clash between worlds.',
        author: 'Kyle Orland',
        publicationDate: '2020-01-24T19:50:12Z',
        url: 'https://arstechnica.com/?p=1648607',
        rule: 4,
        read: false,
        saved: false,
      },
      {
        id: 2141,
        remoteIdentifier: 'https://arstechnica.com/?p=1648757',
        title: 'The most complete brain map ever is here: A fly’s “connectome”',
        body:
          'It took 12 years and at least $40 million to chart a region about 250µm across.',
        author: 'WIRED',
        publicationDate: '2020-01-25T11:06:46Z',
        url: 'https://arstechnica.com/?p=1648757',
        rule: 4,
        read: false,
        saved: false,
      },
    ];

    const rule = {
      id: 4,
      name: 'Ars Technica',
      unread: 100,
      category: 1,
      url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
      favicon: 'https://cdn.arstechnica.net/favicon.ico',
      type: constants.RULE_TYPE,
    };

    fetchMock.getOnce('/api/rules/4/posts/', {
      body: {
        count: 2,
        next: 'https://durp.com/api/rules/4/posts/?cursor=jabadabar',
        previous: null,
        results: posts,
      },
      headers: { 'content-type': 'application/json' },
    });

    const store = mockStore({
      categories: { items: {}, isFetching: false },
      rules: { items: {}, isFetching: false },
      posts: { items: {}, isFetching: false, isUpdating: false },
      selected: { item: {}, next: false, lastReached: false, post: {} },
    });

    const expectedActions = [
      { type: actions.REQUEST_POSTS },
      {
        type: actions.RECEIVE_POSTS,
        next: 'https://durp.com/api/rules/4/posts/?cursor=jabadabar',
        posts,
      },
    ];

    return store.dispatch(actions.fetchPostsBySection(rule)).then(() => {
      expect(store.getActions()).toEqual(expectedActions);
    });
  });

  it('should create multiple actions to fetch posts by category', () => {
    const posts = [
      {
        id: 2067,
        remoteIdentifier: 'https://arstechnica.com/?p=1648607',
        title:
          'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge',
        body:
          '"Stale-reference manipulation," 300-character file names, and a clash between worlds.',
        author: 'Kyle Orland',
        publicationDate: '2020-01-24T19:50:12Z',
        url: 'https://arstechnica.com/?p=1648607',
        rule: 4,
        read: false,
        saved: false,
      },
      {
        id: 2141,
        remoteIdentifier: 'https://arstechnica.com/?p=1648757',
        title: 'The most complete brain map ever is here: A fly’s “connectome”',
        body:
          'It took 12 years and at least $40 million to chart a region about 250µm across.',
        author: 'WIRED',
        publicationDate: '2020-01-25T11:06:46Z',
        url: 'https://arstechnica.com/?p=1648757',
        rule: 4,
        read: false,
        saved: false,
      },
    ];

    const category = {
      id: 1,
      name: 'Tech',
      unread: 2,
      type: constants.CATEGORY_TYPE,
    };

    fetchMock.getOnce('/api/categories/1/posts/', {
      body: {
        count: 2,
        next: 'https://durp.com/api/categories/4/posts/?cursor=jabadabar',
        previous: null,
        results: posts,
      },
      headers: { 'content-type': 'application/json' },
    });

    const store = mockStore({
      categories: { items: {}, isFetching: false },
      rules: { items: {}, isFetching: false },
      posts: { items: {}, isFetching: false, isUpdating: false },
      selected: { item: {}, next: false, lastReached: false, post: {} },
    });

    const expectedActions = [
      { type: actions.REQUEST_POSTS },
      {
        type: actions.RECEIVE_POSTS,
        next: 'https://durp.com/api/categories/4/posts/?cursor=jabadabar',
        posts,
      },
    ];

    return store.dispatch(actions.fetchPostsBySection(category)).then(() => {
      expect(store.getActions()).toEqual(expectedActions);
    });
  });

  it('should create multiple actions to fetch posts by saved state', () => {
    const posts = [
      {
        id: 2067,
        remoteIdentifier: 'https://arstechnica.com/?p=1648607',
        title:
          'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge',
        body:
          '"Stale-reference manipulation," 300-character file names, and a clash between worlds.',
        author: 'Kyle Orland',
        publicationDate: '2020-01-24T19:50:12Z',
        url: 'https://arstechnica.com/?p=1648607',
        rule: 4,
        read: false,
        saved: true,
      },
      {
        id: 2141,
        remoteIdentifier: 'https://arstechnica.com/?p=1648757',
        title: 'The most complete brain map ever is here: A fly’s “connectome”',
        body:
          'It took 12 years and at least $40 million to chart a region about 250µm across.',
        author: 'WIRED',
        publicationDate: '2020-01-25T11:06:46Z',
        url: 'https://arstechnica.com/?p=1648757',
        rule: 4,
        read: false,
        saved: true,
      },
    ];

    fetchMock.getOnce('/api/posts/?saved=true', {
      body: {
        next: 'https://durp.com/api/posts/?cursor=jabadabar&saved=true',
        previous: null,
        results: posts,
      },
      headers: { 'content-type': 'application/json' },
    });

    const store = mockStore({
      categories: { items: {}, isFetching: false },
      rules: { items: {}, isFetching: false },
      posts: { items: {}, isFetching: false, isUpdating: false },
      selected: { item: {}, next: false, lastReached: false, post: {} },
    });

    const expectedActions = [
      { type: actions.REQUEST_POSTS },
      {
        type: actions.RECEIVE_POSTS,
        next: 'https://durp.com/api/posts/?cursor=jabadabar&saved=true',
        posts,
      },
    ];

    return store.dispatch(actions.fetchSavedPosts()).then(() => {
      expect(store.getActions()).toEqual(expectedActions);
    });
  });

  it('should create no actions when fetching posts and section is read', () => {
    const rule = {
      id: 4,
      name: 'Ars Technica',
      unread: 0,
      category: 1,
      url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
      favicon: 'https://cdn.arstechnica.net/favicon.ico',
    };

    const store = mockStore({
      categories: { items: {}, isFetching: false },
      rules: { items: {}, isFetching: false },
      posts: { items: {}, isFetching: false, isUpdating: false },
      selected: { item: {}, next: false, lastReached: false, post: {} },
    });

    const expectedActions = [];

    store.dispatch(actions.fetchPostsBySection(rule));

    expect(store.getActions()).toEqual(expectedActions);
  });

  it('should handle exceptions when marking a post read', () => {
    const post = {
      id: 2067,
      remoteIdentifier: 'https://arstechnica.com/?p=1648607',
      title:
        'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge',
      body:
        '"Stale-reference manipulation," 300-character file names, and a clash between worlds.',
      author: 'Kyle Orland',
      publicationDate: '2020-01-24T19:50:12Z',
      url: 'https://arstechnica.com/?p=1648607',
      rule: 5,
      read: false,
      saved: false,
    };

    const rule = {
      id: 4,
      name: 'Ars Technica',
      unread: 100,
      category: 1,
      url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
      favicon: 'https://cdn.arstechnica.net/favicon.ico',
    };

    const errorMessage = 'Permission denied';

    fetchMock.patch(`/api/posts/${post.id}/`, () => {
      throw new Error(errorMessage);
    });

    const store = mockStore({
      categories: { items: {}, isFetching: false },
      rules: { items: {}, isFetching: false },
      posts: { items: {}, isFetching: false, isUpdating: false },
      selected: { item: { ...rule }, next: false, lastReached: false, post: {} },
    });

    const expectedActions = [
      { type: actions.MARKING_POST },
      { type: actions.RECEIVE_POST, post: {} },
      { type: errorActions.RECEIVE_API_ERROR, error: TypeError(errorMessage) },
    ];

    return store.dispatch(actions.markPostRead(post, 'FAKE_TOKEN')).then(() => {
      expect(store.getActions()).toEqual(expectedActions);
    });
  });

  it('should handle exceptions when toggling a post saved/unsaved', () => {
    const post = {
      id: 2067,
      remoteIdentifier: 'https://arstechnica.com/?p=1648607',
      title:
        'This amazing glitch puts Star Fox 64 ships in an unmodified Zelda cartridge',
      body:
        '"Stale-reference manipulation," 300-character file names, and a clash between worlds.',
      author: 'Kyle Orland',
      publicationDate: '2020-01-24T19:50:12Z',
      url: 'https://arstechnica.com/?p=1648607',
      rule: 5,
      read: false,
      saved: false,
    };

    const rule = {
      id: 4,
      name: 'Ars Technica',
      unread: 100,
      category: 1,
      url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
      favicon: 'https://cdn.arstechnica.net/favicon.ico',
    };

    const errorMessage = 'Permission denied';

    fetchMock.patch(`/api/posts/${post.id}/`, () => {
      throw new Error(errorMessage);
    });

    const store = mockStore({
      categories: { items: {}, isFetching: false },
      rules: { items: {}, isFetching: false },
      posts: { items: {}, isFetching: false, isUpdating: false },
      selected: { item: { ...rule }, next: false, lastReached: false, post: {} },
    });

    const expectedActions = [
      { type: actions.TOGGLING_POST },
      { type: actions.RECEIVE_POST, post: {} },
      { type: errorActions.RECEIVE_API_ERROR, error: Error(errorMessage) },
    ];

    return store.dispatch(actions.toggleSaved(post, 'FAKE_TOKEN')).then(() => {
      expect(store.getActions()).toEqual(expectedActions);
    });
  });

  it('should handle exceptions when fetching posts by section', () => {
    const rule = {
      id: 4,
      name: 'Ars Technica',
      unread: 100,
      category: 1,
      url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
      favicon: 'https://cdn.arstechnica.net/favicon.ico',
      type: constants.RULE_TYPE,
    };

    const errorMessage = 'Page not found';

    fetchMock.getOnce(`/api/rules/${rule.id}/posts/`, () => {
      throw new Error(errorMessage);
    });

    const store = mockStore({
      categories: { items: {}, isFetching: false },
      rules: { items: {}, isFetching: false },
      posts: { items: {}, isFetching: false, isUpdating: false },
      selected: { item: { ...rule }, next: false, lastReached: false, post: {} },
    });

    const expectedActions = [
      { type: actions.REQUEST_POSTS },
      { type: actions.RECEIVE_POSTS, posts: [] },
      { type: errorActions.RECEIVE_API_ERROR, error: Error(errorMessage) },
    ];

    return store.dispatch(actions.fetchPostsBySection(rule)).then(() => {
      expect(store.getActions()).toEqual(expectedActions);
    });
  });
});
