import {
  Article,
  ArticleResponse,
  GroupResponse,
  GroupsResponse,
  NewnewsResponse,
  NntpErrorResponse,
  NntpResponse,
  PostResponse
} from '../src'
import { c, client, integrationSetup, s } from './IntegrationCommon'

integrationSetup()

// Note: RFC977 is deprecated in favor of RFC3977, test cases note
// where the two RFCs conflict.

describe(`4.  Sample Conversations

   These are samples of the conversations that might be expected with
   the news server in hypothetical sessions.  The notation C: indicates
   commands sent to the news server from the client program; S: indicate
   responses received from the server by the client.`, () => {
  test('4.1.  Example 1 - relative access with NEXT', async () => {
    // s('(listens at TCP port 119)')
    // c('(requests connection on TCP port 119)')
    // s('200 wombatvax news server ready - posting ok')

    // (client asks for a current newsgroup list)
    c('LIST')
    s('215 list of newsgroups follows')
    s('net.wombats 00543 00501 y')
    s('net.unix-wizards 10125 10011 y')
    // (more information here)
    s('net.idiots 00100 00001 n')
    s('.')

    let response = await client.list()
    expect(response).toEqual({
      code: 215,
      comment: 'list of newsgroups follows',
      description: 'Information follows (multi-line)',
      newsgroups: [
        {
          name: 'net.wombats',
          high: 543,
          low: 501,
          status: 'y'
        },
        {
          name: 'net.unix-wizards',
          high: 10125,
          low: 10011,
          status: 'y'
        },
        {
          name: 'net.idiots',
          high: 100,
          low: 1,
          status: 'n'
        }
      ]
    } as GroupsResponse)

    // (client selects a newsgroup)
    c('GROUP net.unix-wizards')
    s('211 104 10011 10125 net.unix-wizards group selected')
    // (there are 104 articles on file, from 10011 to 10125)

    response = await client.group('net.unix-wizards')
    expect(response).toEqual({
      code: 211,
      comment: 'group selected',
      description: 'Group successfully selected',
      group: {
        number: 104,
        low: 10011,
        high: 10125,
        name: 'net.unix-wizards'
      }
    } as GroupResponse)

    // (client selects an article to read)
    c('STAT 10110')
    s('223 10110 <23445@sdcsvax.ARPA> article retrieved - statistics')
    // only (article 10110 selected, its message-id is <23445@sdcsvax.ARPA>)

    response = await client.stat(10110)
    expect(response).toEqual({
      code: 223,
      comment: 'article retrieved - statistics',
      description: 'Article exists',
      article: {
        articleNumber: 10110,
        messageId: '<23445@sdcsvax.ARPA>'
      }
    } as ArticleResponse)

    // (client examines the header)
    c('HEAD')
    s('221 10110 <23445@sdcsvax.ARPA> article retrieved - head')
    // follows (text of the header appears here)
    s('.')

    response = await client.head()
    expect(response).toEqual({
      code: 221,
      comment: 'article retrieved - head',
      description: 'Headers follow (multi-line)',
      article: {
        articleNumber: 10110,
        messageId: '<23445@sdcsvax.ARPA>',
        headers: {}
      }
    } as ArticleResponse)

    // (client wants to see the text body of the article)
    c('BODY')
    s('222 10110 <23445@sdcsvax.ARPA> article retrieved - body')
    // follows (body text here)
    s('.')

    response = await client.body()
    expect(response).toEqual({
      code: 222,
      comment: 'article retrieved - body',
      description: 'Body follows (multi-line)',
      article: {
        articleNumber: 10110,
        messageId: '<23445@sdcsvax.ARPA>',
        body: []
      }
    } as ArticleResponse)

    // (client selects next article in group)
    c('NEXT')
    s('223 10113 <21495@nudebch.uucp> article retrieved - statistics')
    // only (article 10113 was next in group)

    response = await client.next()
    expect(response).toEqual({
      code: 223,
      comment: 'article retrieved - statistics',
      description: 'Article found',
      article: {
        articleNumber: 10113,
        messageId: '<21495@nudebch.uucp>'
      }
    } as ArticleResponse)

    // (client finishes session)
    c('QUIT')
    s('205 goodbye.')

    response = await client.quit()
    expect(response).toEqual({
      code: 205,
      comment: 'NNTP Service exits normally',
      description: 'Connection closing'
    } as NntpResponse)
  })

  test('4.2.  Example 2 - absolute article access with ARTICLE', async () => {
    // s('(listens at TCP port 119)')

    // c('(requests connection on TCP port 119)')
    // s('201 UCB-VAX netnews server ready -- no posting allowed')

    c('GROUP msgs')
    s('211 103 402 504 msgs Your new group is msgs')
    //         (there are 103 articles, from 402 to 504)

    let response = await client.group('msgs')
    expect(response).toEqual({
      code: 211,
      comment: 'Your new group is msgs',
      description: 'Group successfully selected',
      group: {
        number: 103,
        low: 402,
        high: 504,
        name: 'msgs'
      }
    } as GroupResponse)

    c('ARTICLE 401')
    s('423 No such article in this newsgroup')

    let caught = false
    try {
      await client.article(401)
    } catch (response) {
      expect(response).toEqual({
        code: 423,
        comment: 'No such article in this newsgroup',
        description: 'No article with that number'
      } as NntpErrorResponse)
      caught = true
    }
    expect(caught).toBe(true)

    c('ARTICLE 402')
    s('220 402 <4105@ucbvax.ARPA> Article retrieved, text follows')
    // article header and body follow
    s('')
    s('.')

    response = await client.article(402)
    expect(response).toEqual({
      code: 220,
      comment: 'Article retrieved, text follows',
      description: 'Article follows (multi-line)',
      article: {
        articleNumber: 402,
        messageId: '<4105@ucbvax.ARPA>',
        headers: {},
        body: []
      }
    } as ArticleResponse)

    c('HEAD 403')
    s('221 403 <3108@mcvax.UUCP> Article retrieved, header follows')
    // article header follows
    s('.')

    response = await client.head(403)
    expect(response).toEqual({
      code: 221,
      comment: 'Article retrieved, header follows',
      description: 'Headers follow (multi-line)',
      article: {
        articleNumber: 403,
        messageId: '<3108@mcvax.UUCP>',
        headers: {}
      }
    } as ArticleResponse)

    c('QUIT')
    s('205 UCB-VAX news server closing connection.  Goodbye.')

    response = await client.quit()
    expect(response).toEqual({
      code: 205,
      comment: 'NNTP Service exits normally',
      description: 'Connection closing'
    } as NntpErrorResponse)
  })

  test('4.3.  Example 3 - NEWGROUPS command', async () => {
    // s('(listens at TCP port 119)')

    // c('(requests connection on TCP port 119)')
    // s('200 Imaginary Institute News Server ready (posting ok)')

    // (client asks for new newsgroups since April 3, 1985)
    c('NEWGROUPS 850403 020000')
    s('231 New newsgroups since 03/04/85 02:00:00 follow')
    s('net.music.gdead')
    s('net.games.sources')
    s('.')

    // Note: RFC977 NEWGROUPS command conflicts with RFC3977 NEWGROUPS command:
    //   In RFC977, the command returns 1 groups per line
    //   In RFC3977 the command returns 4 fields per line

    // let response = await client.newgroups('1985-04-03T02:00:00Z')
    // let response = await client.command('NEWGROUPS', '850403', '020000')
    // expect(response).toEqual({
    //   code: 231,
    //   comment: 'New newsgroups since 03/04/85 02:00:00 follow',
    //   description: 'List of new newsgroups follows (multi-line)',
    //   newsgroups: [
    //     {
    //       name: 'net.music.gdead',
    //     },
    //     {
    //       name: 'net.games.sources',
    //     }
    //   ]
    // })

    c('GROUP net.music.gdead')
    s('211 0 1 1 net.music.gdead Newsgroup selected')
    // (there are no articles in that newsgroup, and
    // the first and last article numbers should be ignored)

    let response = await client.group('net.music.gdead')
    expect(response).toEqual({
      code: 211,
      comment: 'Newsgroup selected',
      description: 'Group successfully selected',
      group: {
        number: 0,
        low: 1,
        high: 1,
        name: 'net.music.gdead'
      }
    } as GroupResponse)

    c('QUIT')
    s('205 Imaginary Institute news server ceasing service.  Bye!')

    response = await client.quit()
    expect(response).toEqual({
      code: 205,
      comment: 'NNTP Service exits normally',
      description: 'Connection closing'
    } as NntpResponse)
  })

  test('4.4.  Example 4 - posting a news article', async () => {
    // s('(listens at TCP port 119)')

    // c('(requests connection on TCP port 119)')
    // s('200 BANZAIVAX news server ready, posting allowed.')

    c('POST')
    s('340 Continue posting; Period on a line by itself to end')

    let response = await client.post()
    expect(response).toEqual({
      code: 340,
      comment: 'Continue posting; Period on a line by itself to end',
      description: 'Send article to be posted',
      send: expect.any(Function)
    } as PostResponse)

    // Note: RFC977's above comment invalidated by RFC3977's ABNF which shows
    // an empty article still has a CR-LF for headers

    // (transmits news article in RFC850 format)
    c('', '.')
    s('240 Article posted successfully.')

    response = await response.send({
      headers: {},
      body: []
    })
    expect(response).toEqual({
      code: 240,
      comment: 'Article posted successfully.',
      description: 'Article received OK'
    } as NntpResponse)

    c('QUIT')
    s('205 BANZAIVAX closing connection.  Goodbye.')

    response = await client.quit()
    expect(response).toEqual({
      code: 205,
      comment: 'NNTP Service exits normally',
      description: 'Connection closing'
    } as NntpResponse)
  })

  test('4.5.  Example 5 - interruption due to operator request', async () => {
    s('(listens at TCP port 119)')

    c('(requests connection on TCP port 119)')
    s('201 genericvax news server ready, no posting allowed.')
    // (assume normal conversation for some time, and
    // that a newsgroup has been selected)

    c('NEXT')
    s('223 1013 <5734@mcvax.UUCP> Article retrieved; text separate.')

    const response = await client.next()
    expect(response).toEqual({
      code: 223,
      comment: 'Article retrieved; text separate.',
      description: 'Article found',
      article: {
        articleNumber: 1013,
        messageId: '<5734@mcvax.UUCP>'
      }
    } as ArticleResponse)

    c('HEAD')
    c('221 1013 <5734@mcvax.UUCP> Article retrieved; head follows.')

    // s(`(sends head of article, but halfway through is
    //     interrupted by an operator request.  The following
    //     then occurs, without client intervention.)`)
    // s('(ends current line with a CR-LF pair)')
    s('.')
    s('400 Connection closed by operator.  Goodbye.')
    // s('(closes connection)')

    // Note: in RFC3977 the client does not take random error codes from server,
    // but expects the 400 as a response to the next command
  })

  test(`4.6.  Example 6 - Using the news server to distribute news between
      systems.`, async () => {
    // s('(listens at TCP port 119)')

    // c('(requests connection on TCP port 119)')
    // s('201 Foobar NNTP server ready (no posting)')

    // (client asks for new newsgroups since 2 am, May 15, 1985)
    c('NEWGROUPS 850515 020000')
    s('235 New newsgroups since 850515 follow')
    s('net.fluff')
    s('net.lint')
    s('.')

    // let response = await client.newgroups('1985-05-15T02:00:00Z')
    // let response = await client.command('NEWGROUPS', '850515', '020000')
    // NOTE: RFC977 and RFC3977 have different responses to NEWGROUPS command

    // (client asks for new news articles since 2 am, May 15, 1985)
    c('NEWNEWS * 850515 020000')
    s('230 New news since 850515 020000 follows')
    s('<1772@foo.UUCP>')
    s('<87623@baz.UUCP>')
    s('<17872@GOLD.CSNET>')
    s('.')

    // let response = await client.newnews('*', '1985-05-15T02:00:00')
    let response = await client.command('NEWNEWS', '*', '850515', '020000')
    expect(response).toEqual({
      code: 230,
      comment: 'New news since 850515 020000 follows',
      description: 'List of new articles follows (multi-line)',
      messageIds: ['<1772@foo.UUCP>', '<87623@baz.UUCP>', '<17872@GOLD.CSNET>']
    } as NewnewsResponse)

    // (client asks for article <1772@foo.UUCP>)
    c('ARTICLE <1772@foo.UUCP>')
    s('220 <1772@foo.UUCP> All of article follows')
    // s('(sends entire message)')
    s('')
    s('.')

    // response = await client.article('<1772@foo.UUCP>')
    // expect(response).toEqual({
    //   code: 220,
    //   comment: '',
    //   description: 'Article follows (multi-line)',
    //   article: {
    //     articleNumber: /* ??? */0,
    //     messageId: '<1772@foo.UUCP>',
    //     headers: {},
    //     body: []
    //   }
    // })
    // Note: RFC977 is missing articleNumber from RFC3977 in response

    // (client asks for article <87623@baz.UUCP>
    c('ARTICLE <87623@baz.UUCP>')
    s('220 <87623@baz.UUCP> All of article follows')
    s('(sends entire message)')
    s('.')

    // Note: RFCs conflict, see above

    // (client asks for article <17872@GOLD.CSNET>
    c('ARTICLE <17872@GOLD.CSNET>')
    s('220 <17872@GOLD.CSNET> All of article follows')
    s('(sends entire message)')
    s('.')

    // Note: RFCs conflict, see above

    // (client offers an article it has received recently)
    c('IHAVE <4105@ucbvax.ARPA>')
    s('435 Already seen that one, where you been?')

    let caught = false
    try {
      await client.ihave('<4105@ucbvax.ARPA>')
    } catch (response) {
      caught = true
      expect(response).toEqual({
        code: 435,
        comment: 'Already seen that one, where you been?',
        description: 'Article not wanted'
      } as NntpErrorResponse)
    }
    expect(caught).toBe(true)

    // (client offers another article)
    c('IHAVE <4106@ucbvax.ARPA>')
    s('335 News to me!  <CRLF.CRLF> to end.')

    response = await client.ihave('<4106@ucbvax.ARPA>')
    expect(response).toEqual({
      code: 335,
      comment: 'News to me!  <CRLF.CRLF> to end.',
      description: 'Send article to be transferred',
      send: expect.any(Function)
    } as PostResponse)

    // c('(sends article)')
    c('.')
    // Note: In RFC3977, a single dot is a syntax error

    // s('235 Article transferred successfully.  Thanks.')
    //
    //    (or)
    //
    // s('436 Transfer failed.')

    // (client is all through with the session)
    c('QUIT')
    s('205 Foobar NNTP server bids you farewell.')

    response = await client.quit()
    expect(response).toEqual({
      code: 205,
      comment: 'NNTP Service exits normally',
      description: 'Connection closing'
    } as NntpResponse)
  })
})
