import { test } from '@japa/runner'
import { Headers } from '../../../src/index.js'

test.group(`string input`, () => {
  const input = 'x-a:a\nx-b:b\nc:c'
  const expectedSize = 3
  const keysArray = ['x-a', 'x-b', 'c']
  const valuesArray = ['a', 'b', 'c']

  test(`Parses a string as an input`, ({ assert }) => {
    const instance = new Headers(input)
    assert.lengthOf(Object.keys(instance._map), expectedSize)
  })

  test('Has correct list of headers', ({ assert }) => {
    const instance = new Headers(input)
    const keys = Object.keys(instance._map)
    keys.forEach((key) => {
      assert.notEqual(keysArray.indexOf(key), -1)
    })
    keys.forEach((key) => {
      assert.notEqual(valuesArray.indexOf(instance._map[key].value), -1)
    })
  })
})

test.group(`map input`, () => {
  const input = {
    'x-a': 'a',
    'x-b': 'b',
    'c': 'c',
  }
  const expectedSize = 3
  const keysArray = ['x-a', 'x-b', 'c']
  const valuesArray = ['a', 'b', 'c']

  test(`Parses a string as an input`, ({ assert }) => {
    const instance = new Headers(input)
    assert.lengthOf(Object.keys(instance._map), expectedSize)
  })

  test('Has correct list of headers', ({ assert }) => {
    const instance = new Headers(input)
    const keys = Object.keys(instance._map)
    keys.forEach((key) => {
      assert.notEqual(keysArray.indexOf(key), -1)
    })
    keys.forEach((key) => {
      assert.notEqual(valuesArray.indexOf(instance._map[key].value), -1)
    })
  })
})

test.group(`undefined input`, () => {
  test(`has no values in the map`, ({ assert }) => {
    const instance = new Headers()
    assert.lengthOf(Object.keys(instance._map), 0)
  })
})

test.group('append()', () => {
  test('Appends an item that does not exists', ({ assert }) => {
    const instance = new Headers()
    instance.append('name', 'value')
    assert.equal(instance._map.name.value, 'value')
    assert.equal(instance._map.name.name, 'name')
  })

  test('Updates existing item', ({ assert }) => {
    const instance = new Headers()
    instance.append('name', 'value')
    instance.append('name', 'other-value')
    assert.equal(instance._map.name.value, 'value,other-value')
  })

  test('Normalizes keys', ({ assert }) => {
    const instance = new Headers()
    instance.append('AbCd', 'value')
    assert.ok(instance._map.abcd)
  })
})

test.group('set()', () => {
  test('Sets an item that does not exists', ({ assert }) => {
    const instance = new Headers()
    instance.set('name', 'value')
    assert.equal(instance._map.name.value, 'value')
    assert.equal(instance._map.name.name, 'name')
  })

  test('Replaces existing item', ({ assert }) => {
    const instance = new Headers()
    instance.set('name', 'value')
    instance.set('name', 'other-value')
    assert.equal(instance._map.name.value, 'other-value')
  })

  test('Normalizes keys', ({ assert }) => {
    const instance = new Headers()
    instance.set('AbCd', 'value')
    assert.ok(instance._map.abcd)
  })
})

test.group('delete()', () => {
  test('Ignores item that does not exists', ({ assert }) => {
    const instance = new Headers('a: b')
    instance.delete('x')
    assert.ok(instance._map.a)
  })

  test('Removes existing item', ({ assert }) => {
    const instance = new Headers('a: b')
    instance.delete('a')
    assert.lengthOf(Object.keys(instance._map), 0)
  })

  test('Normalizes keys', ({ assert }) => {
    const instance = new Headers('AbCd: xxx')
    instance.delete('aBcD')
    assert.lengthOf(Object.keys(instance._map), 0)
  })
})

test.group('get()', () => {
  test('Returns undefined for non existing item', ({ assert }) => {
    const instance = new Headers('a: b')
    const result = instance.get('x')
    assert.isUndefined(result)
  })

  test('Returns value for existing item', ({ assert }) => {
    const instance = new Headers('a: b')
    const result = instance.get('a')
    assert.equal(result, 'b')
  })

  test('Normalizes keys', ({ assert }) => {
    const instance = new Headers('AbCd: xxx')
    const result = instance.get('aBcD')
    assert.equal(result, 'xxx')
  })
})

test.group('has()', () => {
  test('Returns false for non existing item', ({ assert }) => {
    const instance = new Headers('a: b')
    const result = instance.has('x')
    assert.isFalse(result)
  })

  test('Returns value for existing item', ({ assert }) => {
    const instance = new Headers('a: b')
    const result = instance.has('a')
    assert.isTrue(result)
  })

  test('Normalizes keys', ({ assert }) => {
    const instance = new Headers('AbCd: xxx')
    const result = instance.has('aBcD')
    assert.isTrue(result)
  })
})

test.group('forEach()', () => {
  const input = 'AbCd: Test\nMy_Test: header'
  const keys = ['AbCd', 'My_Test']
  const values = ['Test', 'header']
  test('Iterates over items', ({ assert }) => {
    const instance = new Headers(input)
    instance.forEach((value, name) => {
      assert.notEqual(keys.indexOf(name), -1, `${name} is a name`)
      assert.notEqual(values.indexOf(value), -1, `${value} is a value`)
    })
  })
})

test.group('toString()', () => {
  const input = 'My_Test: header\nAbCd: Test'
  test('Returns HTTP string', ({ assert }) => {
    const instance = new Headers(input)
    const result = instance.toString()
    assert.equal(result, input)
  })

  test('Returns HTTP string with multiple values', ({ assert }) => {
    const instance = new Headers(input)
    instance.append('abcd', 'test')
    const result = instance.toString()
    assert.equal(result, `${input},test`)
  })
})

test.group('keys()', () => {
  const input = 'My_Test: header\nAbCd: Test'
  test('Is a generator', ({ assert }) => {
    const instance = new Headers(input)
    const result = instance.keys()
    assert.ok(result.next)
    const next = result.next()
    assert.isFalse(next.done)
  })

  test('can iterate over the keys', ({ assert }) => {
    const instance = new Headers(input)
    const allowed = ['My_Test', 'AbCd']
    for (const key of instance.keys()) {
      assert.notEqual(allowed.indexOf(key), -1)
    }
  })
})

test.group('values()', () => {
  const input = 'My_Test: header\nAbCd: Test'
  test('Is a generator', ({ assert }) => {
    const instance = new Headers(input)
    const result = instance.values()
    assert.ok(result.next)
    const next = result.next()
    assert.isFalse(next.done)
  })

  test('Can iterate over the values', ({ assert }) => {
    const instance = new Headers(input)
    const allowed = ['header', 'Test']
    for (const key of instance.values()) {
      assert.notEqual(allowed.indexOf(key), -1)
    }
  })
})

test.group('entries()', () => {
  const input = 'My_Test: header\nAbCd: Test'
  test('Is a generator', ({ assert }) => {
    const instance = new Headers(input)
    const result = instance.entries()
    assert.ok(result.next)
    const next = result.next()
    assert.isFalse(next.done)
  })

  test('Can iterate over the entries', ({ assert }) => {
    const instance = new Headers(input)
    const allowedKeys = ['My_Test', 'AbCd']
    const allowedValues = ['header', 'Test']
    for (const [key, value] of instance.entries()) {
      assert.notEqual(allowedKeys.indexOf(key), -1)
      assert.notEqual(allowedValues.indexOf(value), -1)
    }
  })
})

test.group('Symbol.iterator', () => {
  const input = 'My_Test: header\nAbCd: Test'

  test('Can iterate over the object', ({ assert }) => {
    const instance = new Headers(input)
    const allowedKeys = ['My_Test', 'AbCd']
    const allowedValues = ['header', 'Test']
    for (const [key, value] of instance) {
      assert.notEqual(allowedKeys.indexOf(key), -1)
      assert.notEqual(allowedValues.indexOf(value), -1)
    }
  })
})
