"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.unicodeJson = exports.json = exports.unicodeJsonObject = exports.jsonObject = exports.object = exports.anything = exports.boxArbitrary = void 0;
const stringify_1 = require("../../utils/stringify");
const ArrayArbitrary_1 = require("./ArrayArbitrary");
const BooleanArbitrary_1 = require("./BooleanArbitrary");
const ConstantArbitrary_1 = require("./ConstantArbitrary");
const DictionaryArbitrary_1 = require("./DictionaryArbitrary");
const FloatingPointArbitrary_1 = require("./FloatingPointArbitrary");
const FrequencyArbitrary_1 = require("./FrequencyArbitrary");
const IntegerArbitrary_1 = require("./IntegerArbitrary");
const MemoArbitrary_1 = require("./MemoArbitrary");
const OneOfArbitrary_1 = require("./OneOfArbitrary");
const SetArbitrary_1 = require("./SetArbitrary");
const StringArbitrary_1 = require("./StringArbitrary");
const TupleArbitrary_1 = require("./TupleArbitrary");
const BigIntArbitrary_1 = require("./BigIntArbitrary");
const DateArbitrary_1 = require("./DateArbitrary");
const TypedArrayArbitrary_1 = require("./TypedArrayArbitrary");
function boxArbitrary(arb) {
    return arb.map((v) => {
        switch (typeof v) {
            case 'boolean':
                return new Boolean(v);
            case 'number':
                return new Number(v);
            case 'string':
                return new String(v);
            default:
                return v;
        }
    });
}
exports.boxArbitrary = boxArbitrary;
class QualifiedObjectConstraints {
    constructor(key, values, maxDepth, maxKeys, withSet, withMap, withObjectString, withNullPrototype, withBigInt, withDate, withTypedArray) {
        this.key = key;
        this.values = values;
        this.maxDepth = maxDepth;
        this.maxKeys = maxKeys;
        this.withSet = withSet;
        this.withMap = withMap;
        this.withObjectString = withObjectString;
        this.withNullPrototype = withNullPrototype;
        this.withBigInt = withBigInt;
        this.withDate = withDate;
        this.withTypedArray = withTypedArray;
    }
    static defaultValues() {
        return [
            BooleanArbitrary_1.boolean(),
            IntegerArbitrary_1.maxSafeInteger(),
            FloatingPointArbitrary_1.double({ next: true }),
            StringArbitrary_1.string(),
            OneOfArbitrary_1.oneof(StringArbitrary_1.string(), ConstantArbitrary_1.constant(null), ConstantArbitrary_1.constant(undefined)),
        ];
    }
    static boxArbitraries(arbs) {
        return arbs.map((arb) => boxArbitrary(arb));
    }
    static boxArbitrariesIfNeeded(arbs, boxEnabled) {
        return boxEnabled ? QualifiedObjectConstraints.boxArbitraries(arbs).concat(arbs) : arbs;
    }
    static from(settings = {}) {
        function orDefault(optionalValue, defaultValue) {
            return optionalValue !== undefined ? optionalValue : defaultValue;
        }
        return new QualifiedObjectConstraints(orDefault(settings.key, StringArbitrary_1.string()), QualifiedObjectConstraints.boxArbitrariesIfNeeded(orDefault(settings.values, QualifiedObjectConstraints.defaultValues()), orDefault(settings.withBoxedValues, false)), orDefault(settings.maxDepth, 2), orDefault(settings.maxKeys, 5), orDefault(settings.withSet, false), orDefault(settings.withMap, false), orDefault(settings.withObjectString, false), orDefault(settings.withNullPrototype, false), orDefault(settings.withBigInt, false), orDefault(settings.withDate, false), orDefault(settings.withTypedArray, false));
    }
}
const anythingInternal = (constraints) => {
    const arbKeys = constraints.withObjectString
        ? MemoArbitrary_1.memo((n) => FrequencyArbitrary_1.frequency({ arbitrary: constraints.key, weight: 10 }, { arbitrary: anythingArb(n).map((o) => stringify_1.stringify(o)), weight: 1 }))
        : MemoArbitrary_1.memo(() => constraints.key);
    const arbitrariesForBase = constraints.values;
    const maxDepth = constraints.maxDepth;
    const maxKeys = constraints.maxKeys;
    const entriesOf = (keyArb, valueArb) => SetArbitrary_1.set(TupleArbitrary_1.tuple(keyArb, valueArb), { maxLength: maxKeys, compare: (t1, t2) => t1[0] === t2[0] });
    const mapOf = (ka, va) => entriesOf(ka, va).map((v) => new Map(v));
    const dictOf = (ka, va) => entriesOf(ka, va).map((v) => DictionaryArbitrary_1.toObject(v));
    const baseArb = OneOfArbitrary_1.oneof(...arbitrariesForBase);
    const arrayBaseArb = OneOfArbitrary_1.oneof(...arbitrariesForBase.map((arb) => ArrayArbitrary_1.array(arb, { maxLength: maxKeys })));
    const objectBaseArb = (n) => OneOfArbitrary_1.oneof(...arbitrariesForBase.map((arb) => dictOf(arbKeys(n), arb)));
    const setBaseArb = () => OneOfArbitrary_1.oneof(...arbitrariesForBase.map((arb) => SetArbitrary_1.set(arb, 0, maxKeys).map((v) => new Set(v))));
    const mapBaseArb = (n) => OneOfArbitrary_1.oneof(...arbitrariesForBase.map((arb) => mapOf(arbKeys(n), arb)));
    const arrayArb = MemoArbitrary_1.memo((n) => OneOfArbitrary_1.oneof(arrayBaseArb, ArrayArbitrary_1.array(anythingArb(n), { maxLength: maxKeys })));
    const setArb = MemoArbitrary_1.memo((n) => OneOfArbitrary_1.oneof(setBaseArb(), SetArbitrary_1.set(anythingArb(n), 0, maxKeys).map((v) => new Set(v))));
    const mapArb = MemoArbitrary_1.memo((n) => OneOfArbitrary_1.oneof(mapBaseArb(n), OneOfArbitrary_1.oneof(mapOf(arbKeys(n), anythingArb(n)), mapOf(anythingArb(n), anythingArb(n)))));
    const objectArb = MemoArbitrary_1.memo((n) => OneOfArbitrary_1.oneof(objectBaseArb(n), dictOf(arbKeys(n), anythingArb(n))));
    const anythingArb = MemoArbitrary_1.memo((n) => {
        if (n <= 0)
            return OneOfArbitrary_1.oneof(baseArb);
        return OneOfArbitrary_1.oneof(baseArb, arrayArb(), objectArb(), ...(constraints.withMap ? [mapArb()] : []), ...(constraints.withSet ? [setArb()] : []), ...(constraints.withObjectString ? [anythingArb().map((o) => stringify_1.stringify(o))] : []), ...(constraints.withNullPrototype ? [objectArb().map((o) => Object.assign(Object.create(null), o))] : []), ...(constraints.withBigInt ? [BigIntArbitrary_1.bigInt()] : []), ...(constraints.withDate ? [DateArbitrary_1.date()] : []), ...(constraints.withTypedArray
            ? [
                TypedArrayArbitrary_1.int8Array(),
                TypedArrayArbitrary_1.uint8Array(),
                TypedArrayArbitrary_1.uint8ClampedArray(),
                TypedArrayArbitrary_1.int16Array(),
                TypedArrayArbitrary_1.uint16Array(),
                TypedArrayArbitrary_1.int32Array(),
                TypedArrayArbitrary_1.uint32Array(),
                TypedArrayArbitrary_1.float32Array(),
                TypedArrayArbitrary_1.float64Array(),
            ]
            : []));
    });
    return anythingArb(maxDepth);
};
const objectInternal = (constraints) => {
    return DictionaryArbitrary_1.dictionary(constraints.key, anythingInternal(constraints));
};
function anything(constraints) {
    return anythingInternal(QualifiedObjectConstraints.from(constraints));
}
exports.anything = anything;
function object(constraints) {
    return objectInternal(QualifiedObjectConstraints.from(constraints));
}
exports.object = object;
function jsonSettings(stringArbitrary, constraints) {
    const key = stringArbitrary;
    const values = [
        BooleanArbitrary_1.boolean(),
        IntegerArbitrary_1.maxSafeInteger(),
        FloatingPointArbitrary_1.double({ next: true, noDefaultInfinity: true, noNaN: true }),
        stringArbitrary,
        ConstantArbitrary_1.constant(null),
    ];
    return constraints != null
        ? typeof constraints === 'number'
            ? { key, values, maxDepth: constraints }
            : { key, values, maxDepth: constraints.maxDepth }
        : { key, values };
}
function jsonObject(constraints) {
    return anything(jsonSettings(StringArbitrary_1.string(), constraints));
}
exports.jsonObject = jsonObject;
function unicodeJsonObject(constraints) {
    return anything(jsonSettings(StringArbitrary_1.unicodeString(), constraints));
}
exports.unicodeJsonObject = unicodeJsonObject;
function json(constraints) {
    const arb = constraints != null ? jsonObject(constraints) : jsonObject();
    return arb.map(JSON.stringify);
}
exports.json = json;
function unicodeJson(constraints) {
    const arb = constraints != null ? unicodeJsonObject(constraints) : unicodeJsonObject();
    return arb.map(JSON.stringify);
}
exports.unicodeJson = unicodeJson;
