import { stringify } from '../../../utils/stringify.js';
import { array } from '../../array.js';
import { frequency } from '../../frequency.js';
import { oneof } from '../../oneof.js';
import { tuple } from '../../tuple.js';
import { bigInt } from '../../bigInt.js';
import { date } from '../../date.js';
import { float32Array } from '../../float32Array.js';
import { float64Array } from '../../float64Array.js';
import { int16Array } from '../../int16Array.js';
import { int32Array } from '../../int32Array.js';
import { int8Array } from '../../int8Array.js';
import { uint16Array } from '../../uint16Array.js';
import { uint32Array } from '../../uint32Array.js';
import { uint8Array } from '../../uint8Array.js';
import { uint8ClampedArray } from '../../uint8ClampedArray.js';
import { sparseArray } from '../../sparseArray.js';
import { keyValuePairsToObjectMapper, keyValuePairsToObjectUnmapper } from '../mappers/KeyValuePairsToObject.js';
import { convertFromNext, convertToNext } from '../../../check/arbitrary/definition/Converters.js';
import { arrayToMapMapper, arrayToMapUnmapper } from '../mappers/ArrayToMap.js';
import { arrayToSetMapper, arrayToSetUnmapper } from '../mappers/ArrayToSet.js';
import { objectToPrototypeLessMapper, objectToPrototypeLessUnmapper } from '../mappers/ObjectToPrototypeLess.js';
import { letrec } from '../../letrec.js';
import { uniqueArray } from '../../uniqueArray.js';
function entriesOf(keyArb, valueArb, maxKeys, size) {
    return convertToNext(uniqueArray(tuple(keyArb, valueArb), {
        maxLength: maxKeys,
        size,
        comparator: 'SameValueZero',
        selector: (t) => t[0],
    }));
}
function mapOf(ka, va, maxKeys, size) {
    return convertFromNext(entriesOf(ka, va, maxKeys, size).map(arrayToMapMapper, arrayToMapUnmapper));
}
function dictOf(ka, va, maxKeys, size) {
    return convertFromNext(entriesOf(ka, va, maxKeys, size).map(keyValuePairsToObjectMapper, keyValuePairsToObjectUnmapper));
}
function setOf(va, maxKeys, size) {
    return convertFromNext(convertToNext(uniqueArray(va, { maxLength: maxKeys, size, comparator: 'SameValueZero' })).map(arrayToSetMapper, arrayToSetUnmapper));
}
function prototypeLessOf(objectArb) {
    return convertFromNext(convertToNext(objectArb).map(objectToPrototypeLessMapper, objectToPrototypeLessUnmapper));
}
function typedArray(constraints) {
    return oneof(int8Array(constraints), uint8Array(constraints), uint8ClampedArray(constraints), int16Array(constraints), uint16Array(constraints), int32Array(constraints), uint32Array(constraints), float32Array(constraints), float64Array(constraints));
}
export function anyArbitraryBuilder(constraints) {
    const arbitrariesForBase = constraints.values;
    const depthFactor = constraints.depthFactor;
    const maxDepth = constraints.maxDepth;
    const maxKeys = constraints.maxKeys;
    const size = constraints.size;
    const baseArb = oneof(...arbitrariesForBase);
    return letrec((tie) => ({
        anything: oneof({ maxDepth, depthFactor }, baseArb, tie('array'), tie('object'), ...(constraints.withMap ? [tie('map')] : []), ...(constraints.withSet ? [tie('set')] : []), ...(constraints.withObjectString ? [tie('anything').map((o) => stringify(o))] : []), ...(constraints.withNullPrototype ? [prototypeLessOf(tie('object'))] : []), ...(constraints.withBigInt ? [bigInt()] : []), ...(constraints.withDate ? [date()] : []), ...(constraints.withTypedArray ? [typedArray({ maxLength: maxKeys, size })] : []), ...(constraints.withSparseArray ? [sparseArray(tie('anything'), { maxNumElements: maxKeys, size })] : [])),
        keys: constraints.withObjectString
            ? frequency({ arbitrary: constraints.key, weight: 10 }, { arbitrary: tie('anything').map((o) => stringify(o)), weight: 1 })
            : constraints.key,
        arrayBase: oneof(...arbitrariesForBase.map((arb) => array(arb, { maxLength: maxKeys, size }))),
        array: oneof(tie('arrayBase'), array(tie('anything'), { maxLength: maxKeys, size })),
        setBase: oneof(...arbitrariesForBase.map((arb) => setOf(arb, maxKeys, size))),
        set: oneof(tie('setBase'), setOf(tie('anything'), maxKeys, size)),
        mapBase: oneof(...arbitrariesForBase.map((arb) => mapOf(tie('keys'), arb, maxKeys, size))),
        map: oneof(tie('mapBase'), oneof(mapOf(tie('keys'), tie('anything'), maxKeys, size), mapOf(tie('anything'), tie('anything'), maxKeys, size))),
        objectBase: oneof(...arbitrariesForBase.map((arb) => dictOf(tie('keys'), arb, maxKeys, size))),
        object: oneof(tie('objectBase'), dictOf(tie('keys'), tie('anything'), maxKeys, size)),
    })).anything;
}
