import { For, Match, Show, Switch, createSignal, onMount } from "solid-js";

import { useLingui } from "@lingui-solid/solid/macro";
import { ImageEmbed, Message as MessageInterface, WebsiteEmbed } from "stoat.js";
import { cva } from "styled-system/css";
import { styled } from "styled-system/jsx";
import { decodeTime } from "ulid";

import { useClient } from "@revolt/client";
import { useTime } from "@revolt/i18n";
import { Markdown } from "@revolt/markdown";
import { useState } from "@revolt/state";
import {
  Attachment,
  Avatar,
  Embed,
  MessageContainer,
  MessageReply,
  Reactions,
  SystemMessage,
  SystemMessageIcon,
  Tooltip,
  Username,
} from "@revolt/ui";
import { Symbol } from "@revolt/ui/components/utils/Symbol";

import { MessageContextMenu } from "../../../menus/MessageContextMenu";
import {
  floatingUserMenus,
  floatingUserMenusFromMessage,
} from "../../../menus/UserContextMenu";

import { EditMessage } from "./EditMessage";

/**
 * Regex for matching URLs
 */
const RE_URL =
  /[(http(s)?)://(www.)?a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/;

interface Props {
  /**
   * Message
   */
  message: MessageInterface;

  /**
   * Whether this is the tail of another message
   */
  tail?: boolean;

  /**
   * Whether to highlight this message
   */
  highlight?: boolean;

  /**
   * Whether to replace content with editor
   */
  editing?: boolean;

  /**
   * Whether this message is a link
   */
  isLink?: boolean;
}

/**
 * Render a Message with or without a tail
 */
export function Message(props: Props) {
  const dayjs = useTime();
  const state = useState();
  const { t } = useLingui();
  const client = useClient();

  const [isHovering, setIsHovering] = createSignal(false);

  /**
   * Determine whether this message only contains a GIF
   */
  const isOnlyGIF = () => {
    if (
      !props.message.embeds ||
      props.message.embeds.length !== 1 ||
      !props.message.content ||
      props.message.content.replace(RE_URL, "").length
    ) {
      return false;
    }

    const embed = props.message.embeds[0];

    if (embed.type === "Image") {
      return (embed as ImageEmbed).url?.startsWith("https://static.klipy.com");
    }

    if (embed.type === "Website") {
      const websiteEmbed = embed as WebsiteEmbed;
      const gifProviders = ["https://tenor.com", "https://klipy.com"];
      return (
        websiteEmbed.specialContent?.type === "GIF" ||
        gifProviders.some((provider) =>
          websiteEmbed.originalUrl?.startsWith(provider),
        )
      );
    }

    return false;
  };

  /**
   * React with an emoji
   * @param emoji Emoji
   */
  const react = (emoji: string) => props.message.react(emoji);

  /**
   * Remove emoji reaction
   * @param emoji Emoji
   */
  const unreact = (emoji: string) => props.message.unreact(emoji);

  return (
    <MessageContainer
      message={props.message}
      onHover={setIsHovering}
      username={
        <div use:floating={floatingUserMenusFromMessage(props.message)}>
          <Username
            username={
              props.message.masquerade?.name ??
              props.message.member?.nickname ??
              props.message.author?.displayName ??
              props.message.author?.username ??
              props.message.username
            }
            colour={props.message.roleColour!}
          />
        </div>
      }
      avatar={
        <div
          class={avatarContainer()}
          use:floating={floatingUserMenusFromMessage(props.message)}
        >
          <Avatar
            size={36}
            src={
              isHovering()
                ? props.message.animatedAvatarURL
                : props.message.avatarURL
            }
          />
        </div>
      }
      contextMenu={() => <MessageContextMenu message={props.message} />}
      timestamp={props.message.createdAt}
      edited={props.message.editedAt}
      mentioned={props.message.mentioned}
      highlight={props.highlight}
      editing={props.editing}
      isLink={props.isLink}
      tail={props.tail || state.settings.getValue("appearance:compact_mode")}
      header={
        <Show when={props.message.replyIds}>
          <For each={props.message.replyIds}>
            {(reply_id) => {
              /**
               * Signal the actual message
               */
              const message = () => client().messages.get(reply_id);

              onMount(() => {
                if (!message()) {
                  props.message.channel!.fetchMessage(reply_id);
                }
              });

              return (
                <MessageReply
                  mention={props.message.mentionIds?.includes(
                    message()!.authorId!,
                  )}
                  message={message()}
                />
              );
            }}
          </For>
        </Show>
      }
      info={
        <Switch fallback={<div />}>
          <Match
            when={
              props.message.masquerade &&
              props.message.authorId === "01FHGJ3NPP7XANQQH8C2BE44ZY"
            }
          >
            <Tooltip
              content={t`Message was sent on another platform`}
              placement="top"
            >
              <Symbol size={16}>link</Symbol>
            </Tooltip>
          </Match>
          <Match when={props.message.author?.privileged}>
            <Tooltip content={t`Official Communication`} placement="top">
              <Symbol size={16}>brightness_alert</Symbol>
            </Tooltip>
          </Match>
          <Match when={props.message.author?.bot}>
            <Tooltip content={t`Bot`} placement="top">
              <Symbol size={16} fill>
                smart_toy
              </Symbol>
            </Tooltip>
          </Match>
          <Match when={props.message.webhook}>
            <Tooltip content={t`Webhook`} placement="top">
              <Symbol size={16} fill>
                cloud
              </Symbol>
            </Tooltip>
          </Match>
          <Match when={props.message.isSuppressed}>
            <Tooltip content={t`Silent`} placement="top">
              <Symbol size={16} fill>
                notifications_off
              </Symbol>
            </Tooltip>
          </Match>
          <Match
            when={
              props.message.authorId &&
              dayjs().diff(decodeTime(props.message.authorId), "day") < 1
            }
          >
            <NewUser>
              <Tooltip content={t`New to Stoat`} placement="top">
                <Symbol size={16} fill>
                  spa
                </Symbol>
              </Tooltip>
            </NewUser>
          </Match>
          <Match
            when={
              props.message.member &&
              dayjs().diff(props.message.member.joinedAt, "day") < 1
            }
          >
            <NewUser>
              <Tooltip content={t`New to the server`} placement="top">
                <Symbol size={16}>spa</Symbol>
              </Tooltip>
            </NewUser>
          </Match>
          {/* <Match when={props.message.authorId === "01EX2NCWQ0CHS3QJF0FEQS1GR4"}>
            <span />
            <span>placeholder &middot; </span>
          </Match> */}
        </Switch>
      }
      compact={
        !!props.message.systemMessage ||
        state.settings.getValue("appearance:compact_mode")
      }
      infoMatch={
        <Match when={props.message.systemMessage}>
          <SystemMessageIcon
            systemMessage={props.message.systemMessage!}
            createdAt={props.message.createdAt}
            isServer={!!props.message.server}
          />
        </Match>
      }
    >
      <Show when={props.message.systemMessage}>
        <SystemMessage
          systemMessage={props.message.systemMessage!}
          menuGenerator={(user) =>
            user
              ? floatingUserMenus(
                  user!,
                  // TODO: try to fetch on demand member
                  props.message.server?.getMember(user!.id),
                )
              : {}
          }
          isServer={!!props.message.server}
        />
      </Show>
      <Switch>
        <Match when={props.editing}>
          <EditMessage message={props.message} />
        </Match>
        <Match when={props.message.content && !isOnlyGIF()}>
          <BreakText>
            <Markdown content={props.message.content!} />
          </BreakText>
        </Match>
      </Switch>
      <Show when={props.message.attachments}>
        <For each={props.message.attachments}>
          {(attachment) => (
            <Attachment message={props.message} file={attachment} />
          )}
        </For>
      </Show>
      <Show when={props.message.embeds}>
        <For each={props.message.embeds}>
          {(embed) => <Embed embed={embed} />}
        </For>
      </Show>
      <Reactions
        reactions={props.message.reactions as never as Map<string, Set<string>>}
        interactions={props.message.interactions}
        userId={client().user!.id}
        addReaction={react}
        removeReaction={unreact}
        sendGIF={(content) =>
          props.message?.channel?.sendMessage({
            content,
            replies: [{ id: props.message.id, mention: true }],
          })
        }
      />
    </MessageContainer>
  );
}

/**
 * New user indicator
 */
const NewUser = styled("div", {
  base: {
    fill: "var(--md-sys-color-primary)",
  },
});

/**
 * Avatar container
 */
const avatarContainer = cva({
  base: {
    height: "fit-content",
    borderRadius: "var(--borderRadius-circle)",
  },
});

/**
 * Break all text and prevent overflow from math blocks
 */
const BreakText = styled("div", {
  base: {
    wordBreak: "break-word",

    "& .math": {
      overflowX: "auto",
      overflowY: "hidden",
      maxHeight: "100vh",
    },
  },
});
