'use strict';

var $RangeError = require('es-errors/range');
var $TypeError = require('es-errors/type');

var Get = require('./Get');
var IsCallable = require('./IsCallable');
var ToIntegerOrInfinity = require('./ToIntegerOrInfinity');
var ToNumber = require('./ToNumber');

var isNaN = require('..//helpers/isNaN');
var isObject = require('es-object-atoms/isObject');

var callBind = require('call-bind');
var isSet = require('is-set');
var stopIterationIterator = require('stop-iteration-iterator');

// https://262.ecma-international.org/16.0/#sec-getsetrecord

module.exports = function GetSetRecord(obj) {
	if (!isObject(obj)) {
		throw new $TypeError('obj is not an Object'); // step 1
	}

	var rawSize = Get(obj, 'size'); // step 2

	var numSize = ToNumber(rawSize); // step 3

	//  4. NOTE: If rawSize is undefined, then numSize will be NaN.
	if (isNaN(numSize)) {
		throw new $TypeError('`size` is not a non-NaN Number'); // step 5
	}

	var intSize = ToIntegerOrInfinity(numSize); // step 6

	if (intSize < 0) {
		throw new $RangeError('set size must be non-negative'); // step 7
	}

	var has = Get(obj, 'has'); // step 8

	if (!IsCallable(has)) {
		throw new $TypeError('has is not a function'); // step 9
	}

	var keys = Get(obj, 'keys'); // step 10
	if (!IsCallable(keys)) {
		throw new $TypeError('keys is not a function'); // step 11
	}
	/* globals StopIteration: false */
	if (isSet(obj) && typeof StopIteration === 'object') {
		var boundKeys = callBind(keys);
		keys = function keys() { // eslint-disable-line no-shadow
			return stopIterationIterator(boundKeys(this)); // eslint-disable-line no-invalid-this
		};
	}

	return {
		'[[SetObject]]': obj,
		'[[Size]]': intSize,
		'[[Has]]': has,
		'[[Keys]]': keys
	}; // step 12
};
