import {
  Map as ImmutableMap,
  OrderedMap as ImmutableOrderedMap,
  Record as ImmutableRecord,
} from 'immutable';
import take from 'lodash/take';

import intlMessages from 'soapbox/__fixtures__/intlMessages.json';
import notification from 'soapbox/__fixtures__/notification.json';
import notifications from 'soapbox/__fixtures__/notifications.json';
import relationship from 'soapbox/__fixtures__/relationship.json';
import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS } from 'soapbox/actions/accounts';
import {
  MARKER_FETCH_SUCCESS,
  MARKER_SAVE_REQUEST,
  MARKER_SAVE_SUCCESS,
} from 'soapbox/actions/markers';
import {
  NOTIFICATIONS_EXPAND_SUCCESS,
  NOTIFICATIONS_EXPAND_REQUEST,
  NOTIFICATIONS_EXPAND_FAIL,
  NOTIFICATIONS_FILTER_SET,
  NOTIFICATIONS_SCROLL_TOP,
  NOTIFICATIONS_UPDATE,
  NOTIFICATIONS_UPDATE_QUEUE,
  NOTIFICATIONS_DEQUEUE,
  NOTIFICATIONS_CLEAR,
  NOTIFICATIONS_MARK_READ_REQUEST,
} from 'soapbox/actions/notifications';
import { TIMELINE_DELETE } from 'soapbox/actions/timelines';
import { applyActions } from 'soapbox/jest/test-helpers';

import reducer from '../notifications';

const initialState = reducer(undefined, {});

describe('notifications reducer', () => {
  it('should return the initial state', () => {
    const expected = {
      items: {},
      hasMore: true,
      top: false,
      unread: 0,
      isLoading: false,
      queuedNotifications: {},
      totalQueuedNotificationsCount: 0,
      lastRead: -1,
    };

    expect(ImmutableRecord.isRecord(initialState)).toBe(true);
    expect(initialState.toJS()).toMatchObject(expected);
  });

  describe('NOTIFICATIONS_EXPAND_SUCCESS', () => {
    it('imports the notifications', () => {
      const action = {
        type: NOTIFICATIONS_EXPAND_SUCCESS,
        notifications: take(notifications, 3),
        next: null,
        skipLoading: true,
      };

      const result = reducer(undefined, action);

      // The items are parsed as records
      expect(ImmutableOrderedMap.isOrderedMap(result.items)).toBe(true);
      expect(ImmutableRecord.isRecord(result.items.get('10743'))).toBe(true);

      // We can get an item
      expect(result.items.get('10744').emoji).toEqual('😢');

      // hasMore is set to false because `next` is null
      expect(result.hasMore).toBe(false);
    });

    it('drops invalid notifications', () => {
      const action = {
        type: NOTIFICATIONS_EXPAND_SUCCESS,
        notifications: [
          { id: '1', type: 'mention', status: null, account: { id: '10' } },
          { id: '2', type: 'reblog', status: null, account: { id: '9' } },
          { id: '3', type: 'favourite', status: null, account: { id: '8' } },
          { id: '4', type: 'mention', status: { id: 'a' }, account: { id: '7' } },
          { id: '5', type: 'reblog', status: { id: 'b' }, account: null },
        ],
        next: null,
        skipLoading: true,
      };

      const result = reducer(undefined, action);

      // Only '4' is valid
      expect(result.items.size).toEqual(1);
      expect(result.items.get('4').id).toEqual('4');
    });

    it('imports move notification', () => {
      const action = {
        type: NOTIFICATIONS_EXPAND_SUCCESS,
        notifications: [
          require('soapbox/__fixtures__/pleroma-notification-move.json'),
        ],
        next: null,
        skipLoading: true,
      };

      const result = reducer(undefined, action).items.get('406814');

      expect(result.account).toEqual('AFmHQ18XZ7Lco68MW8');
      expect(result.target).toEqual('A5c5LK7EJTFR0u26Pg');
    });
  });

  describe('NOTIFICATIONS_EXPAND_REQUEST', () => {
    it('sets isLoading to true', () => {
      const state = initialState.set('isLoading', false);
      const action = { type: NOTIFICATIONS_EXPAND_REQUEST };

      expect(reducer(state, action).isLoading).toBe(true);
    });
  });

  describe('NOTIFICATIONS_EXPAND_FAIL', () => {
    it('sets isLoading to false', () => {
      const state = initialState.set('isLoading', true);
      const action = { type: NOTIFICATIONS_EXPAND_FAIL };

      expect(reducer(state, action).isLoading).toBe(false);
    });
  });

  describe('NOTIFICATIONS_FILTER_SET', () => {
    it('clears the items', () => {
      const actions = [{
        type: NOTIFICATIONS_EXPAND_SUCCESS,
        notifications: [
          { id: '1', type: 'mention', status: { id: '4' }, account: { id: '7' } },
          { id: '2', type: 'mention', status: { id: '5' }, account: { id: '8' } },
          { id: '3', type: 'mention', status: { id: '6' }, account: { id: '9' } },
        ],
        next: null,
        skipLoading: true,
      }, {
        type: NOTIFICATIONS_FILTER_SET,
      }];

      // Setup by expanding, then calling `NOTIFICATIONS_FILTER_SET`
      const result = applyActions(initialState, actions, reducer);

      // Setting the filter wipes notifications
      expect(result.items.isEmpty()).toBe(true);
    });

    it('sets hasMore to true', () => {
      const state = initialState.set('hasMore', false);
      const action = { type: NOTIFICATIONS_FILTER_SET };
      const result = reducer(state, action);

      expect(result.hasMore).toBe(true);
    });
  });

  describe('NOTIFICATIONS_SCROLL_TOP', () => {
    it('resets `unread` counter to 0 when top is true (ie, scrolled to the top)', () => {
      const state = initialState.set('unread', 1);
      const action = { type: NOTIFICATIONS_SCROLL_TOP, top: true };
      const result = reducer(state, action);

      expect(result.unread).toEqual(0);
      expect(result.top).toBe(true);
    });

    it('leaves `unread` alone when top is false (ie, not scrolled to top)', () => {
      const state = initialState.set('unread', 3);
      const action = { type: NOTIFICATIONS_SCROLL_TOP, top: false };
      const result = reducer(state, action);

      expect(result.unread).toEqual(3);
      expect(result.top).toBe(false);
    });
  });

  describe('NOTIFICATIONS_UPDATE', () => {
    it('imports the notification', () => {
      const action = { type: NOTIFICATIONS_UPDATE, notification };
      const result = reducer(initialState, action);

      expect(result.items.get('10743').type).toEqual('favourite');
    });

    it('imports follow_request notification', () => {
      const action = {
        type: NOTIFICATIONS_UPDATE,
        notification: require('soapbox/__fixtures__/notification-follow_request.json'),
      };

      const result = reducer(initialState, action);
      expect(result.items.get('87967').type).toEqual('follow_request');
    });

    it('increments `unread` counter when top is false', () => {
      const action = { type: NOTIFICATIONS_UPDATE, notification };
      const result = reducer(initialState, action);

      expect(result.unread).toEqual(1);
    });
  });

  describe('NOTIFICATIONS_UPDATE_QUEUE', () => {
    it('adds the notification to the queue (and increases the counter)', () => {
      const action = {
        type: NOTIFICATIONS_UPDATE_QUEUE,
        notification,
        intlMessages,
        intlLocale: 'en',
      };

      const result = reducer(initialState, action);

      // Doesn't add it as a regular item
      expect(result.items.isEmpty()).toBe(true);

      // Adds it to the queued items
      expect(result.queuedNotifications.size).toEqual(1);
      expect(result.totalQueuedNotificationsCount).toEqual(1);
      expect(result.queuedNotifications.getIn(['10743', 'notification', 'type'])).toEqual('favourite');
    });
  });

  describe('NOTIFICATIONS_DEQUEUE', () => {
    it('resets the queued counter to 0', () => {
      const state = initialState.set('totalQueuedNotificationsCount', 1);
      const action = { type: NOTIFICATIONS_DEQUEUE };
      const result = reducer(state, action);

      expect(result.totalQueuedNotificationsCount).toEqual(0);
    });
  });

  describe('NOTIFICATIONS_EXPAND_SUCCESS', () => {
    it('with non-empty items and next set true', () => {
      const state = ImmutableMap({
        items: ImmutableOrderedMap([
          ['10734', ImmutableMap({
            id: '10734',
            type: 'pleroma:emoji_reaction',
            account: '9vMAje101ngtjlMj7w',
            target: null,
            created_at: '2020-06-10T02:54:39.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: '😢',
            chat_message: null,
          })],
        ]),
        unread: 1,
        hasMore: true,
        isLoading: false,
      });

      const action = {
        type: NOTIFICATIONS_EXPAND_SUCCESS,
        notifications: take(notifications, 3),
        next: true,
      };

      const expected = ImmutableMap({
        items: ImmutableOrderedMap([
          ['10744', ImmutableMap({
            id: '10744',
            type: 'pleroma:emoji_reaction',
            account: '9vMAje101ngtjlMj7w',
            target: null,
            created_at: '2020-06-10T02:54:39.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: '😢',
            chat_message: null,
            total_count: null,
          })],
          ['10743', ImmutableMap({
            id: '10743',
            type: 'favourite',
            account: '9v5c6xSEgAi3Zu1Lv6',
            target: null,
            created_at: '2020-06-10T02:51:05.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: null,
            chat_message: null,
            total_count: null,
          })],
          ['10741', ImmutableMap({
            id: '10741',
            type: 'favourite',
            account: '9v5cKMOPGqPcgfcWp6',
            target: null,
            created_at: '2020-06-10T02:05:06.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: null,
            chat_message: null,
            total_count: null,
          })],
          ['10734', ImmutableMap({
            id: '10734',
            type: 'pleroma:emoji_reaction',
            account: '9vMAje101ngtjlMj7w',
            target: null,
            created_at: '2020-06-10T02:54:39.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: '😢',
            chat_message: null,
          })],
        ]),
        unread: 1,
        hasMore: true,
        isLoading: false,
      });

      expect(reducer(state, action).toJS()).toEqual(expected.toJS());
    });

    it('with empty items and next set true', () => {
      const state = ImmutableMap({
        items: ImmutableOrderedMap(),
        unread: 1,
        hasMore: true,
        isLoading: false,
      });

      const action = {
        type: NOTIFICATIONS_EXPAND_SUCCESS,
        notifications: take(notifications, 3),
        next: true,
      };

      const expected = ImmutableMap({
        items: ImmutableOrderedMap([
          ['10744', ImmutableMap({
            id: '10744',
            type: 'pleroma:emoji_reaction',
            account: '9vMAje101ngtjlMj7w',
            target: null,
            created_at: '2020-06-10T02:54:39.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: '😢',
            chat_message: null,
            total_count: null,
          })],
          ['10743', ImmutableMap({
            id: '10743',
            type: 'favourite',
            account: '9v5c6xSEgAi3Zu1Lv6',
            target: null,
            created_at: '2020-06-10T02:51:05.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: null,
            chat_message: null,
            total_count: null,
          })],
          ['10741', ImmutableMap({
            id: '10741',
            type: 'favourite',
            account: '9v5cKMOPGqPcgfcWp6',
            target: null,
            created_at: '2020-06-10T02:05:06.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: null,
            chat_message: null,
            total_count: null,
          })],
        ]),
        unread: 1,
        hasMore: true,
        isLoading: false,
      });

      expect(reducer(state, action).toJS()).toEqual(expected.toJS());
    });
  });

  describe('ACCOUNT_BLOCK_SUCCESS', () => {
    it('should handle', () => {
      const state = ImmutableMap({
        items: ImmutableOrderedMap([
          ['10744', ImmutableMap({
            id: '10744',
            type: 'pleroma:emoji_reaction',
            account: '9vMAje101ngtjlMj7w',
            target: null,
            created_at: '2020-06-10T02:54:39.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: '😢',
            chat_message: null,
          })],
          ['10743', ImmutableMap({
            id: '10743',
            type: 'favourite',
            account: '9v5c6xSEgAi3Zu1Lv6',
            target: null,
            created_at: '2020-06-10T02:51:05.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: null,
            chat_message: null,
          })],
          ['10741', ImmutableMap({
            id: '10741',
            type: 'favourite',
            account: '9v5cKMOPGqPcgfcWp6',
            target: null,
            created_at: '2020-06-10T02:05:06.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: null,
            chat_message: null,
          })],
        ]),
      });
      const action = {
        type: ACCOUNT_BLOCK_SUCCESS,
        relationship,
      };
      expect(reducer(state, action)).toEqual(ImmutableMap({
        items: ImmutableOrderedMap([
          ['10743', ImmutableMap({
            id: '10743',
            type: 'favourite',
            account: '9v5c6xSEgAi3Zu1Lv6',
            target: null,
            created_at: '2020-06-10T02:51:05.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: null,
            chat_message: null,
          })],
          ['10741', ImmutableMap({
            id: '10741',
            type: 'favourite',
            account: '9v5cKMOPGqPcgfcWp6',
            target: null,
            created_at: '2020-06-10T02:05:06.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: null,
            chat_message: null,
          })],
        ]),
      }));
    });
  });

  describe('ACCOUNT_MUTE_SUCCESS', () => {
    it('should handle', () => {
      const state = ImmutableMap({
        items: ImmutableOrderedMap([
          ['10744', ImmutableMap({
            id: '10744',
            type: 'pleroma:emoji_reaction',
            account: '9vMAje101ngtjlMj7w',
            target: null,
            created_at: '2020-06-10T02:54:39.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: '😢',
            chat_message: null,
          })],
          ['10743', ImmutableMap({
            id: '10743',
            type: 'favourite',
            account: '9v5c6xSEgAi3Zu1Lv6',
            target: null,
            created_at: '2020-06-10T02:51:05.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: null,
            chat_message: null,
          })],
          ['10741', ImmutableMap({
            id: '10741',
            type: 'favourite',
            account: '9v5cKMOPGqPcgfcWp6',
            target: null,
            created_at: '2020-06-10T02:05:06.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: null,
            chat_message: null,
          })],
        ]),
      });
      const action = {
        type: ACCOUNT_MUTE_SUCCESS,
        relationship: relationship,
      };
      expect(reducer(state, action)).toEqual(ImmutableMap({
        items: ImmutableOrderedMap([
          ['10743', ImmutableMap({
            id: '10743',
            type: 'favourite',
            account: '9v5c6xSEgAi3Zu1Lv6',
            target: null,
            created_at: '2020-06-10T02:51:05.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: null,
            chat_message: null,
          })],
          ['10741', ImmutableMap({
            id: '10741',
            type: 'favourite',
            account: '9v5cKMOPGqPcgfcWp6',
            target: null,
            created_at: '2020-06-10T02:05:06.000Z',
            status: '9vvNxoo5EFbbnfdXQu',
            emoji: null,
            chat_message: null,
          })],
        ]),
      }));
    });
  });

  describe('NOTIFICATIONS_CLEAR', () => {
    it('clears the items', () => {
      const state = initialState.set('items', ImmutableOrderedMap([['1', {}], ['2', {}]]));
      const action = { type: NOTIFICATIONS_CLEAR };
      const result = reducer(state, action);

      expect(result.items.isEmpty()).toBe(true);
    });
  });

  describe('NOTIFICATIONS_MARK_READ_REQUEST', () => {
    it('sets lastRead to the one in the action', () => {
      const action = { type: NOTIFICATIONS_MARK_READ_REQUEST, lastRead: '1234' };
      const result = reducer(undefined, action);

      expect(result.lastRead).toEqual('1234');
    });
  });

  describe('TIMELINE_DELETE', () => {
    it('deletes notifications corresponding to the status ID', () => {
      const actions = [{
        type: NOTIFICATIONS_EXPAND_SUCCESS,
        notifications: [
          { id: '1', type: 'mention', status: { id: '4' }, account: { id: '7' } },
          { id: '2', type: 'mention', status: { id: '5' }, account: { id: '8' } },
          { id: '3', type: 'mention', status: { id: '6' }, account: { id: '9' } },
          { id: '4', type: 'mention', status: { id: '5' }, account: { id: '7' } },
        ],
        next: null,
        skipLoading: true,
      }, {
        type: TIMELINE_DELETE,
        id: '5',
      }];

      // Setup by expanding, then calling `NOTIFICATIONS_FILTER_SET`
      const result = applyActions(initialState, actions, reducer);

      expect(result.items.size).toEqual(2);
      expect(result.items.get('5')).toBe(undefined);
    });
  });

  describe('MARKER_FETCH_SUCCESS', () => {
    it('sets lastRead', () => {
      const action = {
        type: MARKER_FETCH_SUCCESS,
        timeline: ['notifications'],
        marker: {
          notifications: {
            last_read_id: '1234',
          },
        },
      };

      expect(reducer(undefined, action).get('lastRead')).toEqual('1234');
    });

    it('updates the unread count', () => {
      const action = {
        type: MARKER_FETCH_SUCCESS,
        timeline: ['notifications'],
        marker: {
          notifications: {
            last_read_id: '5678',
          },
        },
      };

      const state = ImmutableMap({
        items: ImmutableOrderedMap({
          '9012': ImmutableMap({ id: '9012' }),
          '5678': ImmutableMap({ id: '5678' }),
          '1234': ImmutableMap({ id: '1234' }),
        }),
        unread: 3,
      });

      expect(reducer(state, action).get('unread')).toEqual(1);
    });
  });

  describe('MARKER_SAVE_REQUEST', () => {
    it('sets lastRead', () => {
      const action = {
        type: MARKER_SAVE_REQUEST,
        timeline: ['notifications'],
        marker: {
          notifications: {
            last_read_id: '1234',
          },
        },
      };

      expect(reducer(undefined, action).get('lastRead')).toEqual('1234');
    });

    it('updates the unread count', () => {
      const action = {
        type: MARKER_SAVE_REQUEST,
        timeline: ['notifications'],
        marker: {
          notifications: {
            last_read_id: '5678',
          },
        },
      };

      const state = ImmutableMap({
        items: ImmutableOrderedMap({
          '9012': ImmutableMap({ id: '9012' }),
          '5678': ImmutableMap({ id: '5678' }),
          '1234': ImmutableMap({ id: '1234' }),
        }),
        unread: 3,
      });

      expect(reducer(state, action).get('unread')).toEqual(1);
    });
  });

  describe('MARKER_SAVE_SUCCESS', () => {
    it('sets lastRead', () => {
      const action = {
        type: MARKER_SAVE_SUCCESS,
        timeline: ['notifications'],
        marker: {
          notifications: {
            last_read_id: '5678',
          },
        },
      };

      expect(reducer(undefined, action).get('lastRead')).toEqual('5678');
    });

    it('updates the unread count', () => {
      const action = {
        type: MARKER_SAVE_SUCCESS,
        timeline: ['notifications'],
        marker: {
          notifications: {
            last_read_id: '9012',
          },
        },
      };

      const state = ImmutableMap({
        items: ImmutableOrderedMap({
          '9012': ImmutableMap({ id: '9012' }),
          '5678': ImmutableMap({ id: '5678' }),
          '1234': ImmutableMap({ id: '1234' }),
        }),
        unread: 3,
      });

      expect(reducer(state, action).get('unread')).toEqual(0);
    });
  });
});
