text: 'use strict'
  /* eslint-env mocha */
  
  const { expect } = require('aegir/utils/chai')
  const sinon = require('sinon')
  
  const { Multiaddr } = require('multiaddr')
  const Resolver = require('multiaddr/src/resolvers/dns')
  
  const { codes: ErrorCodes } = require('../../src/errors')
  
  const peerUtils = require('../utils/creators/peer')
  const baseOptions = require('../utils/base-options.browser')
  
  const { MULTIADDRS_WEBSOCKETS } = require('../fixtures/browser')
  const relayAddr = MULTIADDRS_WEBSOCKETS[0]
  
  const getDnsaddrStub = (peerId) => [
    [`dnsaddr=/dnsaddr/ams-1.bootstrap.libp2p.io/p2p/${peerId}`],
    [`dnsaddr=/dnsaddr/ams-2.bootstrap.libp2p.io/p2p/${peerId}`],
    [`dnsaddr=/dnsaddr/lon-1.bootstrap.libp2p.io/p2p/${peerId}`],
    [`dnsaddr=/dnsaddr/nrt-1.bootstrap.libp2p.io/p2p/${peerId}`],
    [`dnsaddr=/dnsaddr/nyc-1.bootstrap.libp2p.io/p2p/${peerId}`],
    [`dnsaddr=/dnsaddr/sfo-2.bootstrap.libp2p.io/p2p/${peerId}`]
  ]
  
  const relayedAddr = (peerId) => `${relayAddr}/p2p-circuit/p2p/${peerId}`
  
  const getDnsRelayedAddrStub = (peerId) => [
    [`dnsaddr=${relayedAddr(peerId)}`]
  ]
  
  describe('Dialing (resolvable addresses)', () => {
    let libp2p, remoteLibp2p
  
    beforeEach(async () => {
      [libp2p, remoteLibp2p] = await peerUtils.createPeer({
        number: 2,
        config: {
          ...baseOptions,
          addresses: {
            listen: [new Multiaddr(`${relayAddr}/p2p-circuit`)]
          },
          config: {
            ...baseOptions.config,
            peerDiscovery: {
              autoDial: false
            }
          }
        },
        started: true,
        populateAddressBooks: false
      })
    })
  
    afterEach(async () => {
      sinon.restore()
      await Promise.all([libp2p, remoteLibp2p].map(n => n.stop()))
    })
  
    it('resolves dnsaddr to ws local address', async () => {
      const remoteId = remoteLibp2p.peerId.toB58String()
      const dialAddr = new Multiaddr(`/dnsaddr/remote.libp2p.io/p2p/${remoteId}`)
      const relayedAddrFetched = new Multiaddr(relayedAddr(remoteId))
  
      // Transport spy
      const transport = libp2p.transportManager._transports.get('Circuit')
      sinon.spy(transport, 'dial')
  
      // Resolver stub
      const stub = sinon.stub(Resolver.prototype, 'resolveTxt')
      stub.onCall(0).returns(Promise.resolve(getDnsRelayedAddrStub(remoteId)))
  
      // Dial with address resolve
      const connection = await libp2p.dial(dialAddr)
      expect(connection).to.exist()
      expect(connection.remoteAddr.equals(relayedAddrFetched))
  
      const dialArgs = transport.dial.firstCall.args
      expect(dialArgs[0].equals(relayedAddrFetched)).to.eql(true)
    })
  
    it('resolves a dnsaddr recursively', async () => {
      const remoteId = remoteLibp2p.peerId.toB58String()
      const dialAddr = new Multiaddr(`/dnsaddr/remote.libp2p.io/p2p/${remoteId}`)
      const relayedAddrFetched = new Multiaddr(relayedAddr(remoteId))
  
      // Transport spy
      const transport = libp2p.transportManager._transports.get('Circuit')
      sinon.spy(transport, 'dial')
  
      // Resolver stub
      const stub = sinon.stub(Resolver.prototype, 'resolveTxt')
      let firstCall = false
      stub.callsFake(() => {
        if (!firstCall) {
          firstCall = true
          // Return an array of dnsaddr
          return Promise.resolve(getDnsaddrStub(remoteId))
        }
        return Promise.resolve(getDnsRelayedAddrStub(remoteId))
      })
  
      // Dial with address resolve
      const connection = await libp2p.dial(dialAddr)
      expect(connection).to.exist()
      expect(connection.remoteAddr.equals(relayedAddrFetched))
  
      const dialArgs = transport.dial.firstCall.args
      expect(dialArgs[0].equals(relayedAddrFetched)).to.eql(true)
    })
  
    // TODO: Temporary solution does not resolve dns4/dns6
    // Resolver just returns the received multiaddrs
    it('stops recursive resolve if finds dns4/dns6 and dials it', async () => {
      const remoteId = remoteLibp2p.peerId.toB58String()
      const dialAddr = new Multiaddr(`/dnsaddr/remote.libp2p.io/p2p/${remoteId}`)
  
      // Stub resolver
      const dnsMa = new Multiaddr(`/dns4/ams-1.remote.libp2p.io/tcp/443/wss/p2p/${remoteId}`)
      const stubResolve = sinon.stub(Resolver.prototype, 'resolveTxt')
      stubResolve.returns(Promise.resolve([
        [`dnsaddr=${dnsMa}`]
      ]))
  
      // Stub transport
      const transport = libp2p.transportManager._transports.get('WebSockets')
      const stubTransport = sinon.stub(transport, 'dial')
      stubTransport.callsFake((multiaddr) => {
        expect(multiaddr.equals(dnsMa)).to.eql(true)
      })
  
      await libp2p.dial(dialAddr)
    })
  
    it('resolves a dnsaddr recursively not failing if one address fails to resolve', async () => {
      const remoteId = remoteLibp2p.peerId.toB58String()
      const dialAddr = new Multiaddr(`/dnsaddr/remote.libp2p.io/p2p/${remoteId}`)
      const relayedAddrFetched = new Multiaddr(relayedAddr(remoteId))
  
      // Transport spy
      const transport = libp2p.transportManager._transports.get('Circuit')
      sinon.spy(transport, 'dial')
  
      // Resolver stub
      const stub = sinon.stub(Resolver.prototype, 'resolveTxt')
      stub.onCall(0).callsFake(() => Promise.resolve(getDnsaddrStub(remoteId)))
      stub.onCall(1).callsFake(() => Promise.reject(new Error()))
      stub.callsFake(() => Promise.resolve(getDnsRelayedAddrStub(remoteId)))
  
      // Dial with address resolve
      const connection = await libp2p.dial(dialAddr)
      expect(connection).to.exist()
      expect(connection.remoteAddr.equals(relayedAddrFetched))
  
      const dialArgs = transport.dial.firstCall.args
      expect(dialArgs[0].equals(relayedAddrFetched)).to.eql(true)
    })
  
    it('fails to dial if resolve fails and there are no addresses to dial', async () => {
      const remoteId = remoteLibp2p.peerId.toB58String()
      const dialAddr = new Multiaddr(`/dnsaddr/remote.libp2p.io/p2p/${remoteId}`)
  
      // Stub resolver
      const stubResolve = sinon.stub(Resolver.prototype, 'resolveTxt')
      stubResolve.returns(Promise.reject(new Error()))
  
      // Stub transport
      const transport = libp2p.transportManager._transports.get('WebSockets')
      const spy = sinon.spy(transport, 'dial')
  
      await expect(libp2p.dial(dialAddr))
        .to.eventually.be.rejectedWith(Error)
        .and.to.have.nested.property('.code', ErrorCodes.ERR_NO_VALID_ADDRESSES)
      expect(spy.callCount).to.eql(0)
    })
  })
  