/* global log, process */
const EventEmitter = require('events');
const LokiAppDotNetAPI = require('./loki_app_dot_net_api');
const OpenGroupUtils = require('../../ts/opengroup/utils/OpenGroupUtils');

class LokiPublicChatFactoryAPI extends EventEmitter {
  constructor(ourKey) {
    super();
    this.ourKey = ourKey;
    this.servers = [];
    this.allMembers = [];
    this.openGroupPubKeys = {};
    // Multidevice states
  }

  // MessageReceiver.connect calls this
  // start polling in all existing registered channels
  async open() {
    await Promise.all(this.servers.map(server => server.open()));
  }

  // MessageReceiver.close
  async close() {
    await Promise.all(this.servers.map(server => server.close()));
  }

  // server getter/factory
  async findOrCreateServer(serverUrl) {
    let thisServer = this.servers.find(server => server.baseServerUrl === serverUrl);
    if (!thisServer) {
      log.info(`loki_public_chat::findOrCreateServer - creating ${serverUrl}`);

      const serverIsValid = await OpenGroupUtils.validOpenGroupServer(serverUrl);
      if (!serverIsValid) {
        // FIXME: add toast?
        log.error(`loki_public_chat::findOrCreateServer - error: ${serverUrl} is not valid`);
        return null;
      }

      // after verification then we can start up all the pollers
      if (process.env.USE_STUBBED_NETWORK) {
        // eslint-disable-next-line global-require
        const StubAppDotNetAPI = require('../.././ts/test/session/integration/stubs/stub_app_dot_net_api');
        thisServer = new StubAppDotNetAPI(this.ourKey, serverUrl);
      } else {
        thisServer = new LokiAppDotNetAPI(this.ourKey, serverUrl);
        if (this.openGroupPubKeys[serverUrl]) {
          thisServer.getPubKeyForUrl();
          if (!thisServer.pubKeyHex) {
            log.warn(`loki_public_chat::findOrCreateServer - failed to set public key`);
          }
        }
      }

      const gotToken = await thisServer.getOrRefreshServerToken();
      if (!gotToken) {
        log.warn(`loki_public_chat::findOrCreateServer - Invalid server ${serverUrl}`);
        return null;
      }

      this.servers.push(thisServer);
    }
    return thisServer;
  }

  // channel getter/factory
  async findOrCreateChannel(serverUrl, channelId, conversationId) {
    const server = await this.findOrCreateServer(serverUrl);
    if (!server) {
      log.error(`Failed to create server for: ${serverUrl}`);
      return null;
    }
    return server.findOrCreateChannel(this, channelId, conversationId);
  }

  // deallocate resources server uses
  unregisterChannel(serverUrl, channelId) {
    const i = this.servers.findIndex(server => server.baseServerUrl === serverUrl);
    if (i === -1) {
      log.warn(`Tried to unregister from nonexistent server ${serverUrl}`);
      return;
    }
    const thisServer = this.servers[i];
    if (!thisServer) {
      log.warn(`Tried to unregister from nonexistent server ${i}`);
      return;
    }
    thisServer.unregisterChannel(channelId);
    this.servers.splice(i, 1);
  }

  // shouldn't this be scoped per conversation?
  async getListOfMembers() {
    // enable in the next release
    /*
    let members = [];
    await Promise.all(this.servers.map(async server => {
      await Promise.all(server.channels.map(async channel => {
        const newMembers = await channel.getSubscribers();
        members = [...members, ...newMembers];
      }));
    }));
    const results = members.map(member => {
      return { authorPhoneNumber: member.username };
    });
    */
    return this.allMembers;
  }

  // TODO: make this private (or remove altogether) when
  // we switch to polling the server for group members
  setListOfMembers(members) {
    this.allMembers = members;
  }

  async setProfileName(profileName) {
    await Promise.all(
      this.servers.map(async server => {
        await server.setProfileName(profileName);
      })
    );
  }

  async setHomeServer(homeServer) {
    await Promise.all(
      this.servers.map(async server => {
        // this may fail
        // but we can't create a sql table to remember to retry forever
        // I think we just silently fail for now
        await server.setHomeServer(homeServer);
      })
    );
  }

  async setAvatar(url, profileKey) {
    await Promise.all(
      this.servers.map(async server => {
        // this may fail
        // but we can't create a sql table to remember to retry forever
        // I think we just silently fail for now
        await server.setAvatar(url, profileKey);
      })
    );
  }
}

// These files are expected to be in commonjs so we can't use es6 syntax :(
// If we move these to TS then we should be able to use es6
module.exports = LokiPublicChatFactoryAPI;
