// *****************************************************************************
// Copyright 2013-2024 Aerospike, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License")
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// *****************************************************************************

'use strict'

/* global expect, describe, context, it */
/* eslint-disable no-unused-expressions */

import Aerospike, { Key as K, status as statusModule, Client as Cli, KeyOptions} from 'aerospike';

import { expect } from 'chai'; 
import * as helper from './test_helper';

const Key: typeof K = Aerospike.Key
const status: typeof statusModule = Aerospike.status


describe('Key #noserver', function () {
  describe('constructor', function () {

    /*
    context('namespace', function () {
      it('rejects empty namespace', function () {
        expect(function () { return new Key('', 'set', 'key') }).to.throw('Namespace must be a valid string')
      })

      it('rejects namespace that is too long', function () {
        expect(function () { return new Key('abcdefghijklmnopqrstuvwxyz0123456789', 'set', 'key') }).to.throw('Namespace must be a valid string')
      })

      it('rejects null namespace', function () {
        expect(function () { return new Key(null, 'set', 'key') }).to.throw('Namespace must be a valid string')
      })

      it('rejects undefined namespace', function () {
        expect(function () { return new Key(undefined, 'set', 'key') }).to.throw('Namespace must be a valid string')
      })

      it('rejects namespace that is not a string', function () {
        expect(function () { return new Key(1234, 'set', 'key') }).to.throw('Namespace must be a valid string')
      })
    })
    */

    context('set name', function () {
      it('allows null set name', function () {
        expect(new Key('ns', null, 'key')).to.be.ok
      })

      it('allows undefined set name', function () {
        expect(new Key('ns', undefined, 'key')).to.be.ok
      })
      /*
      it('rejects empty set name', function () {
        expect(function () { return new Key('ns', '', 'key') }).to.throw('Set must be a valid string')
      })

      it('rejects set name that is too long', function () {
        expect(function () { return new Key('ns', 'abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789', 'key') }).to.throw('Set must be a valid string')
      })

      it('rejects set name that is not a string', function () {
        expect(function () { return new Key('ns', 1234, 'key') }).to.throw('Set must be a valid string')
      })
      */
    })

    context('user key', function () {
      const dummyDigest = Buffer.from([0x15, 0xc7, 0x49, 0xfd, 0x01, 0x54, 0x43, 0x8b, 0xa9, 0xd9, 0x5d, 0x0c, 0x6e, 0x27, 0x0f, 0x1a, 0x76, 0xfc, 0x31, 0x15])

      it('allows string user key', function () {
        expect(new Key('ns', 'set', 'abc')).to.be.ok
      })

      it('allows string user key of arbitrary length', function () {
        expect(new Key('ns', 'set', 'abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz0123456789')).to.be.ok
      })

      it('allows integer user key', function () {
        expect(new Key('ns', 'set', 1234)).to.be.ok
        expect(new Key('ns', 'set', -1234)).to.be.ok
      })

      context('BigInt keys', function () {
        it('allows BigInt user key', function () {
          expect(new Key('ns', 'set', BigInt(42))).to.be.ok
          expect(new Key('ns', 'set', BigInt(2) ** BigInt(63) - BigInt(1))).to.be.ok
          expect(new Key('ns', 'set', BigInt(-2) ** BigInt(63))).to.be.ok
        })
        /*
        it('rejects BigInt user keys outside valid range', function () {
          expect(() => new Key('ns', 'set', BigInt(2) ** BigInt(63))).to.throw(TypeError, /Invalid user key/)
          expect(() => new Key('ns', 'set', BigInt(-2) ** BigInt(63) - BigInt(1))).to.throw(TypeError, /Invalid user key/)
        })
        */
      })

      it('allows byte array user key', function () {
        const buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72])
        expect(new Key('ns', 'set', buf)).to.be.ok
      })

      it('allows undefined user key', function () {
        expect(new Key('ns', 'set', undefined, dummyDigest)).to.be.ok
      })

      it('allows null user key', function () {
        expect(new Key('ns', 'set', null, dummyDigest)).to.be.ok
      })
      /*
      it('rejects empty string user key', function () {
        expect(() => new Key('ns', 'set', '')).to.throw(TypeError, /Invalid user key/)
      })

      it('rejects empty byte array user key', function () {
        expect(() => new Key('ns', 'set', Buffer.from([]))).to.throw(TypeError, /Invalid user key/)
      })

      it('rejects float user key', function () {
        expect(() => new Key('ns', 'set', 3.1415)).to.throw(TypeError, /Invalid user key/)
      })

      it('rejects Object user key', function () {
        expect(() => new Key('ns', 'set', { key: 'myKey' })).to.throw(TypeError, /Invalid user key/)
      })

      it('requires either key or digest', function () {
        expect(() => new Key('ns', 'set')).to.throw('Either key or digest must be set')
      })
      */
    })

    context('digest', function () {
      it('allows creating a new key with just the namespace and digest', function () {
        const digest = Buffer.from([0x15, 0xc7, 0x49, 0xfd, 0x01, 0x54, 0x43, 0x8b, 0xa9, 0xd9, 0x5d, 0x0c, 0x6e, 0x27, 0x0f, 0x1a, 0x76, 0xfc, 0x31, 0x15])
        expect(new Key('ns', null, null, digest)).to.be.ok
      })
      /*
      it('rejects a digest that is not a buffer', function () {
        expect(function () { return new Key('ns', null, null, 'some string') }).to.throw('Digest must be a 20-byte Buffer')
      })

      it('rejects a digest that is not the right size', function () {
        expect(function () { return new Key('ns', null, null, Buffer.from([0x01])) }).to.throw('Digest must be a 20-byte Buffer')
      })
      */
    })
  })

  describe('equals', function () {
    it('matches two keys with identical ns, set and user key', function () {
      const key1: K = new Key('ns1', 'set1', 'key1')
      const key2: K = new Key('ns1', 'set1', 'key1')
      expect(key1.equals(key2)).to.be.true
      expect(key2.equals(key1)).to.be.true
    })

    it('matches two keys with identical ns, set, user key and digest', function () {
      const key1: K = new Key('ns1', 'set1', 'key1', Buffer.from('a1b2c3d4e5f6g7h8i9j0'))
      const key2: K = new Key('ns1', 'set1', 'key1', Buffer.from('a1b2c3d4e5f6g7h8i9j0'))
      expect(key1.equals(key2)).to.be.true
      expect(key2.equals(key1)).to.be.true
    })

    it('matches two keys with identical ns, set and digest', function () {
      const key1: K = new Key('ns1', 'set1', null, Buffer.from('a1b2c3d4e5f6g7h8i9j0'))
      const key2: K = new Key('ns1', 'set1', null, Buffer.from('a1b2c3d4e5f6g7h8i9j0'))
      expect(key1.equals(key2)).to.be.true
      expect(key2.equals(key1)).to.be.true
    })

    it('a key with digest to another key with identical ns, set and user key but without digest', function () {
      const key1: K = new Key('ns1', 'set1', 'key1', Buffer.from('a1b2c3d4e5f6g7h8i9j0'))
      const key2: K = new Key('ns1', 'set1', 'key1')
      expect(key1.equals(key2)).to.be.true
      expect(key2.equals(key1)).to.be.true
    })

    it('matches two keys with identical ns, empty set and user key', function () {
      const key1: K = new Key('ns1', null, 'key1')
      const key2: K = new Key('ns1', null, 'key1')
      expect(key1.equals(key2)).to.be.true
      expect(key2.equals(key1)).to.be.true
    })

    it('does not match two keys with different ns', function () {
      const key1: K = new Key('ns1', 'set1', 'key1')
      const key2: K = new Key('ns2', 'set1', 'key1')
      expect(key1.equals(key2)).to.be.false
      expect(key2.equals(key1)).to.be.false
    })

    it('does not match two keys with different set', function () {
      const key1: K = new Key('ns1', 'set1', 'key1')
      const key2: K = new Key('ns1', 'set2', 'key1')
      expect(key1.equals(key2)).to.be.false
      expect(key2.equals(key1)).to.be.false
    })

    it('does not match a key with set and a key without set', function () {
      const key1: K = new Key('ns1', 'set1', 'key1')
      const key2: K = new Key('ns1', null, 'key1')
      expect(key1.equals(key2)).to.be.false
      expect(key2.equals(key1)).to.be.false
    })

    it('does not match two keys with different user keys', function () {
      const key1: K = new Key('ns1', 'set1', 'key1')
      const key2: K = new Key('ns1', 'set1', 'key2')
      expect(key1.equals(key2)).to.be.false
      expect(key2.equals(key1)).to.be.false
    })

    it('does not match a key with user key and a key without user key', function () {
      const key1: K = new Key('ns1', 'set1', 'key1', Buffer.from('a1b2c3d4e5f6g7h8i9j0'))
      const key2: K = new Key('ns1', 'set1', null, Buffer.from('a1b2c3d4e5f6g7h8i9j0'))
      expect(key1.equals(key2)).to.be.false
      expect(key2.equals(key1)).to.be.false
    })

    it('does not match two keys with different digests', function () {
      const key1: K = new Key('ns1', 'set1', 'key1', Buffer.from('a1b2c3d4e5f6g7h8i9j0'))
      const key2: K = new Key('ns1', 'set1', 'key1', Buffer.from('0j9i8h7g6f5e4d3c2b1a'))
      expect(key1.equals(key2)).to.be.false
      expect(key2.equals(key1)).to.be.false
    })
  })
})

context('Plain Object Keys (for backward compatibility)', function () {
  const client: Cli = helper.client

  it('accepts plain objects as user keys', function (done) {
    const key: KeyOptions = { ns: helper.namespace, set: helper.set, key: 1234 }
    client.put(key, { foo: 'bar' }, function (err: any) {
      expect(err).to.not.be.ok
      done()
    })
  })

  it('returns an error for an unsupported float user key', function (done) {
    const key: KeyOptions = { ns: helper.namespace, set: helper.set, key: 3.1415 }
    client.put(key, { foo: 'bar' }, function (err: any) {
      expect(err.code).to.equal(status.ERR_PARAM)
      done()
    })
  })

  /*
  it('returns an error for an invalid user key', function (done) {
    const key = { ns: helper.namespace, set: helper.set, key: { a: 1, b: 2, c: 3 } }
    client.put(key, { foo: 'bar' }, function (err: any) {
      expect(err.code).to.equal(status.ERR_PARAM)
      done()
    })
  })
  */
})
