'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var chai_js = require('aegir/utils/chai.js');
var mocha = require('../utils/mocha.js');
var PeerId = require('peer-id');
var env_js = require('ipfs-utils/src/env.js');
var ipns = require('ipns');
var delay = require('delay');
var last = require('it-last');
var waitFor = require('../utils/wait-for.js');
var fromString = require('uint8arrays/from-string');
var toString = require('uint8arrays/to-string');

function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }

function _interopNamespace(e) {
  if (e && e.__esModule) return e;
  var n = Object.create(null);
  if (e) {
    Object.keys(e).forEach(function (k) {
      if (k !== 'default') {
        var d = Object.getOwnPropertyDescriptor(e, k);
        Object.defineProperty(n, k, d.get ? d : {
          enumerable: true,
          get: function () { return e[k]; }
        });
      }
    });
  }
  n["default"] = e;
  return Object.freeze(n);
}

var PeerId__default = /*#__PURE__*/_interopDefaultLegacy(PeerId);
var ipns__namespace = /*#__PURE__*/_interopNamespace(ipns);
var delay__default = /*#__PURE__*/_interopDefaultLegacy(delay);
var last__default = /*#__PURE__*/_interopDefaultLegacy(last);

const namespace = '/record/';
const ipfsRef = '/ipfs/QmPFVLPmp9zv5Z5KUqLhe2EivAGccQW2r7M7jhVJGLZoZU';
const daemonsOptions = { ipfsOptions: { EXPERIMENTAL: { ipnsPubsub: true } } };
function testPubsub(factory, options) {
  const describe = mocha.getDescribe(options);
  const it = mocha.getIt(options);
  describe('.name.pubsub', () => {
    if (!env_js.isNode)
      return;
    let nodes;
    let nodeA;
    let nodeB;
    let idA;
    let idB;
    before(async function () {
      this.timeout(120 * 1000);
      nodes = await Promise.all([
        factory.spawn({ ...daemonsOptions }),
        factory.spawn({ ...daemonsOptions })
      ]);
      nodeA = nodes[0].api;
      nodeB = nodes[1].api;
      const ids = await Promise.all([
        nodeA.id(),
        nodeB.id()
      ]);
      idA = ids[0];
      idB = ids[1];
      await nodeA.swarm.connect(idB.addresses[0]);
    });
    after(() => factory.clean());
    it('should publish and then resolve correctly', async function () {
      this.timeout(80 * 1000);
      let subscribed = false;
      function checkMessage() {
        subscribed = true;
      }
      const alreadySubscribed = () => {
        return subscribed === true;
      };
      const keys = ipns__namespace.getIdKeys(fromString.fromString(idA.id, 'base58btc'));
      const topic = `${ namespace }${ toString.toString(keys.routingKey.uint8Array(), 'base64url') }`;
      await chai_js.expect(last__default["default"](nodeB.name.resolve(idA.id))).to.eventually.be.rejected().with.property('message').that.matches(/not found/);
      await waitFor(async () => {
        const res = await nodeA.pubsub.peers(topic);
        return Boolean(res && res.length);
      }, { name: `node A to subscribe to ${ topic }` });
      await nodeB.pubsub.subscribe(topic, checkMessage);
      await nodeA.name.publish(ipfsRef, { resolve: false });
      await waitFor(alreadySubscribed);
      await delay__default["default"](1000);
      const res = await last__default["default"](nodeB.name.resolve(idA.id));
      chai_js.expect(res).to.equal(ipfsRef);
    });
    it('should self resolve, publish and then resolve correctly', async function () {
      this.timeout(6000);
      const emptyDirCid = '/ipfs/QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn';
      const {path} = await nodeA.add(fromString.fromString('pubsub records'));
      const resolvesEmpty = await last__default["default"](nodeB.name.resolve(idB.id));
      chai_js.expect(resolvesEmpty).to.be.eq(emptyDirCid);
      await chai_js.expect(last__default["default"](nodeA.name.resolve(idB.id))).to.eventually.be.rejected().with.property('message').that.matches(/not found/);
      const publish = await nodeB.name.publish(path);
      chai_js.expect(publish).to.be.eql({
        name: idB.id,
        value: `/ipfs/${ path }`
      });
      const resolveB = await last__default["default"](nodeB.name.resolve(idB.id));
      chai_js.expect(resolveB).to.be.eq(`/ipfs/${ path }`);
      await delay__default["default"](1000);
      const resolveA = await last__default["default"](nodeA.name.resolve(idB.id));
      chai_js.expect(resolveA).to.be.eq(`/ipfs/${ path }`);
    });
    it('should handle event on publish correctly', async function () {
      this.timeout(80 * 1000);
      const testAccountName = 'test-account';
      let publishedMessage;
      function checkMessage(msg) {
        publishedMessage = msg;
      }
      const alreadySubscribed = () => {
        return Boolean(publishedMessage);
      };
      const testAccount = await nodeA.key.gen(testAccountName, {
        type: 'rsa',
        size: 2048,
        'ipns-base': 'b58mh'
      });
      const keys = ipns__namespace.getIdKeys(fromString.fromString(testAccount.id, 'base58btc'));
      const topic = `${ namespace }${ toString.toString(keys.routingKey.uint8Array(), 'base64url') }`;
      await nodeB.pubsub.subscribe(topic, checkMessage);
      await nodeA.name.publish(ipfsRef, {
        resolve: false,
        key: testAccountName
      });
      await waitFor(alreadySubscribed);
      if (!publishedMessage) {
        throw new Error('Pubsub handler not invoked');
      }
      const publishedMessageData = ipns__namespace.unmarshal(publishedMessage.data);
      if (!publishedMessageData.pubKey) {
        throw new Error('No public key found in message data');
      }
      const messageKey = await PeerId__default["default"].createFromB58String(publishedMessage.from);
      const pubKeyPeerId = await PeerId__default["default"].createFromPubKey(publishedMessageData.pubKey);
      chai_js.expect(pubKeyPeerId.toB58String()).not.to.equal(messageKey.toB58String());
      chai_js.expect(pubKeyPeerId.toB58String()).to.equal(testAccount.id);
      chai_js.expect(publishedMessage.from).to.equal(idA.id);
      chai_js.expect(messageKey.toB58String()).to.equal(idA.id);
      chai_js.expect(toString.toString(publishedMessageData.value)).to.equal(ipfsRef);
      await ipns__namespace.validate(pubKeyPeerId.pubKey, publishedMessageData);
    });
  });
}

exports.testPubsub = testPubsub;
