'use strict'

const { EventEmitter } = require('events')
const debug = require('debug')('nock.socket')

module.exports = class Socket extends EventEmitter {
  constructor(options) {
    super()

    if (options.proto === 'https') {
      // https://github.com/nock/nock/issues/158
      this.authorized = true
    }

    this.bufferSize = 0
    this.writableLength = 0
    this.writable = true
    this.readable = true
    this.pending = false
    this.destroyed = false
    this.connecting = true

    // Undocumented flag used by ClientRequest to ensure errors aren't double-fired
    this._hadError = false

    // Maximum allowed delay. 0 means unlimited.
    this.timeout = 0

    const ipv6 = options.family === 6
    this.remoteFamily = ipv6 ? 'IPv6' : 'IPv4'
    this.localAddress = this.remoteAddress = ipv6 ? '::1' : '127.0.0.1'
    this.localPort = this.remotePort = parseInt(options.port)
  }

  setNoDelay() {}
  setKeepAlive() {}
  resume() {}
  ref() {}
  unref() {}

  address() {
    return {
      port: this.remotePort,
      family: this.remoteFamily,
      address: this.remoteAddress,
    }
  }

  setTimeout(timeoutMs, fn) {
    this.timeout = timeoutMs
    if (fn) {
      this.once('timeout', fn)
    }
    return this
  }

  /**
   * Artificial delay that will trip socket timeouts when appropriate.
   *
   * Doesn't actually wait for time to pass.
   * Timeout events don't necessarily end the request.
   * While many clients choose to abort the request upon a timeout, Node itself does not.
   */
  applyDelay(delayMs) {
    if (this.timeout && delayMs > this.timeout) {
      debug('socket timeout')
      this.emit('timeout')
    }
  }

  getPeerCertificate() {
    return Buffer.from(
      (Math.random() * 10000 + Date.now()).toString()
    ).toString('base64')
  }

  /**
   * Denotes that no more I/O activity should happen on this socket.
   *
   * The implementation in Node if far more complex as it juggles underlying async streams.
   * For the purposes of Nock, we just need it to set some flags and on the first call
   * emit a 'close' and optional 'error' event. Both events propagate through the request object.
   */
  destroy(err) {
    if (this.destroyed) {
      return this
    }

    debug('socket destroy')
    this.destroyed = true
    this.readable = this.writable = false

    process.nextTick(() => {
      if (err) {
        this._hadError = true
        this.emit('error', err)
      }
      this.emit('close')
    })

    return this
  }
}
