import Service, { inject as service } from "@ember/service";
import { ajax } from "discourse/lib/ajax";
import discourseDebounce from "discourse-common/lib/debounce";
import { bind } from "discourse-common/utils/decorators";
import { mentionRegex } from "pretty-text/mentions";
import { cancel } from "@ember/runloop";
import { tracked } from "@glimmer/tracking";

const MENTION_RESULT = {
  invalid: -1,
  unreachable: 0,
  over_members_limit: 1,
};

const MENTION_DEBOUNCE_MS = 1000;

export default class ChatComposerWarningsTracker extends Service {
  @service siteSettings;

  // Track mention hints to display warnings
  @tracked unreachableGroupMentions = [];
  @tracked overMembersLimitGroupMentions = [];
  @tracked tooManyMentions = false;
  @tracked mentionsCount = 0;
  @tracked mentionsTimer = null;

  // Complimentary structure to avoid repeating mention checks.
  _mentionWarningsSeen = {};

  willDestroy() {
    cancel(this.mentionsTimer);
  }

  @bind
  trackMentions(message) {
    this.mentionsTimer = discourseDebounce(
      this,
      this._trackMentions,
      message,
      MENTION_DEBOUNCE_MS
    );
  }

  @bind
  _trackMentions(message) {
    if (!this.siteSettings.enable_mentions) {
      return;
    }

    const mentions = this._extractMentions(message);
    this.mentionsCount = mentions?.length;

    if (this.mentionsCount > 0) {
      this.tooManyMentions =
        this.mentionsCount > this.siteSettings.max_mentions_per_chat_message;

      if (!this.tooManyMentions) {
        const newMentions = mentions.filter(
          (mention) => !(mention in this._mentionWarningsSeen)
        );

        if (newMentions?.length > 0) {
          this._recordNewWarnings(newMentions, mentions);
        } else {
          this._rebuildWarnings(mentions);
        }
      }
    } else {
      this.tooManyMentions = false;
      this.unreachableGroupMentions = [];
      this.overMembersLimitGroupMentions = [];
    }
  }

  _extractMentions(message) {
    const regex = mentionRegex(this.siteSettings.unicode_usernames);
    const mentions = [];
    let mentionsLeft = true;

    while (mentionsLeft) {
      const matches = message.match(regex);

      if (matches) {
        const mention = matches[1] || matches[2];
        mentions.push(mention);
        message = message.replaceAll(`${mention}`, "");

        if (mentions.length > this.siteSettings.max_mentions_per_chat_message) {
          mentionsLeft = false;
        }
      } else {
        mentionsLeft = false;
      }
    }

    return mentions;
  }

  _recordNewWarnings(newMentions, mentions) {
    ajax("/chat/api/mentions/groups.json", {
      data: { mentions: newMentions },
    })
      .then((newWarnings) => {
        newWarnings.unreachable.forEach((warning) => {
          this._mentionWarningsSeen[warning] = MENTION_RESULT["unreachable"];
        });

        newWarnings.over_members_limit.forEach((warning) => {
          this._mentionWarningsSeen[warning] =
            MENTION_RESULT["over_members_limit"];
        });

        newWarnings.invalid.forEach((warning) => {
          this._mentionWarningsSeen[warning] = MENTION_RESULT["invalid"];
        });

        this._rebuildWarnings(mentions);
      })
      .catch(this._rebuildWarnings(mentions));
  }

  _rebuildWarnings(mentions) {
    const newWarnings = mentions.reduce(
      (memo, mention) => {
        if (
          mention in this._mentionWarningsSeen &&
          !(this._mentionWarningsSeen[mention] === MENTION_RESULT["invalid"])
        ) {
          if (
            this._mentionWarningsSeen[mention] === MENTION_RESULT["unreachable"]
          ) {
            memo[0].push(mention);
          } else {
            memo[1].push(mention);
          }
        }

        return memo;
      },
      [[], []]
    );

    this.unreachableGroupMentions = newWarnings[0];
    this.overMembersLimitGroupMentions = newWarnings[1];
  }
}
