import { Eltro as t, assert, stub } from 'eltro'
import http from 'http'
import https from 'https'
import { setTimeout } from 'timers/promises'
import { request } from '../core/client.mjs'
import HttpServer from '../core/http.mjs'

const port = 61413
let prefix = `http://localhost:${port}/`

t.describe('config', function() {
  t.test('should use https if https is true', function() {
    let server = new HttpServer()
    assert.strictEqual(server.creator, http)
    assert.strictEqual(server.ishttps, false)
    server = new HttpServer({ https: true })
    assert.strictEqual(server.creator, https)
    assert.strictEqual(server.ishttps, true)
  })
})

t.describe('Sockets', function() {
  let http = new HttpServer()

  t.after(function() {
    return http.closeServer()
  })

  t.test('should keep track of sockets through its lifetime', function(cb) {
    let actives = []
  
    let server = http.createServer(function(req, res) {
      req.on('error', function(err) { cb(err) })
      res.on('error', function(err) { cb(err) })
      res.on('finish', function() { })
  
      actives.push(res)
    })
  
    Promise.resolve()
    .then(async function() {
      await new Promise(function(res, rej) {
        server.listen(port, function() { res()})
      })

      assert.strictEqual(actives.length, 0)
      assert.strictEqual(http.sockets.size, 0)

      request({}, prefix).then(function() {}, cb)
      request({}, prefix).then(async function() {
        while (http.sockets.size > 0) {
          await setTimeout(10)
        }
        assert.strictEqual(http.sockets.size, 0)
        cb()
      }, cb)

      while (actives.length < 2) {
        await setTimeout(10)
      }
      assert.strictEqual(http.sockets.size, 2)

      assert.ok(http.active)
      actives[0].statusCode = 200
      actives[0].end('{}')
      actives[1].statusCode = 200
      actives[1].end('{}')
    }).catch(cb)
  })
})

t.describe('closeServer()', function() {
  let http = new HttpServer()

  t.after(function() {
    return http.closeServer()
  })

  t.test('should not fail if server is not listening', function() {
    http.createServer(function() { })

    return http.closeServer()
  })

  t.test('should support forcefully closing them on server close', function(cb) {
    let requestErrors = []
    let serverErrors = []

    let server = http.createServer(function(req, res) {
      req.on('error', function(err) { serverErrors.push(err) })
      res.on('error', function(err) { serverErrors.push(err) })
      res.on('finish', function() { })
    })

    Promise.resolve()
    .then(async function() {
      await new Promise(function(res, rej) {
        server.listen(port, function() { res()})
      })

      assert.strictEqual(http.sockets.size, 0)

      request({}, prefix).then(
        function() { cb(new Error('first succeeded')) },
        function(err) { requestErrors.push(err) }
      )
      request({}, prefix).then(
        function() { cb(new Error('first succeeded')) },
        function(err) { requestErrors.push(err) }
      )

      while (http.sockets.size < 2) {
        await setTimeout(10)
      }
      
      assert.ok(http.active)

      http.closeServer().then(function() { }, cb)
      
      while (requestErrors.length < 2) {
        await setTimeout(10)
      }
      assert.strictEqual(http.sockets.size, 0)
      assert.strictEqual(requestErrors.length, 2)
      assert.strictEqual(serverErrors.length, 2)
      assert.strictEqual(serverErrors[0].code, 'ECONNRESET')
      assert.strictEqual(serverErrors[1].code, 'ECONNRESET')
      assert.strictEqual(requestErrors[0].code, 'ECONNRESET')
      assert.strictEqual(requestErrors[1].code, 'ECONNRESET')
      
      while (requestErrors.length < 2) {
        await setTimeout(10)
      }
      while (http.active) {
        await setTimeout(10)
      }
    })
    .then(function() { cb()}, cb)
  })
})

t.describe('listenAsync()', function() {
  let httpFirst = new HttpServer()
  let httpSecond = new HttpServer()

  t.after(function() {
    return Promise.all([
      httpFirst.closeServer(),
      httpSecond.closeServer(),
    ])
  })

  t.test('should reject successfully if port is busy', async function() {
    let serverFirst = httpFirst.createServer(function() { })
    let serverSecond = httpSecond.createServer(function() { })

    await serverFirst.listenAsync(port)
    
    await setTimeout(10)

    let err = await assert.isRejected(serverSecond.listenAsync(port))
    assert.strictEqual(err.code, 'EADDRINUSE')

    assert.ok(serverFirst.listening)
    assert.notOk(serverSecond.listening)
  })
})
