import Service, { inject as service } from "@ember/service";
import Promise from "rsvp";
import ChatChannel from "discourse/plugins/chat/discourse/models/chat-channel";
import { tracked } from "@glimmer/tracking";
import { TrackedObject } from "@ember-compat/tracked-built-ins";
import { popupAjaxError } from "discourse/lib/ajax-error";

const DIRECT_MESSAGE_CHANNELS_LIMIT = 20;

/*
  The ChatChannelsManager service is responsible for managing the loaded chat channels.
  It provides helpers to facilitate using and managing laoded channels instead of constantly
  fetching them from the server.
*/

export default class ChatChannelsManager extends Service {
  @service chatSubscriptionsManager;
  @service chatApi;
  @service currentUser;
  @tracked _cached = new TrackedObject();

  async find(id, options = { fetchIfNotFound: true }) {
    const existingChannel = this.#findStale(id);
    if (existingChannel) {
      return Promise.resolve(existingChannel);
    } else if (options.fetchIfNotFound) {
      return this.#find(id);
    } else {
      return Promise.resolve();
    }
  }

  get channels() {
    return Object.values(this._cached);
  }

  store(channelObject) {
    let model = this.#findStale(channelObject.id);

    if (!model) {
      model = ChatChannel.create(channelObject);
      this.#cache(model);
    }

    return model;
  }

  async follow(model) {
    this.chatSubscriptionsManager.startChannelSubscription(model);

    if (!model.currentUserMembership.following) {
      return this.chatApi.followChannel(model.id).then((membership) => {
        model.currentUserMembership.following = membership.following;
        model.currentUserMembership.muted = membership.muted;
        model.currentUserMembership.desktop_notification_level =
          membership.desktop_notification_level;
        model.currentUserMembership.mobile_notification_level =
          membership.mobile_notification_level;

        return model;
      });
    } else {
      return Promise.resolve(model);
    }
  }

  async unfollow(model) {
    this.chatSubscriptionsManager.stopChannelSubscription(model);

    return this.chatApi.unfollowChannel(model.id).then((membership) => {
      model.currentUserMembership = membership;

      return model;
    });
  }

  get unreadCount() {
    let count = 0;
    this.publicMessageChannels.forEach((channel) => {
      count += channel.currentUserMembership.unread_count || 0;
    });
    return count;
  }

  get unreadUrgentCount() {
    let count = 0;
    this.channels.forEach((channel) => {
      if (channel.isDirectMessageChannel) {
        count += channel.currentUserMembership.unread_count || 0;
      }
      count += channel.currentUserMembership.unread_mentions || 0;
    });
    return count;
  }

  get publicMessageChannels() {
    return this.channels
      .filter(
        (channel) =>
          channel.isCategoryChannel && channel.currentUserMembership.following
      )
      .sort((a, b) => a?.slug?.localeCompare?.(b?.slug));
  }

  get directMessageChannels() {
    return this.#sortDirectMessageChannels(
      this.channels.filter((channel) => {
        const membership = channel.currentUserMembership;
        return channel.isDirectMessageChannel && membership.following;
      })
    );
  }

  get truncatedDirectMessageChannels() {
    return this.directMessageChannels.slice(0, DIRECT_MESSAGE_CHANNELS_LIMIT);
  }

  async #find(id) {
    return this.chatApi
      .channel(id)
      .catch(popupAjaxError)
      .then((channel) => {
        this.#cache(channel);
        return channel;
      });
  }

  #cache(channel) {
    this._cached[channel.id] = channel;
  }

  #findStale(id) {
    return this._cached[id];
  }

  #sortDirectMessageChannels(channels) {
    return channels.sort((a, b) => {
      const unreadCountA = a.currentUserMembership.unread_count || 0;
      const unreadCountB = b.currentUserMembership.unread_count || 0;
      if (unreadCountA === unreadCountB) {
        return new Date(a.get("last_message_sent_at")) >
          new Date(b.get("last_message_sent_at"))
          ? -1
          : 1;
      } else {
        return unreadCountA > unreadCountB ? -1 : 1;
      }
    });
  }
}
