import Service, { inject as service } from "@ember/service";
import { bind } from "discourse-common/utils/decorators";
import { CHANNEL_STATUSES } from "discourse/plugins/chat/discourse/models/chat-channel";

export default class ChatSubscriptionsManager extends Service {
  @service store;
  @service chatChannelsManager;
  @service currentUser;
  @service appEvents;

  _channelSubscriptions = new Set();

  startChannelSubscription(channel) {
    if (
      channel.currentUserMembership.muted ||
      this._channelSubscriptions.has(channel.id)
    ) {
      return;
    }

    this._channelSubscriptions.add(channel.id);

    if (!channel.isDirectMessageChannel) {
      this._startChannelMentionsSubscription(channel);
    }

    this._startChannelNewMessagesSubscription(channel);
  }

  stopChannelSubscription(channel) {
    this.messageBus.unsubscribe(
      `/chat/${channel.id}/new-messages`,
      this._onNewMessages
    );
    if (!channel.isDirectMessageChannel) {
      this.messageBus.unsubscribe(
        `/chat/${channel.id}/new-mentions`,
        this._onNewMentions
      );
    }

    this._channelSubscriptions.delete(channel.id);
  }

  startChannelsSubscriptions(messageBusIds) {
    this._startNewChannelSubscription(messageBusIds.new_channel);
    this._startChannelArchiveStatusSubscription(messageBusIds.archive_status);
    this._startUserTrackingStateSubscription(messageBusIds.user_tracking_state);
    this._startChannelsEditsSubscription(messageBusIds.channel_edits);
    this._startChannelsStatusChangesSubscription(messageBusIds.channel_status);
    this._startChannelsMetadataChangesSubscription(
      messageBusIds.channel_metadata
    );
  }

  stopChannelsSubscriptions() {
    this._stopNewChannelSubscription();
    this._stopChannelArchiveStatusSubscription();
    this._stopUserTrackingStateSubscription();
    this._stopChannelsEditsSubscription();
    this._stopChannelsStatusChangesSubscription();
    this._stopChannelsMetadataChangesSubscription();

    (this.chatChannelsManager.channels || []).forEach((channel) => {
      this.stopChannelSubscription(channel);
    });
  }

  _startChannelArchiveStatusSubscription(lastId) {
    if (this.currentUser.admin) {
      this.messageBus.subscribe(
        "/chat/channel-archive-status",
        this._onChannelArchiveStatusUpdate,
        lastId
      );
    }
  }

  _stopChannelArchiveStatusSubscription() {
    if (this.currentUser.admin) {
      this.messageBus.unsubscribe(
        "/chat/channel-archive-status",
        this._onChannelArchiveStatusUpdate
      );
    }
  }

  _startChannelMentionsSubscription(channel) {
    this.messageBus.subscribe(
      `/chat/${channel.id}/new-mentions`,
      this._onNewMentions,
      channel.meta.message_bus_last_ids.new_mentions
    );
  }

  @bind
  _onChannelArchiveStatusUpdate(busData) {
    // we don't want to fetch a channel we don't have locally because archive status changed
    this.chatChannelsManager
      .find(busData.chat_channel_id, { fetchIfNotFound: false })
      .then((channel) => {
        if (!channel) {
          return;
        }

        channel.setProperties({
          archive_failed: busData.archive_failed,
          archive_completed: busData.archive_completed,
          archived_messages: busData.archived_messages,
          archive_topic_id: busData.archive_topic_id,
          total_messages: busData.total_messages,
        });
      });
  }

  @bind
  _onNewMentions(busData) {
    this.chatChannelsManager.find(busData.channel_id).then((channel) => {
      const membership = channel.currentUserMembership;
      if (busData.message_id > membership?.last_read_message_id) {
        membership.unread_mentions = (membership.unread_mentions || 0) + 1;
      }
    });
  }

  _startChannelNewMessagesSubscription(channel) {
    this.messageBus.subscribe(
      `/chat/${channel.id}/new-messages`,
      this._onNewMessages,
      channel.meta.message_bus_last_ids.new_messages
    );
  }

  @bind
  _onNewMessages(busData) {
    this.chatChannelsManager.find(busData.channel_id).then((channel) => {
      if (busData.user_id === this.currentUser.id) {
        // User sent message, update tracking state to no unread
        channel.currentUserMembership.last_read_message_id = busData.message_id;
      } else {
        // Ignored user sent message, update tracking state to no unread
        if (this.currentUser.ignored_users.includes(busData.username)) {
          channel.currentUserMembership.last_read_message_id =
            busData.message_id;
        } else {
          // Message from other user. Increment trackings state
          if (
            busData.message_id >
            (channel.currentUserMembership.last_read_message_id || 0)
          ) {
            channel.currentUserMembership.unread_count =
              channel.currentUserMembership.unread_count + 1;
          }
        }
      }

      channel.set("last_message_sent_at", new Date());
    });
  }

  _startUserTrackingStateSubscription(lastId) {
    if (!this.currentUser) {
      return;
    }

    this.messageBus.subscribe(
      `/chat/user-tracking-state/${this.currentUser.id}`,
      this._onUserTrackingStateUpdate,
      lastId
    );
  }

  _stopUserTrackingStateSubscription() {
    if (!this.currentUser) {
      return;
    }

    this.messageBus.unsubscribe(
      `/chat/user-tracking-state/${this.currentUser.id}`,
      this._onUserTrackingStateUpdate
    );
  }

  @bind
  _onUserTrackingStateUpdate(busData) {
    this.chatChannelsManager.find(busData.chat_channel_id).then((channel) => {
      if (
        channel?.currentUserMembership?.last_read_message_id <=
        busData.chat_message_id
      ) {
        channel.currentUserMembership.last_read_message_id =
          busData.chat_message_id;
        channel.currentUserMembership.unread_count = 0;
        channel.currentUserMembership.unread_mentions = 0;
      }
    });
  }

  _startNewChannelSubscription(lastId) {
    this.messageBus.subscribe(
      "/chat/new-channel",
      this._onNewChannelSubscription,
      lastId
    );
  }

  _stopNewChannelSubscription() {
    this.messageBus.unsubscribe(
      "/chat/new-channel",
      this._onNewChannelSubscription
    );
  }

  @bind
  _onNewChannelSubscription(data) {
    this.chatChannelsManager.find(data.channel.id).then((channel) => {
      // we need to refrehs here to have correct last message ids
      channel.meta = data.channel.meta;

      if (
        channel.isDirectMessageChannel &&
        !channel.currentUserMembership.following
      ) {
        channel.currentUserMembership.unread_count = 1;
      }

      this.chatChannelsManager.follow(channel);
    });
  }

  _startChannelsMetadataChangesSubscription(lastId) {
    this.messageBus.subscribe(
      "/chat/channel-metadata",
      this._onChannelMetadata,
      lastId
    );
  }

  _startChannelsEditsSubscription(lastId) {
    this.messageBus.subscribe(
      "/chat/channel-edits",
      this._onChannelEdits,
      lastId
    );
  }

  _startChannelsStatusChangesSubscription(lastId) {
    this.messageBus.subscribe(
      "/chat/channel-status",
      this._onChannelStatus,
      lastId
    );
  }

  _stopChannelsStatusChangesSubscription() {
    this.messageBus.unsubscribe("/chat/channel-status", this._onChannelStatus);
  }

  _stopChannelsEditsSubscription() {
    this.messageBus.unsubscribe("/chat/channel-edits", this._onChannelEdits);
  }

  _stopChannelsMetadataChangesSubscription() {
    this.messageBus.unsubscribe(
      "/chat/channel-metadata",
      this._onChannelMetadata
    );
  }

  @bind
  _onChannelMetadata(busData) {
    this.chatChannelsManager.find(busData.chat_channel_id).then((channel) => {
      if (channel) {
        channel.setProperties({
          memberships_count: busData.memberships_count,
        });
        this.appEvents.trigger("chat:refresh-channel-members");
      }
    });
  }

  @bind
  _onChannelEdits(busData) {
    this.chatChannelsManager.find(busData.chat_channel_id).then((channel) => {
      if (channel) {
        channel.setProperties({
          title: busData.name,
          description: busData.description,
          slug: busData.slug,
        });
      }
    });
  }

  @bind
  _onChannelStatus(busData) {
    this.chatChannelsManager.find(busData.chat_channel_id).then((channel) => {
      channel.set("status", busData.status);

      // it is not possible for the user to set their last read message id
      // if the channel has been archived, because all the messages have
      // been deleted. we don't want them seeing the blue dot anymore so
      // just completely reset the unreads
      if (busData.status === CHANNEL_STATUSES.archived) {
        channel.currentUserMembership.unread_count = 0;
        channel.currentUserMembership.unread_mentions = 0;
      }
    });
  }
}
