import * as React from 'react';
import { FormattedMessage } from 'react-intl';
import flatMap from 'lodash-es/flatMap';

import { Badge } from 'mweb/chat/badgerService';
import {
  MessagePart,
  MessageEventUserData,
  TEXT,
  LINK,
  EMOTE,
} from 'mweb/chat/events/utils/createMessageData';
import { ACTION, MessageEvent, POST } from 'mweb/chat/events/messageEvent';
import {
  BAN,
  TIMEOUT,
  ModerationEvent,
} from 'mweb/chat/events/moderationEvent';
import {
  SUBSCRIPTION,
  RESUBSCRIPTION,
  SubscriptionEvent,
  ResubscriptionEvent,
} from 'mweb/chat/events/subscribeEvent';
import {
  CONNECTED,
  DISCONNECTED,
  RECONNECT,
  HOSTING,
  UNHOST,
} from 'mweb/chat/events/statusEvent';
import { ChatEvent } from 'mweb/chat/events/baseChatEvent';

if (process.env.BROWSER) {
  require('./chatMessage.sass');
}

interface ChatImageProps {
  key: string | number;
  className: string;
  srcKey: string;
  srcSet: { [scale: string]: string };
  alt?: string;
}

export function ChatImage(props: ChatImageProps): JSX.Element {
  return (
    <img
      className={props.className}
      src={props.srcSet[props.srcKey]}
      srcSet={Object.keys(props.srcSet)
        .map(scale => `${props.srcSet[scale]} ${scale}`)
        .join(',')}
      alt={props.alt}
    />
  );
}

function badges(badgeList: Badge[]): JSX.Element[] {
  return badgeList.map((badge, idx) => (
    <ChatImage
      key={`badge-${idx}`}
      className="chat-message__badge"
      srcKey="1x"
      srcSet={badge.images}
      alt={badge.alt}
    />
  ));
}

export function username(userData: MessageEventUserData): JSX.Element[] {
  const usernameComponents = [
    <span
      key="username-display"
      className="chat-message__username"
      style={{ color: userData.color }}
    >
      {userData.usernameDisplay}
    </span>,
  ];

  if (userData.isIntl) {
    usernameComponents.push(
      <span
        key="username-base"
        style={{ color: userData.color }}
      >{` (${userData.username})`}</span>,
    );
  }

  return usernameComponents;
}

function deletedMessage(): JSX.Element {
  return (
    <span key="deleted" className="chat-message--deleted">
      {'<'}
      <FormattedMessage
        id="chat-moderation--message-deleted"
        defaultMessage="message deleted"
      />
      {'>'}
    </span>
  );
}

function messageParts(
  parts: MessagePart[],
  isDeleted: boolean | undefined,
): JSX.Element | JSX.Element[] {
  if (isDeleted) {
    return deletedMessage();
  }
  return flatMap(parts, (part, idx) => {
    switch (part.type) {
      case TEXT:
        return <span key={idx}>{part.content}</span>;
      case LINK:
        return (
          <a
            key={idx}
            className="chat-message__link"
            href={part.content.url}
            target="_blank"
            rel="noreferrer noopener"
          >
            {part.content.displayText}
          </a>
        );
      case EMOTE:
        if (part.content.cheerAmount) {
          return [
            <ChatImage
              key={idx}
              className="chat-message__emote"
              srcKey="1x"
              srcSet={part.content.images}
              alt={part.content.alt}
            />,
            <span
              key={`cheer-amount-${idx}`}
              className="chat-message__cheer-amount"
              style={{ color: part.content.cheerColor }}
            >
              {part.content.cheerAmount}
            </span>,
          ];
        }
        return (
          <ChatImage
            key={idx}
            className="chat-message__emote"
            srcKey="1x"
            srcSet={part.content.images}
            alt={part.content.alt}
          />
        );
    }
  });
}

function moderationMessage(message: ModerationEvent): JSX.Element {
  if (message.type === 'BAN') {
    if (message.reason) {
      return (
        <FormattedMessage
          id="chat-moderation--ban-with-reason"
          defaultMessage="{username} has been banned. Reason: {reason}"
          values={{
            username: message.username,
            reason: message.reason,
          }}
        />
      );
    }
    return (
      <FormattedMessage
        id="chat-moderation--ban"
        defaultMessage="{username} has been banned."
        values={{
          username: message.username,
        }}
      />
    );
  }
  if (message.reason) {
    return (
      <FormattedMessage
        id="chat-moderation--timeout-with-reason"
        defaultMessage="{username} has been timed out for {duration} seconds. Reason: {reason}"
        values={{
          username: message.username,
          duration: message.duration,
          reason: message.reason,
        }}
      />
    );
  }
  return (
    <FormattedMessage
      id="chat-moderation--timeout"
      defaultMessage="{username} has been timed out for {duration} seconds."
      values={{
        username: message.username,
        duration: message.duration,
      }}
    />
  );
}

function twitchPrimeLink(channel: string): JSX.Element {
  return (
    <a
      className="chat-subscribe__prime"
      href={`https://twitch.amazon.com/prime?ref=subscriptionMessage&channel=${channel}`}
      target="_blank"
      rel="noopener"
    >
      Twitch Prime
    </a>
  );
}

function subscriptionMessage(message: SubscriptionEvent): JSX.Element {
  if (message.isPrime) {
    return (
      <FormattedMessage
        id="chat--subscribed-with"
        key="sub-text"
        defaultMessage="{username} just subscribed with {service}!"
        values={{
          username: message.username,
          service: twitchPrimeLink(message.channel),
        }}
      />
    );
  }
  return (
    <FormattedMessage
      id="chat--subscribed"
      key="sub-text"
      defaultMessage="{username} just subscribed!"
      values={{ username: message.username }}
    />
  );
}

function resubscriptionMessage(message: ResubscriptionEvent): JSX.Element[] {
  let resubscriptionMessageComponents: JSX.Element[] = [];

  if (message.isPrime && message.user) {
    resubscriptionMessageComponents.push(
      <FormattedMessage
        id="chat--subscribed-with"
        key="sub-text-service"
        defaultMessage="{username} just subscribed with {service}!"
        values={{
          username: message.user.usernameDisplay,
          service: twitchPrimeLink(message.channel),
        }}
      />,
      <span key="sub-separator"> </span>,
    );
  }

  if (message.user && message.months) {
    resubscriptionMessageComponents.push(
      <FormattedMessage
        id="chat--resubbed"
        key="sub-text"
        defaultMessage="{username} subscribed for {months} months in a row!"
        values={{
          username: message.user.usernameDisplay,
          months: message.months,
        }}
      />,
    );
  }

  if (message.messageParts && message.badges && message.user) {
    resubscriptionMessageComponents.push(
      <div key="resub-message" className="chat-subscribe__message">
        {badges(message.badges)}
        {username(message.user)}
        <span key="separator">{' : '}</span>
        {messageParts(message.messageParts, message.deleted)}
      </div>,
    );
  }

  return resubscriptionMessageComponents;
}

interface ChatMessageProps {
  message: ChatEvent;
}

export default class ChatMessage extends React.Component<ChatMessageProps, {}> {
  shouldComponentUpdate(nextProps: ChatMessageProps): boolean {
    if (nextProps.message.id !== this.props.message.id) {
      return true;
    }
    return (
      (nextProps.message as MessageEvent).deleted !==
      (this.props.message as MessageEvent).deleted
    );
  }

  render(): JSX.Element | undefined {
    const message = this.props.message;
    switch (message.type) {
      case CONNECTED:
        return (
          <li className="chat-status">
            <FormattedMessage
              id="chat--welcome-message"
              defaultMessage="Welcome to the chat room!"
            />
          </li>
        );
      case DISCONNECTED:
        if (message.reason) {
          return (
            <li className="chat-status">
              <FormattedMessage
                id="chat--disconnect-with-reason"
                defaultMessage="You have been disconnected from chat for the following reason: {reason}"
                values={{
                  reason: message.reason,
                }}
              />
            </li>
          );
        }
        return (
          <li className="chat-status">
            <FormattedMessage
              id="chat--disconnect"
              defaultMessage="You have been disconnected from chat."
            />
          </li>
        );
      case RECONNECT:
        return (
          <li className="chat-status">
            <FormattedMessage
              id="chat--reconnect"
              defaultMessage="Sorry, we were unable to connect to chat. Attempting to reconnect..."
            />
          </li>
        );
      case HOSTING:
        return (
          <li className="chat-status">
            <FormattedMessage
              id="chat--hosting"
              defaultMessage="Now hosting {channel}."
              values={{ channel: message.hostedChannel }}
            />
          </li>
        );
      case UNHOST:
        return (
          <li className="chat-status">
            <FormattedMessage
              id="chat--unhost"
              defaultMessage="No longer hosting."
            />
          </li>
        );
      case POST:
      case ACTION:
        let style,
          separator = ' : ';
        if (message.type === ACTION) {
          style = { color: message.user.color };
          separator = ' ';
        }
        return (
          message.messageParts && (
            <li className="chat-message" style={style}>
              {badges(message.badges)}
              {username(message.user)}
              <span key="separator">{separator}</span>
              {messageParts(message.messageParts, message.deleted)}
            </li>
          )
        );
      case BAN:
      case TIMEOUT:
        return (
          <li className="chat-moderation">{moderationMessage(message)}</li>
        );
      case SUBSCRIPTION:
        return (
          <li className="chat-subscribe">{subscriptionMessage(message)}</li>
        );
      case RESUBSCRIPTION:
        return (
          <li className="chat-subscribe">{resubscriptionMessage(message)}</li>
        );
      default:
        return undefined;
    }
  }
}
