import { BigIntValue, Descriptor, evalQ, Get, IntrinsicsFunctionToString, isArrayBufferObject, isArrayExoticObject, IsCallable, isDataViewObject, isDateObject, isECMAScriptFunctionObject, isErrorObject, isIntegerIndex, isMapObject, isPromiseObject, isProxyExoticObject, isRegExpObject, isSetObject, isTypedArrayObject, isWeakMapObject, isWeakSetObject, JSStringValue, NumberValue, ObjectValue, PrivateElementRecord, PrivateName, R, surroundingAgent, SymbolDescriptiveString, SymbolValue, ToString, skipDebugger, UndefinedValue, Value, isModuleNamespaceObject, DataBlock, TypedArrayGetElement, TypedArrayLength, MakeTypedArrayWithBufferWitnessRecord, DateProto_toISOString, ValueOfNormalCompletion, NormalCompletion, } from '#self';
const Null = {
    toRemoteObject: () => ({ type: 'object', subtype: 'null', value: null }),
    toObjectPreview: () => ({
        type: 'object', subtype: 'null', properties: [], overflow: false,
    }),
    toPropertyPreview: (name) => ({
        name, type: 'object', subtype: 'null', value: 'null',
    }),
    toDescription: () => '',
};
const Undefined = {
    toRemoteObject: () => ({ type: 'undefined' }),
    toObjectPreview: () => ({
        type: 'undefined', properties: [], overflow: false,
    }),
    toPropertyPreview: (name) => ({
        name, type: 'undefined', value: 'undefined',
    }),
    toDescription: () => 'undefined',
};
const Boolean = {
    toRemoteObject: (value) => ({ type: 'boolean', value: value.booleanValue() }),
    toPropertyPreview: (name, value) => ({
        name, type: 'boolean', value: value.booleanValue().toString(),
    }),
    toObjectPreview(value) {
        return {
            type: 'boolean',
            value: value.booleanValue(),
            description: value.booleanValue().toString(),
            overflow: false,
            properties: [],
        };
    },
    toDescription: (value) => value.booleanValue().toString(),
};
const Symbol = {
    toRemoteObject: (value, getObjectId) => ({
        type: 'symbol',
        description: SymbolDescriptiveString(value).stringValue(),
        objectId: getObjectId(value),
    }),
    toPropertyPreview: (name, value) => ({
        name, type: 'symbol', value: SymbolDescriptiveString(value).stringValue(),
    }),
    toObjectPreview: (value) => ({
        type: 'symbol',
        description: SymbolDescriptiveString(value).stringValue(),
        overflow: false,
        properties: [],
    }),
    toDescription: (value) => SymbolDescriptiveString(value).stringValue(),
};
const String = {
    toRemoteObject: (value) => ({ type: 'string', value: value.stringValue() }),
    toPropertyPreview(name, value) {
        return {
            name, type: 'string', value: value.stringValue(),
        };
    },
    toObjectPreview(value) {
        return {
            type: 'string',
            description: value.stringValue(),
            overflow: false,
            properties: [],
        };
    },
    toDescription: (value) => value.stringValue(),
};
const Number = {
    toRemoteObject(value) {
        const v = R(value);
        let description = v.toString();
        const isNeg0 = Object.is(v, -0);
        // Includes values `-0`, `NaN`, `Infinity`, `-Infinity`, and bigint literals.
        if (isNeg0 || !globalThis.Number.isFinite(v)) {
            if (typeof v === 'bigint') {
                description += 'n';
                return { type: 'bigint', unserializableValue: description, description };
            }
            return { type: 'number', unserializableValue: description, description: isNeg0 ? '-0' : description };
        }
        return { type: 'number', value: v, description };
    },
    toPropertyPreview(name, value) {
        return {
            name, type: 'number', value: this.toDescription(value),
        };
    },
    toObjectPreview(value) {
        return {
            type: 'number',
            description: this.toDescription(value),
            overflow: false,
            properties: [],
        };
    },
    toDescription: (value) => {
        const r = R(value);
        return value instanceof BigIntValue ? `${r}n` : r.toString();
    },
};
const Function = {
    toRemoteObject(value, getObjectId) {
        const result = {
            type: 'function',
            objectId: getObjectId(value),
        };
        result.description = IntrinsicsFunctionToString(value);
        if (isECMAScriptFunctionObject(value) && value.ECMAScriptCode) {
            if (value.ECMAScriptCode.type === 'FunctionBody') {
                result.className = 'Function';
            }
            else if (value.ECMAScriptCode.type === 'GeneratorBody') {
                result.className = 'GeneratorFunction';
            }
            else if (value.ECMAScriptCode.type === 'AsyncBody') {
                result.className = 'AsyncFunction';
            }
            else if (value.ECMAScriptCode.type === 'AsyncGeneratorBody') {
                result.className = 'AsyncGeneratorFunction';
            }
        }
        else {
            result.className = 'Function';
        }
        return result;
    },
    toPropertyPreview: (name) => ({ name, type: 'function', value: '' }),
    toObjectPreview(value) {
        return {
            type: 'function',
            description: IntrinsicsFunctionToString(value),
            overflow: false,
            properties: [],
        };
    },
    toDescription: () => 'Function',
};
class ObjectInspector {
    subtype;
    className;
    toDescription;
    toEntries;
    additionalProperties;
    constructor(className, subtype, toDescription, additionalOptions) {
        this.className = className;
        this.subtype = subtype;
        this.toDescription = toDescription;
        this.toEntries = additionalOptions?.entries;
        this.additionalProperties = additionalOptions?.additionalProperties;
    }
    toRemoteObject(value, getObjectId) {
        return {
            type: 'object',
            subtype: this.subtype,
            objectId: getObjectId(value),
            className: typeof this.className === 'string' ? this.className : this.className(value),
            description: this.toDescription(value),
            preview: this.toObjectPreview(value),
        };
    }
    toPropertyPreview(name, value) {
        return {
            name, type: 'object', subtype: this.subtype, value: this.toDescription(value),
        };
    }
    toObjectPreview(value) {
        const e = this.toEntries?.(value);
        return {
            type: 'object',
            subtype: this.subtype,
            description: this.toDescription(value),
            entries: e?.length ? e : undefined,
            ...propertiesToPropertyPreview(value, this.additionalProperties?.(value)),
        };
    }
}
const Default = new ObjectInspector('Object', undefined, () => 'Object');
const ArrayBuffer = new ObjectInspector('ArrayBuffer', 'arraybuffer', (value) => `ArrayBuffer(${value.ArrayBufferByteLength})`, {});
const DataView = new ObjectInspector('DataView', 'dataview', (value) => `DataView(${value.ByteLength})`);
const Error = new ObjectInspector('SyntaxError', 'error', (value) => {
    let text = '';
    surroundingAgent.debugger_scopePreview(() => {
        evalQ((Q) => {
            if (value instanceof ObjectValue) {
                const stack = Q(skipDebugger(Get(value, Value('stack'))));
                if (stack !== Value.undefined) {
                    text += Q(skipDebugger(ToString(stack))).stringValue();
                }
            }
        });
    });
    return text;
});
const Map = new ObjectInspector('Map', 'map', (value) => `Map(${value.MapData.filter((x) => !!x.Key).length})`, {
    additionalProperties: (value) => [['size', Value(value.MapData.filter((x) => !!x.Key).length)]],
    entries: (value) => value.MapData.filter((x) => x.Key).map(({ Key, Value }) => ({
        key: getInspector(Key).toObjectPreview(Key),
        value: getInspector(Value).toObjectPreview(Value),
    })),
});
const Set = new ObjectInspector('Set', 'set', (value) => `Set(${value.SetData.filter(globalThis.Boolean).length})`, {
    additionalProperties: (value) => [['size', Value(value.SetData.filter(globalThis.Boolean).length)]],
    entries: (value) => value.SetData.filter(globalThis.Boolean).map((Value) => ({
        value: getInspector(Value).toObjectPreview(Value),
    })),
});
const WeakMap = new ObjectInspector('WeakMap', 'weakmap', () => 'WeakMap', {
    entries: (value) => value.WeakMapData.filter((x) => x.Key).map(({ Key, Value }) => ({
        key: getInspector(Key).toObjectPreview(Key),
        value: getInspector(Value).toObjectPreview(Value),
    })),
});
const WeakSet = new ObjectInspector('WeakSet', 'weakset', () => 'WeakSet', {
    entries: (value) => value.WeakSetData.filter(globalThis.Boolean).map((Value) => ({
        value: getInspector(Value).toObjectPreview(Value),
    })),
});
const Date = new ObjectInspector('Date', 'date', ((value) => {
    if (!globalThis.Number.isFinite(R(value.DateValue))) {
        return 'Invalid Date';
    }
    const val = DateProto_toISOString([], { thisValue: value, NewTarget: Value.undefined });
    return ValueOfNormalCompletion(val).stringValue();
}));
const Promise = new ObjectInspector('Promise', 'promise', () => 'Promise', {
    additionalProperties: (value) => [['[[PromiseState]]', Value(value.PromiseState)], ['[[PromiseResult]]', value.PromiseResult || Value.undefined]],
});
const Proxy = new ObjectInspector('Proxy', 'proxy', (value) => {
    if (IsCallable(value.ProxyTarget)) {
        return 'Proxy(Function)';
    }
    if (value.ProxyTarget instanceof ObjectValue) {
        return 'Proxy(Object)';
    }
    return 'Proxy';
});
const RegExp = new ObjectInspector('RegExp', 'regexp', (value) => `/${value.OriginalSource.stringValue()}/${value.OriginalFlags.stringValue()}`);
const Module = new ObjectInspector('Module', undefined, () => 'Module', {});
const Array = {
    toRemoteObject(value, getObjectId) {
        return {
            type: 'object',
            className: 'Array',
            subtype: 'array',
            objectId: getObjectId(value),
            description: getInspector(value).toDescription(value),
            preview: this.toObjectPreview?.(value),
        };
    },
    toPropertyPreview(name, value) {
        return {
            name, type: 'object', subtype: 'array', value: this.toDescription(value),
        };
    },
    toObjectPreview(value) {
        const result = {
            type: 'object',
            subtype: 'array',
            overflow: false,
            properties: [],
            description: this.toDescription(value),
        };
        const indexProp = [];
        const otherProp = [];
        for (const [key, desc] of value.properties) {
            if (indexProp.length > 100) {
                result.overflow = true;
                break;
            }
            if (isIntegerIndex(key)) {
                indexProp.push(propertyToPropertyPreview(key, desc));
            }
            else if (!(key instanceof JSStringValue && key.stringValue() === 'length')) {
                otherProp.push(propertyToPropertyPreview(key, desc));
            }
        }
        result.properties = indexProp.concat(otherProp).slice(0, 100);
        return result;
    },
    toDescription(value) {
        const length = [...value.properties.entries()].find(([key]) => key instanceof JSStringValue && key.stringValue() === 'length');
        if (!length || !(length[1].Value instanceof NumberValue)) {
            throw new TypeError('Bad ArrayExoticObject');
        }
        return `Array(${R(length[1].Value)})`;
    },
};
const TypedArray = new ObjectInspector('TypedArray', 'typedarray', (value) => `${value.TypedArrayName.stringValue()}(${value.ArrayLength})`);
function propertyToPropertyPreview(key, desc) {
    let name;
    if (key instanceof JSStringValue) {
        name = key.stringValue();
    }
    else if (key instanceof PrivateName) {
        name = key.Description.stringValue();
    }
    else {
        name = SymbolDescriptiveString(key).stringValue();
    }
    if (desc.Get || desc.Set) {
        return { name, type: 'accessor' };
    }
    else {
        return getInspector(desc.Value).toPropertyPreview(name, desc.Value);
    }
}
function propertiesToPropertyPreview(value, extra, max = 5) {
    let overflow = false;
    const properties = [];
    if (extra) {
        for (const [key, value] of extra) {
            properties.push(getInspector(value).toPropertyPreview(key, value));
        }
    }
    if (isTypedArrayObject(value) && value.ViewedArrayBuffer instanceof ObjectValue && value.ViewedArrayBuffer.ArrayBufferData instanceof DataBlock) {
        const record = MakeTypedArrayWithBufferWitnessRecord(value, 'seq-cst');
        const length = TypedArrayLength(record);
        for (let index = 0; index < length; index += 1) {
            const index_value = TypedArrayGetElement(value, Value(index));
            if (index_value instanceof UndefinedValue) {
                break;
            }
            if (properties.length > 100) {
                overflow = true;
                break;
            }
            properties.push(getInspector(index_value).toPropertyPreview(index.toString(), index_value));
        }
        properties.push({
            name: 'buffer', type: 'object', subtype: 'arraybuffer', value: `ArrayBuffer(${value.ViewedArrayBuffer.ArrayBufferData.byteLength})`,
        }, { name: 'byteLength', type: 'number', value: globalThis.String(value.ArrayLength) }, { name: 'byteOffset', type: 'number', value: globalThis.String(value.ByteOffset) }, { name: 'length', type: 'number', value: globalThis.String(length) });
    }
    for (const [key, desc] of value.properties) {
        if (properties.length > max) {
            overflow = true;
            break;
        }
        properties.push(propertyToPropertyPreview(key, desc));
    }
    for (const desc of value.PrivateElements) {
        if (properties.length > max) {
            overflow = true;
            break;
        }
        properties.push(propertyToPropertyPreview(desc.Key, desc));
    }
    return { overflow, properties };
}
export function getInspector(value) {
    switch (true) {
        case value === Value.null:
            return Null;
        case value === Value.undefined:
            return Undefined;
        case value === Value.true || value === Value.false:
            return Boolean;
        case value instanceof SymbolValue:
            return Symbol;
        case value instanceof JSStringValue:
            return String;
        case value instanceof NumberValue:
        case value instanceof BigIntValue:
            return Number;
        case isProxyExoticObject(value):
            return Proxy;
        case IsCallable(value):
            return Function;
        case isArrayExoticObject(value):
            return Array;
        case isRegExpObject(value):
            return RegExp;
        case isDateObject(value):
            return Date;
        case isMapObject(value):
            return Map;
        case isSetObject(value):
            return Set;
        case isWeakMapObject(value):
            return WeakMap;
        case isWeakSetObject(value):
            return WeakSet;
        // generator
        case isErrorObject(value):
            return Error;
        case isPromiseObject(value):
            return Promise;
        case isTypedArrayObject(value):
            return TypedArray;
        case isArrayBufferObject(value):
            return ArrayBuffer;
        case isDataViewObject(value):
            return DataView;
        case isModuleNamespaceObject(value):
            return Module;
        default:
            return Default;
    }
}
//# sourceMappingURL=inspect.mjs.map