// @flow
import { observable, action } from "mobx";
import { USER_PRESENCE_INTERVAL } from "shared/constants";

type DocumentPresence = Map<string, { isEditing: boolean, userId: string }>;

export default class PresenceStore {
  @observable data: Map<string, DocumentPresence> = new Map();
  timeouts: Map<string, TimeoutID> = new Map();

  // called to setup when we get the initial state from document.presence
  // websocket message. overrides any existing state
  @action
  init(documentId: string, userIds: string[], editingIds: string[]) {
    this.data.set(documentId, new Map());
    userIds.forEach((userId) =>
      this.touch(documentId, userId, editingIds.includes(userId))
    );
  }

  // called when a user leave the room – user.leave websocket message.
  @action
  leave(documentId: string, userId: string) {
    const existing = this.data.get(documentId);
    if (existing) {
      existing.delete(userId);
    }
  }

  @action
  update(documentId: string, userId: string, isEditing: boolean) {
    const existing = this.data.get(documentId) || new Map();
    existing.set(userId, { isEditing, userId });
    this.data.set(documentId, existing);
  }

  // called when a user presence message is received – user.presence websocket
  // message.
  // While in edit mode a message is sent every USER_PRESENCE_INTERVAL, if
  // the other clients don't receive within USER_PRESENCE_INTERVAL*2 then a
  // timeout is triggered causing the users presence to default back to not
  // editing state as a safety measure.
  touch(documentId: string, userId: string, isEditing: boolean) {
    const id = `${documentId}-${userId}`;
    let timeout = this.timeouts.get(id);
    if (timeout) {
      clearTimeout(timeout);
      this.timeouts.delete(id);
    }

    this.update(documentId, userId, isEditing);

    if (isEditing) {
      timeout = setTimeout(() => {
        this.update(documentId, userId, false);
      }, USER_PRESENCE_INTERVAL * 2);
      this.timeouts.set(id, timeout);
    }
  }

  get(documentId: string): ?DocumentPresence {
    return this.data.get(documentId);
  }

  @action
  clear() {
    this.data.clear();
  }
}
