import { List as ImmutableList, Map as ImmutableMap, fromJS } from 'immutable';

import { normalizeStatus } from 'soapbox/normalizers';

import {
  sortEmoji,
  mergeEmojiFavourites,
  filterEmoji,
  oneEmojiPerAccount,
  reduceEmoji,
  getReactForStatus,
  simulateEmojiReact,
  simulateUnEmojiReact,
} from '../emoji-reacts';

const ALLOWED_EMOJI = ImmutableList([
  '👍',
  '❤',
  '😂',
  '😯',
  '😢',
  '😡',
]);

describe('filterEmoji', () => {
  describe('with a mix of allowed and disallowed emoji', () => {
    const emojiReacts = fromJS([
      { 'count': 1, 'me': true, 'name': '🌵' },
      { 'count': 1, 'me': true, 'name': '😂' },
      { 'count': 1, 'me': true, 'name': '👀' },
      { 'count': 1, 'me': true, 'name': '🍩' },
      { 'count': 1, 'me': true, 'name': '😡' },
      { 'count': 1, 'me': true, 'name': '🔪' },
      { 'count': 1, 'me': true, 'name': '😠' },
    ]) as ImmutableList<ImmutableMap<string, any>>;
    it('filters only allowed emoji', () => {
      expect(filterEmoji(emojiReacts, ALLOWED_EMOJI)).toEqual(fromJS([
        { 'count': 1, 'me': true, 'name': '😂' },
        { 'count': 1, 'me': true, 'name': '😡' },
      ]));
    });
  });
});

describe('sortEmoji', () => {
  describe('with an unsorted list of emoji', () => {
    const emojiReacts = fromJS([
      { 'count': 7,  'me': true, 'name': '😯' },
      { 'count': 3,  'me': true, 'name': '😢' },
      { 'count': 1,  'me': true, 'name': '😡' },
      { 'count': 20, 'me': true, 'name': '👍' },
      { 'count': 7,  'me': true, 'name': '😂' },
      { 'count': 15, 'me': true, 'name': '❤' },
    ]) as ImmutableList<ImmutableMap<string, any>>;
    it('sorts the emoji by count', () => {
      expect(sortEmoji(emojiReacts)).toEqual(fromJS([
        { 'count': 20, 'me': true, 'name': '👍' },
        { 'count': 15, 'me': true, 'name': '❤' },
        { 'count': 7,  'me': true, 'name': '😯' },
        { 'count': 7,  'me': true, 'name': '😂' },
        { 'count': 3,  'me': true, 'name': '😢' },
        { 'count': 1,  'me': true, 'name': '😡' },
      ]));
    });
  });
});

describe('mergeEmojiFavourites', () => {
  const favouritesCount = 12;
  const favourited = true;

  describe('with existing 👍 reacts', () => {
    const emojiReacts = fromJS([
      { 'count': 20, 'me': false, 'name': '👍' },
      { 'count': 15, 'me': false, 'name': '❤' },
      { 'count': 7,  'me': false, 'name': '😯' },
    ]) as ImmutableList<ImmutableMap<string, any>>;
    it('combines 👍 reacts with favourites', () => {
      expect(mergeEmojiFavourites(emojiReacts, favouritesCount, favourited)).toEqual(fromJS([
        { 'count': 32, 'me': true,  'name': '👍' },
        { 'count': 15, 'me': false, 'name': '❤' },
        { 'count': 7,  'me': false, 'name': '😯' },
      ]));
    });
  });

  describe('without existing 👍 reacts', () => {
    const emojiReacts = fromJS([
      { 'count': 15, 'me': false, 'name': '❤' },
      { 'count': 7,  'me': false, 'name': '😯' },
    ]) as ImmutableList<ImmutableMap<string, any>>;
    it('adds 👍 reacts to the map equaling favourite count', () => {
      expect(mergeEmojiFavourites(emojiReacts, favouritesCount, favourited)).toEqual(fromJS([
        { 'count': 15, 'me': false, 'name': '❤' },
        { 'count': 7,  'me': false, 'name': '😯' },
        { 'count': 12, 'me': true,  'name': '👍' },
      ]));
    });
    it('does not add 👍 reacts when there are no favourites', () => {
      expect(mergeEmojiFavourites(emojiReacts, 0, false)).toEqual(fromJS([
        { 'count': 15, 'me': false,  'name': '❤' },
        { 'count': 7,  'me': false,  'name': '😯' },
      ]));
    });
  });
});

describe('reduceEmoji', () => {
  describe('with a clusterfuck of emoji', () => {
    const emojiReacts = fromJS([
      { 'count': 1,  'me': false, 'name': '😡' },
      { 'count': 1,  'me': true,  'name': '🔪' },
      { 'count': 7,  'me': true,  'name': '😯' },
      { 'count': 3,  'me': false, 'name': '😢' },
      { 'count': 1,  'me': true,  'name': '🌵' },
      { 'count': 20, 'me': true,  'name': '👍' },
      { 'count': 7,  'me': false, 'name': '😂' },
      { 'count': 15, 'me': true,  'name': '❤' },
      { 'count': 1,  'me': false, 'name': '👀' },
      { 'count': 1,  'me': false, 'name': '🍩' },
    ]) as ImmutableList<ImmutableMap<string, any>>;
    it('sorts, filters, and combines emoji and favourites', () => {
      expect(reduceEmoji(emojiReacts, 7, true, ALLOWED_EMOJI)).toEqual(fromJS([
        { 'count': 27, 'me': true,  'name': '👍' },
        { 'count': 15, 'me': true,  'name': '❤' },
        { 'count': 7,  'me': true,  'name': '😯' },
        { 'count': 7,  'me': false, 'name': '😂' },
        { 'count': 3,  'me': false, 'name': '😢' },
        { 'count': 1,  'me': false, 'name': '😡' },
      ]));
    });
  });
});

describe('oneEmojiPerAccount', () => {
  it('reduces to one react per account', () => {
    const emojiReacts = fromJS([
      // Sorted
      { 'count': 2, 'me': true,  'name': '👍', accounts: [{ id: '1' }, { id: '2' }] },
      { 'count': 2, 'me': true,  'name': '❤', accounts: [{ id: '1' }, { id: '2' }] },
      { 'count': 1, 'me': true,  'name': '😯', accounts: [{ id: '1' }] },
      { 'count': 1, 'me': false, 'name': '😂', accounts: [{ id: '3' }] },
    ]) as ImmutableList<ImmutableMap<string, any>>;
    expect(oneEmojiPerAccount(emojiReacts, '1')).toEqual(fromJS([
      { 'count': 2, 'me': true,  'name': '👍', accounts: [{ id: '1' }, { id: '2' }] },
      { 'count': 1, 'me': false, 'name': '😂', accounts: [{ id: '3' }] },
    ]));
  });
});

describe('getReactForStatus', () => {
  it('returns a single owned react (including favourite) for the status', () => {
    const status = normalizeStatus(fromJS({
      favourited: false,
      pleroma: {
        emoji_reactions: [
          { 'count': 20, 'me': false, 'name': '👍' },
          { 'count': 15, 'me': true,  'name': '❤' },
          { 'count': 7,  'me': true,  'name': '😯' },
          { 'count': 7,  'me': false, 'name': '😂' },
        ],
      },
    }));
    expect(getReactForStatus(status, ALLOWED_EMOJI)).toEqual('❤');
  });

  it('returns a thumbs-up for a favourite', () => {
    const status = normalizeStatus(fromJS({ favourites_count: 1, favourited: true }));
    expect(getReactForStatus(status)).toEqual('👍');
  });

  it('returns undefined when a status has no reacts (or favourites)', () => {
    const status = normalizeStatus(fromJS({}));
    expect(getReactForStatus(status)).toEqual(undefined);
  });

  it('returns undefined when a status has no valid reacts (or favourites)', () => {
    const status = normalizeStatus(fromJS([
      { 'count': 1,  'me': true,  'name': '🔪' },
      { 'count': 1,  'me': true,  'name': '🌵' },
      { 'count': 1,  'me': false, 'name': '👀' },
      { 'count': 1,  'me': false, 'name': '🍩' },
    ]));
    expect(getReactForStatus(status)).toEqual(undefined);
  });
});

describe('simulateEmojiReact', () => {
  it('adds the emoji to the list', () => {
    const emojiReacts = fromJS([
      { 'count': 2, 'me': false, 'name': '👍' },
      { 'count': 2, 'me': false, 'name': '❤' },
    ]) as ImmutableList<ImmutableMap<string, any>>;
    expect(simulateEmojiReact(emojiReacts, '❤')).toEqual(fromJS([
      { 'count': 2, 'me': false, 'name': '👍' },
      { 'count': 3, 'me': true,  'name': '❤' },
    ]));
  });

  it('creates the emoji if it didn\'t already exist', () => {
    const emojiReacts = fromJS([
      { 'count': 2, 'me': false, 'name': '👍' },
      { 'count': 2, 'me': false, 'name': '❤' },
    ]) as ImmutableList<ImmutableMap<string, any>>;
    expect(simulateEmojiReact(emojiReacts, '😯')).toEqual(fromJS([
      { 'count': 2, 'me': false, 'name': '👍' },
      { 'count': 2, 'me': false, 'name': '❤' },
      { 'count': 1, 'me': true,  'name': '😯' },
    ]));
  });
});

describe('simulateUnEmojiReact', () => {
  it('removes the emoji from the list', () => {
    const emojiReacts = fromJS([
      { 'count': 2, 'me': false, 'name': '👍' },
      { 'count': 3, 'me': true, 'name': '❤' },
    ]) as ImmutableList<ImmutableMap<string, any>>;
    expect(simulateUnEmojiReact(emojiReacts, '❤')).toEqual(fromJS([
      { 'count': 2, 'me': false, 'name': '👍' },
      { 'count': 2, 'me': false,  'name': '❤' },
    ]));
  });

  it('removes the emoji if it\'s the last one in the list', () => {
    const emojiReacts = fromJS([
      { 'count': 2, 'me': false, 'name': '👍' },
      { 'count': 2, 'me': false, 'name': '❤' },
      { 'count': 1, 'me': true,  'name': '😯' },
    ]) as ImmutableList<ImmutableMap<string, any>>;
    expect(simulateUnEmojiReact(emojiReacts, '😯')).toEqual(fromJS([
      { 'count': 2, 'me': false, 'name': '👍' },
      { 'count': 2, 'me': false, 'name': '❤' },
    ]));
  });
});
