import * as React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import expect from 'expect';
import { FormattedMessage } from 'react-intl';

import {
  buildTestMessageEvent,
  buildTestMessageEventWithCheer,
  buildTestModerationEvent,
  buildTestSubscriptionEvent,
  buildTestResubscriptionEvent,
  buildTestConnectionStatusMessage,
  buildTestHostingStatusMessage,
} from 'mtest/chat/helpers/chatObjects';

import ChatMessage, { ChatImage } from 'mweb/chat/components/chatMessage';
import { ACTION } from 'mweb/chat/events/messageEvent';
import { BAN } from 'mweb/chat/events/moderationEvent';
import {
  RECONNECT,
  DISCONNECTED,
  UNHOST,
  HostingEvent,
} from 'mweb/chat/events/statusEvent';
import { ChatEvent } from 'mweb/chat/events/baseChatEvent';
import { EmoteData, LinkData } from 'mweb/chat/events/utils/createMessageData';

describe('<ChatMessage/>', () => {
  function subject(message: ChatEvent): ShallowWrapper<{}, {}> {
    return shallow(<ChatMessage message={message} />);
  }

  describe('StatusEvent', () => {
    it('renders a CONNECTED type message', () => {
      const event = buildTestConnectionStatusMessage();
      const message = subject(event);
      expect(message.find('li').hasClass('chat-status')).toEqual(true);
      expect(message.find(FormattedMessage).prop('id')).toEqual(
        'chat--welcome-message',
      );
    });

    it('renders a RECONNECT type message', () => {
      const event = buildTestConnectionStatusMessage(RECONNECT);
      const message = subject(event);
      expect(message.find('li').hasClass('chat-status')).toEqual(true);
      expect(message.find(FormattedMessage).prop('id')).toEqual(
        'chat--reconnect',
      );
    });

    it('renders a DISCONNECTED type message', () => {
      const event = buildTestConnectionStatusMessage(DISCONNECTED);
      const message = subject(event);
      expect(message.find('li').hasClass('chat-status')).toEqual(true);
      expect(message.find(FormattedMessage).prop('id')).toEqual(
        'chat--disconnect',
      );
    });

    it('renders a DISCONNECTED with reason type message', () => {
      const event = buildTestConnectionStatusMessage(DISCONNECTED, true);
      const message = subject(event);
      expect(message.find('li').hasClass('chat-status')).toEqual(true);
      expect(message.find(FormattedMessage).prop('id')).toEqual(
        'chat--disconnect-with-reason',
      );
    });

    it('renders a HOSTING with reason type message', () => {
      const event = buildTestHostingStatusMessage();
      const message = subject(event);
      expect(message.find('li').hasClass('chat-status')).toEqual(true);
      expect(message.find(FormattedMessage).prop('id')).toEqual(
        'chat--hosting',
      );
      expect(
        (message.find(FormattedMessage).prop('values') as any).channel,
      ).toEqual((event as HostingEvent).hostedChannel);
    });

    it('renders a UNHOST with reason type message', () => {
      const event = buildTestHostingStatusMessage(UNHOST);
      const message = subject(event);
      expect(message.find('li').hasClass('chat-status')).toEqual(true);
      expect(message.find(FormattedMessage).prop('id')).toEqual('chat--unhost');
    });
  });

  describe('MessageEvent', () => {
    it('renders a POST type message', () => {
      const event = buildTestMessageEvent();
      const message = subject(event);
      expect(message.children().length).toEqual(6);

      expect(message.find('li').hasClass('chat-message')).toEqual(true);
      expect(message.find('li').prop('style')).toNotExist();

      const badge = message.childAt(0);
      expect(badge.type()).toEqual(ChatImage);
      expect(badge.hasClass('chat-message__badge')).toEqual(true);
      expect(badge.prop('srcKey')).toEqual('1x');
      expect(badge.prop('srcSet')).toEqual(event.badges[0].images);
      expect(badge.prop('alt')).toEqual(event.badges[0].alt);

      const username = message.childAt(1);
      expect(username.hasClass('chat-message__username')).toEqual(true);
      expect(username.prop('style')).toEqual({ color: event.user.color });

      const separator = message.childAt(2);
      expect(separator.key()).toEqual('separator');
      expect(separator.text()).toEqual(' : ');

      const emote = message.childAt(3);
      const emoteData = event.messageParts![0].content as EmoteData;
      expect(emote.type()).toEqual(ChatImage);
      expect(emote.hasClass('chat-message__emote')).toEqual(true);
      expect(emote.prop('srcKey')).toEqual('1x');
      expect(emote.prop('srcSet')).toEqual(emoteData.images);
      expect(emote.prop('alt')).toEqual(emoteData.alt);

      const text = message.childAt(4);
      expect(text.text()).toEqual(event.messageParts![1].content);

      const link = message.childAt(5);
      expect(link.text()).toEqual(
        (event.messageParts![2].content as LinkData).displayText,
      );
      expect(link.type()).toEqual('a');
    });

    it('renders a deleted POST type message', () => {
      const event = buildTestMessageEvent(undefined, undefined, true);
      const message = subject(event);
      expect(message.children().length).toEqual(4);

      expect(message.find('li').hasClass('chat-message')).toEqual(true);
      expect(message.find('li').prop('style')).toNotExist();

      const badge = message.childAt(0);
      expect(badge.type()).toEqual(ChatImage);
      expect(badge.hasClass('chat-message__badge')).toEqual(true);
      expect(badge.prop('srcKey')).toEqual('1x');
      expect(badge.prop('srcSet')).toEqual(event.badges[0].images);
      expect(badge.prop('alt')).toEqual(event.badges[0].alt);

      const username = message.childAt(1);
      expect(username.hasClass('chat-message__username')).toEqual(true);
      expect(username.prop('style')).toEqual({ color: event.user.color });

      const separator = message.childAt(2);
      expect(separator.text()).toEqual(' : ');

      const text = message.childAt(3);
      expect(text.hasClass('chat-message--deleted')).toEqual(true);
      expect(text.childAt(0).text()).toEqual('<');
      expect(text.childAt(1).type()).toEqual(FormattedMessage);
      expect(text.childAt(1).prop('id')).toEqual(
        'chat-moderation--message-deleted',
      );
      expect(text.childAt(2).text()).toEqual('>');
    });

    it('renders an ACTION type message', () => {
      const event = buildTestMessageEvent(undefined, ACTION);
      const message = subject(event);
      expect(message.children().length).toEqual(6);

      expect(message.find('li').hasClass('chat-message')).toEqual(true);
      expect(message.find('li').prop('style')).toEqual({
        color: event.user.color,
      });

      const badge = message.childAt(0);
      expect(badge.type()).toEqual(ChatImage);
      expect(badge.hasClass('chat-message__badge')).toEqual(true);
      expect(badge.prop('srcKey')).toEqual('1x');
      expect(badge.prop('srcSet')).toEqual(event.badges[0].images);
      expect(badge.prop('alt')).toEqual(event.badges[0].alt);

      const username = message.childAt(1);
      expect(username.hasClass('chat-message__username')).toEqual(true);
      expect(username.prop('style')).toEqual({ color: event.user.color });

      const separator = message.childAt(2);
      expect(separator.key()).toEqual('separator');
      expect(separator.text()).toEqual(' ');

      const emoteData = event.messageParts![0].content as EmoteData;
      const emote = message.childAt(3);
      expect(emote.type()).toEqual(ChatImage);
      expect(emote.hasClass('chat-message__emote')).toEqual(true);
      expect(emote.prop('srcKey')).toEqual('1x');
      expect(emote.prop('srcSet')).toEqual(emoteData.images);
      expect(emote.prop('alt')).toEqual(emoteData.alt);

      const text = message.childAt(4);
      expect(text.text()).toEqual(event.messageParts![1].content);

      const link = message.childAt(5);
      expect(link.text()).toEqual(
        (event.messageParts![2].content as LinkData).displayText,
      );
      expect(link.type()).toEqual('a');
    });

    it('renders a deleted ACTION type message', () => {
      const event = buildTestMessageEvent(undefined, ACTION, true);
      const message = subject(event);
      expect(message.children().length).toEqual(4);

      expect(message.find('li').hasClass('chat-message')).toEqual(true);
      expect(message.find('li').prop('style')).toEqual({
        color: event.user.color,
      });

      const badge = message.childAt(0);
      expect(badge.type()).toEqual(ChatImage);
      expect(badge.hasClass('chat-message__badge')).toEqual(true);
      expect(badge.prop('srcKey')).toEqual('1x');
      expect(badge.prop('srcSet')).toEqual(event.badges[0].images);
      expect(badge.prop('alt')).toEqual(event.badges[0].alt);

      const username = message.childAt(1);
      expect(username.hasClass('chat-message__username')).toEqual(true);
      expect(username.prop('style')).toEqual({ color: event.user.color });

      const separator = message.childAt(2);
      expect(separator.key()).toEqual('separator');
      expect(separator.text()).toEqual(' ');

      const text = message.childAt(3);
      expect(text.hasClass('chat-message--deleted')).toEqual(true);
      expect(text.childAt(0).text()).toEqual('<');
      expect(text.childAt(1).type()).toEqual(FormattedMessage);
      expect(text.childAt(1).prop('id')).toEqual(
        'chat-moderation--message-deleted',
      );
      expect(text.childAt(2).text()).toEqual('>');
    });

    it('renders a POST type message with cheer', () => {
      const event = buildTestMessageEventWithCheer();
      const message = subject(event);
      expect(message.children().length).toEqual(4);

      expect(message.find('li').hasClass('chat-message')).toEqual(true);
      expect(message.find('li').prop('style')).toNotExist();

      const username = message.childAt(0);
      expect(username.hasClass('chat-message__username')).toEqual(true);
      expect(username.prop('style')).toEqual({ color: event.user.color });

      const separator = message.childAt(1);
      expect(separator.key()).toEqual('separator');
      expect(separator.text()).toEqual(' : ');

      const cheerData = event.messageParts![0].content as EmoteData;
      const cheermote = message.childAt(2);
      expect(cheermote.type()).toEqual(ChatImage);
      expect(cheermote.hasClass('chat-message__emote')).toEqual(true);
      expect(cheermote.prop('srcKey')).toEqual('1x');
      expect(cheermote.prop('srcSet')).toEqual(cheerData.images);
      expect(cheermote.prop('alt')).toEqual(cheerData.alt);

      const cheerText = message.childAt(3);
      expect(cheerText.hasClass('chat-message__cheer-amount')).toEqual(true);
      expect(cheerText.prop('style')).toEqual({ color: cheerData.cheerColor });
      expect(cheerText.text()).toEqual(cheerData.cheerAmount);
    });
  });

  describe('ModerationEvent', () => {
    it('renders a TIMEOUT with reason moderation', () => {
      const event = buildTestModerationEvent();
      const message = subject(event);
      expect(message.find('li').hasClass('chat-moderation')).toEqual(true);

      const moderation = message.children();
      expect(moderation.type()).toEqual(FormattedMessage);
      expect(moderation.prop('id')).toEqual(
        'chat-moderation--timeout-with-reason',
      );
    });

    it('renders a TIMEOUT without reason moderation', () => {
      const event = buildTestModerationEvent(undefined, undefined, null);
      const message = subject(event);
      expect(message.find('li').hasClass('chat-moderation')).toEqual(true);

      const moderation = message.children();
      expect(moderation.type()).toEqual(FormattedMessage);
      expect(moderation.prop('id')).toEqual('chat-moderation--timeout');
    });

    it('renders a BAN with reason moderation', () => {
      const event = buildTestModerationEvent(undefined, BAN);
      const message = subject(event);
      expect(message.find('li').hasClass('chat-moderation')).toEqual(true);

      const moderation = message.children();
      expect(moderation.type()).toEqual(FormattedMessage);
      expect(moderation.prop('id')).toEqual('chat-moderation--ban-with-reason');
    });

    it('renders a BAN without reason moderation', () => {
      const event = buildTestModerationEvent(undefined, BAN, null);
      const message = subject(event);
      expect(message.find('li').hasClass('chat-moderation')).toEqual(true);

      const moderation = message.children();
      expect(moderation.type()).toEqual(FormattedMessage);
      expect(moderation.prop('id')).toEqual('chat-moderation--ban');
    });
  });

  describe('SubscribeEvent', () => {
    it('renders a SUBSCRIPTION without prime event', () => {
      const event = buildTestSubscriptionEvent();
      const message = subject(event);
      expect(message.children().length).toEqual(1);

      expect(message.find('li').hasClass('chat-subscribe')).toEqual(true);

      const subText = message.childAt(0);
      expect(subText.type()).toEqual(FormattedMessage);
      expect(subText.prop('id')).toEqual('chat--subscribed');
    });

    it('renders a SUBSCRIPTION with prime event', () => {
      const event = buildTestSubscriptionEvent(true);
      const message = subject(event);
      expect(message.children().length).toEqual(1);

      expect(message.find('li').hasClass('chat-subscribe')).toEqual(true);

      const subText = message.childAt(0);
      expect(subText.type()).toEqual(FormattedMessage);
      expect(subText.prop('id')).toEqual('chat--subscribed-with');

      const primeText = shallow(subText.prop('values').service);
      expect(primeText.type()).toEqual('a');
      expect(primeText.hasClass('chat-subscribe__prime')).toEqual(true);
      expect(primeText.text()).toEqual('Twitch Prime');
    });

    it('renders a RESUBSCRIPTION without prime and without message event', () => {
      const event = buildTestResubscriptionEvent();
      const message = subject(event);
      expect(message.children().length).toEqual(1);

      expect(message.find('li').hasClass('chat-subscribe')).toEqual(true);

      const resubText = message.childAt(0);
      expect(resubText.type()).toEqual(FormattedMessage);
      expect(resubText.prop('id')).toEqual('chat--resubbed');
    });

    it('renders a RESUBSCRIPTION with prime and without message event', () => {
      const event = buildTestResubscriptionEvent(true);
      const message = subject(event);
      expect(message.children().length).toEqual(3);

      expect(message.find('li').hasClass('chat-subscribe')).toEqual(true);

      const primeText = message.childAt(0);
      expect(primeText.type()).toEqual(FormattedMessage);
      expect(primeText.prop('id')).toEqual('chat--subscribed-with');

      const separator = message.childAt(1);
      expect(separator.key()).toEqual('sub-separator');
      expect(separator.text()).toEqual(' ');

      const resubText = message.childAt(2);
      expect(resubText.type()).toEqual(FormattedMessage);
      expect(resubText.prop('id')).toEqual('chat--resubbed');
    });

    it('renders a RESUBSCRIPTION without prime and with message event', () => {
      const event = buildTestResubscriptionEvent(false, true);
      const message = subject(event);
      expect(message.children().length).toEqual(2);

      expect(message.find('li').hasClass('chat-subscribe')).toEqual(true);

      const subText = message.childAt(0);
      expect(subText.type()).toEqual(FormattedMessage);
      expect(subText.prop('id')).toEqual('chat--resubbed');

      const subMessage = message.childAt(1);
      expect(subMessage.children().length).toEqual(3);
      expect(subMessage.hasClass('chat-subscribe__message')).toEqual(true);

      const subMessageUsername = subMessage.childAt(0);
      expect(subMessageUsername.hasClass('chat-message__username')).toEqual(
        true,
      );
      expect(subMessageUsername.text()).toEqual(event.user!.usernameDisplay);

      expect(subMessage.childAt(1).text()).toEqual(' : ');

      const subMessageText = subMessage.childAt(2);
      expect(subMessageText.text()).toEqual(event.messageParts![0].content);
    });

    it('renders a RESUBSCRIPTION with prime and with message event', () => {
      const event = buildTestResubscriptionEvent(true, true);
      const message = subject(event);
      expect(message.children().length).toEqual(4);

      expect(message.find('li').hasClass('chat-subscribe')).toEqual(true);

      const primeText = message.childAt(0);
      expect(primeText.type()).toEqual(FormattedMessage);
      expect(primeText.prop('id')).toEqual('chat--subscribed-with');

      const separator = message.childAt(1);
      expect(separator.key()).toEqual('sub-separator');
      expect(separator.text()).toEqual(' ');

      const subText = message.childAt(2);
      expect(subText.type()).toEqual(FormattedMessage);
      expect(subText.prop('id')).toEqual('chat--resubbed');

      const subMessage = message.childAt(3);
      expect(subMessage.children().length).toEqual(3);
      expect(subMessage.hasClass('chat-subscribe__message')).toEqual(true);

      const subMessageUsername = subMessage.childAt(0);
      expect(subMessageUsername.hasClass('chat-message__username')).toEqual(
        true,
      );
      expect(subMessageUsername.text()).toEqual(event.user!.usernameDisplay);

      expect(subMessage.childAt(1).text()).toEqual(' : ');

      const subMessageText = subMessage.childAt(2);
      expect(subMessageText.text()).toEqual(event.messageParts![0].content);
    });

    it('renders a RESUBSCRIPTION with deleted message event', () => {
      const event = buildTestResubscriptionEvent(undefined, true, true);
      const message = subject(event);
      expect(message.children().length).toEqual(2);

      expect(message.find('li').hasClass('chat-subscribe')).toEqual(true);

      const subText = message.childAt(0);
      expect(subText.type()).toEqual(FormattedMessage);
      expect(subText.prop('id')).toEqual('chat--resubbed');

      const subMessage = message.childAt(1);
      expect(subMessage.children().length).toEqual(3);
      expect(subMessage.hasClass('chat-subscribe__message')).toEqual(true);

      const subMessageUsername = subMessage.childAt(0);
      expect(subMessageUsername.hasClass('chat-message__username')).toEqual(
        true,
      );
      expect(subMessageUsername.text()).toEqual(event.user!.usernameDisplay);

      expect(subMessage.childAt(1).text()).toEqual(' : ');

      const subMessageText = subMessage.childAt(2);
      expect(subMessageText.hasClass('chat-message--deleted')).toEqual(true);
      expect(subMessageText.childAt(0).text()).toEqual('<');
      expect(subMessageText.childAt(1).type()).toEqual(FormattedMessage);
      expect(subMessageText.childAt(1).prop('id')).toEqual(
        'chat-moderation--message-deleted',
      );
      expect(subMessageText.childAt(2).text()).toEqual('>');
    });
  });
});
