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

import * as actions from '../../../pages/homepage/actions/selected.js';
import * as categoryActions from '../../../pages/homepage/actions/categories.js';
import * as errorActions from '../../../pages/homepage/actions/error.js';
import * as ruleActions from '../../../pages/homepage/actions/rules.js';
import * as constants from '../../../pages/homepage/constants.js';

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

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

  it('should create an action to mark a section read', () => {
    const category = {
      id: 1,
      name: 'Test category',
      unread: 100,
      type: constants.CATEGORY_TYPE,
    };

    const expectedAction = {
      section: { ...category, type: constants.CATEGORY_TYPE },
      type: actions.MARK_SECTION_READ,
    };

    expect(actions.markSectionRead(category)).toEqual(expectedAction);
  });

  it('should create an action to select saved items', () => {
    const expectedAction = {
      type: actions.SELECT_SAVED,
    };

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

  it('should mark a category as read', () => {
    const category = { id: 1, name: 'Test category', unread: 100 };
    const rules = {
      1: {
        id: 1,
        name: 'Ars Technica',
        url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
        favicon: 'https://cdn.arstechnica.net/favicon.ico',
        category: 1,
        unread: 200,
      },
      2: {
        id: 2,
        name: 'Hacker News',
        url: 'https://news.ycombinator.com/rss',
        favicon: 'https://news.ycombinator.com/favicon.ico',
        category: 1,
        unread: 350,
      },
    };

    fetchMock.postOnce('/api/categories/1/read/', {
      body: { ...category, unread: 0 },
      headers: { 'content-type': 'application/json' },
    });

    const expectedActions = [
      { type: categoryActions.REQUEST_CATEGORY },
      {
        type: categoryActions.RECEIVE_CATEGORY,
        category: { ...category, unread: 0 },
      },
      {
        type: actions.MARK_SECTION_READ,
        section: {
          ...category,
          unread: 0,
          rules: rules,
          type: constants.CATEGORY_TYPE,
        },
      },
    ];

    const store = mockStore({
      categories: { items: {}, isFetching: false },
      rules: { items: { ...rules }, isFetching: false },
      posts: { items: {}, isFetching: false },
      selected: {
        item: { ...category, type: actions.CATEGORY_TYPE },
        next: false,
        lastReached: false,
        post: {},
      },
    });

    return store
      .dispatch(actions.markRead({ ...category, type: constants.CATEGORY_TYPE }, 'TOKEN'))
      .then(() => {
        expect(store.getActions()).toEqual(expectedActions);
      });
  });

  it('should mark a rule as read', () => {
    const rule = {
      id: 1,
      name: 'Ars Technica',
      url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
      favicon: 'https://cdn.arstechnica.net/favicon.ico',
      category: 1,
      unread: 200,
    };

    fetchMock.postOnce('/api/rules/1/read/', {
      body: { ...rule, unread: 0 },
      headers: { 'content-type': 'application/json' },
    });

    const expectedActions = [
      { type: ruleActions.REQUEST_RULE },
      {
        type: ruleActions.RECEIVE_RULE,
        rule: { ...rule, unread: 0 },
      },
      {
        type: actions.MARK_SECTION_READ,
        section: { ...rule, type: constants.RULE_TYPE },
      },
    ];

    const store = mockStore({
      categories: { items: {}, isFetching: false },
      rules: { items: { [rule.id]: { ...rule } }, isFetching: false },
      posts: { items: {}, isFetching: false },
      selected: {
        item: { ...rule, type: constants.RULE_TYPE },
        next: false,
        lastReached: false,
        post: {},
      },
    });

    return store
      .dispatch(actions.markRead({ ...rule, type: constants.RULE_TYPE }, 'TOKEN'))
      .then(() => {
        expect(store.getActions()).toEqual(expectedActions);
      });
  });

  it('should handle exceptions when marking a category as read', () => {
    const category = { id: 1, name: 'Test category', unread: 100 };
    const rules = {
      1: {
        id: 1,
        name: 'Ars Technica',
        url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
        favicon: 'https://cdn.arstechnica.net/favicon.ico',
        category: 1,
        unread: 200,
      },
      2: {
        id: 2,
        name: 'Hacker News',
        url: 'https://news.ycombinator.com/rss',
        favicon: 'https://news.ycombinator.com/favicon.ico',
        category: 1,
        unread: 350,
      },
    };

    const errorMessage = 'Page not found';

    fetchMock.postOnce('/api/categories/1/read/', () => {
      throw new Error(errorMessage);
    });

    const expectedActions = [
      { type: categoryActions.REQUEST_CATEGORY },
      { type: categoryActions.RECEIVE_CATEGORY, category: {} },
      { type: errorActions.RECEIVE_API_ERROR, error: Error(errorMessage) },
    ];

    const store = mockStore({
      categories: { items: {}, isFetching: false },
      rules: { items: { ...rules }, isFetching: false },
      posts: { items: {}, isFetching: false },
      selected: {
        item: { ...category, type: actions.CATEGORY_TYPE },
        next: false,
        lastReached: false,
        post: {},
      },
      error: {},
    });

    return store
      .dispatch(actions.markRead({ ...category, type: constants.CATEGORY_TYPE }, 'TOKEN'))
      .then(() => {
        expect(store.getActions()).toEqual(expectedActions);
      });
  });

  it('should handle exceptions when marking a rule as read', () => {
    const rule = {
      id: 1,
      name: 'Ars Technica',
      url: 'http://feeds.arstechnica.com/arstechnica/index?fmt=xml',
      favicon: 'https://cdn.arstechnica.net/favicon.ico',
      category: 1,
      unread: 200,
    };

    const errorMessage = 'Page not found';

    fetchMock.postOnce('/api/rules/1/read/', () => {
      throw new Error(errorMessage);
    });

    const expectedActions = [
      { type: ruleActions.REQUEST_RULE },
      { type: ruleActions.RECEIVE_RULE, rule: {} },
      { type: errorActions.RECEIVE_API_ERROR, error: Error(errorMessage) },
    ];

    const store = mockStore({
      categories: { items: {}, isFetching: false },
      rules: { items: { [rule.id]: { ...rule } }, isFetching: false },
      posts: { items: {}, isFetching: false },
      selected: {
        item: { ...rule, type: constants.RULE_TYPE },
        next: false,
        lastReached: false,
        post: {},
      },
      error: {},
    });

    return store
      .dispatch(actions.markRead({ ...rule, type: constants.RULE_TYPE }, 'TOKEN'))
      .then(() => {
        expect(store.getActions()).toEqual(expectedActions);
      });
  });
});
