Client = (require '../main').Client
assert = require 'assert'
nodemailer = require 'nodemailer'

describe 'POP3 client tests', () ->
  this.timeout 120000
  count = 0
  before (done) ->
    transporter = nodemailer.createTransport {
      service: 'Mail.ru'
      auth: {
        user: 'yapople@mail.ru'
        pass: 'yapopleyapopleyapopleyapople'
      }
    }
    mailOptions = {
      from: 'yapople@mail.ru'
      to: 'yapople@mail.ru'
      subject: 'msg 0 сообщение'
      text: 'msg 0 сообщение'
      html: '<b>Hello world ✔ Дорждынька</b>'
    }
    transporter.sendMail mailOptions, (error) ->
      if error
        console.log error
      else
        mailOptions.subject = 'msg 1 сообщение'
        mailOptions.text = 'msg 1 сообщение'
        transporter.sendMail mailOptions, (error) ->
          if error
            console.log error
          else
            mailOptions.subject = 'msg 2 сообщение'
            mailOptions.text = 'msg 2 сообщение'
            transporter.sendMail mailOptions, (error) ->
              if error
                console.log error
              else
                mailOptions.subject = 'msg 3 сообщение'
                mailOptions.text = 'msg 3 сообщение'
                transporter.sendMail mailOptions, (error) ->
                  if error
                    console.log error
                  else
                    done()

  options = {
    hostname: 'pop.mail.ru'
    port: 110
    username: 'yapople'
    password: 'yapopleyapopleyapopleyapople'
  }
  tlsOptions = {
    hostname: 'pop.mail.ru'
    port:  995
    tls: true
    username: 'yapople'
    password: 'yapopleyapopleyapopleyapople'
  }

  describe 'connect', () ->
    ###it 'should return an error when connection refused', (done) ->
      client = new Client {hostname: 'unknown'}
      client.connect (err) ->
        assert.notEqual err, null
        done()###
    it 'should not executes commands not being connected', (done) ->
      client = new Client options
      client.list (err) ->
        assert.notEqual err, null
        done()
    it 'should not login to TLS server without tls option', (done) ->
      client = new Client options
      client.connect (err, data) ->
        assert.notEqual err, null
        assert.equal err.message, 'POP3 is available only with SSL or TLS connection enabled'
        done()
    it 'should login to TLS server with tls option', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        assert.equal err, null
        assert.equal data, 'Welcome!'
        client.disconnect done

  describe 'stat command', () ->
    it 'returns message stat count', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        assert.equal err, null
        client.stat (err, data) ->
          assert.equal err, null
          assert.equal typeof data.count, 'number'
          assert.equal typeof data.length, 'number'
          client.disconnect done

  describe 'list command', () ->
    it 'returns message list count', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        assert.equal err, null
        client.list (err, data) ->
          assert.equal err, null
          assert.ok Object.keys(data).length >= 0
          client.disconnect
          done()
    it 'returns info about message', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        assert.equal err, null
        client.list 1, (err, data) ->
          assert.equal err, null
          client.disconnect()
          done()

  describe 'retr command', () ->
    it 'should return message body for known message', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        client.retr 1, (err, data) ->
          assert.equal err, null
          client.disconnect done
    it 'should return an error for unknown message', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        client.retr 666, (err, data) ->
          assert.notEqual err, null
          client.disconnect done
    it 'should return parsed message using mailparser', (done) ->
      tlsOptions.mailparser = true
      client = new Client tlsOptions
      client.connect (err, data) ->
        client.retr 1, (err, data) ->
          assert.equal err, null
          assert.ok data.text
          console.log data if not data.subject
          assert.ok data.subject
          assert.ok data.headers
          client.disconnect done

  describe 'count command', () ->
    it 'should return message count', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        client.count (err, data) ->
          assert.equal err, null
          assert.ok data >= 0
          count = data
          client.disconnect done

  describe 'dele command', () ->
    it 'should mark last message as deleted', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        client.dele count, (err, data) ->
          assert.equal err, null
          client.quit done
    it 'should be deleted after the end of transaction', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        assert.equal err, null
        client.count (err, data) ->
          assert.equal err, null
          assert.equal data, count - 1
          count = data
          client.disconnect done

  describe 'rset command', () ->
    it 'should mark last message as deleted, then reset', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        client.dele count, (err, data) ->
          assert.equal err, null
          client.rset (err, data) ->
            assert.equal err, null
            client.quit done
    it 'should not be deleted after the end of transaction', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        assert.equal err, null
        client.count (err, data) ->
          assert.equal err, null
          assert.equal data, count
          client.disconnect done

  describe 'connect', () ->
    it 'should properly connect after disconnection', (done) ->
      client = new Client tlsOptions
      client.connect (err) ->
        assert.equal err, null
        client.disconnect (err) ->
          assert.equal err, null
          client.connect (err) ->
            assert.equal err, null
            client.disconnect (err) ->
              assert.equal err, null
              done()

  describe 'top', () ->
    it 'should return an error for wrong message number', (done) ->
      client = new Client tlsOptions
      client.connect (err) ->
        client.top 0, 0, (err, res) ->
          assert.notEqual err, null
          client.disconnect done
    it 'should return raw message headers', (done) ->
      tlsOptions.mailparser = false
      client = new Client tlsOptions
      client.connect (err) ->
        client.top 1, 0, (err, res) ->
          assert.equal err, null
          assert.ok Buffer.isBuffer(res) # typeof res, 'string'
          client.disconnect done
    it 'should return message headers and body', (done) ->
      tlsOptions.mailparser = false
      client = new Client tlsOptions
      client.connect (err) ->
        client.top 1, 10, (err, res) ->
          assert.equal err, null
          assert.ok Buffer.isBuffer(res) # typeof res, 'string'
          client.disconnect done
    it 'should return parsed message headers', (done) ->
      tlsOptions.mailparser = true
      client = new Client tlsOptions
      client.connect (err) ->
        client.top 1, 0, (err, res) ->
          assert.equal err, null
          assert.equal typeof res, 'object'
          assert.ok res.subject
          assert.ok res.from
          assert.ok res.to
          assert.ok res.date
          client.disconnect done

  describe 'retrieve', () ->
    it 'should properly works on message number', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        client.retrieve count, (err, data) ->
          assert.equal err, null
          assert.equal typeof data, 'object'
          client.disconnect done
    it 'should properly works on array of message numbers', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        client.retrieve [count, count - 1, count - 2], (err, data) ->
          assert.equal err, null
          assert.ok Array.isArray data
          assert.equal data.filter((a) -> a).length, 3
          # TODO message checking
          client.disconnect done
    it 'should return an error with bad arguments', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        client.retrieve [count, count + 1, count + 2], (err, data) ->
          assert.notEqual err, null
          client.disconnect done

  describe 'delete', () ->
    it 'should properly delete an array of messages', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        client.delete [count, count - 1, count - 2], (err, data) ->
          assert.equal err, null
          assert.ok Array.isArray data
          assert.equal data.filter((a) -> a).length, 3
          data.forEach (msg) ->
            assert.ok /^message (\d)* deleted$/.test(msg)
          client.rset (err, data) ->
            assert.equal err, null
            client.disconnect done
    it 'should return an error with bad arguments and make a rset after all', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        client.delete [count, count + 1, count + 2], (err, data) ->
          assert.notEqual err, null
          client.disconnect () ->
            client.connect () ->
              client.count (err, cou) ->
                assert.equal cou, count
                client.disconnect done

  describe 'retrieveAll', () ->
    it 'should return all messages', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        client.retrieveAll (err, data) ->
          assert.equal err, null
          assert.ok Array.isArray data
          assert.equal data.filter((a) -> a).length, count
          client.disconnect done

  describe 'deleteAll', () ->
    it 'should delete all messages', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        client.deleteAll (err, data) ->
          assert.equal err, null
          assert.ok Array.isArray data
          assert.equal data.filter((a) -> a).length, count
          client.rset () ->
            client.disconnect done

  describe 'retrieveAndDeleteAll', () ->
    it 'should return all messages and delete them', (done) ->
      client = new Client tlsOptions
      client.connect (err, data) ->
        client.retrieveAndDeleteAll (err, data) ->
          assert.equal err, null
          assert.ok Array.isArray data
          assert.equal data.filter((a) -> a).length, count
          client.disconnect () ->
            client.connect () ->
              client.count (err, count) ->
                assert.equal err, null
                assert.equal count, 0
                client.disconnect done

  # TODO command sequence test
