import { TextDecoderEsque } from "../internals/esque.js";
import {
	TsonBranded,
	TsonType,
	TsonTypeTesterCustom,
	serialized,
} from "../sync/syncTypes.js";
import { TsonStreamInterruptedError } from "./asyncErrors.js";

export type TsonAsyncStringifierIterable<TValue> = AsyncIterable<string> & {
	[serialized]: TValue;
};

export type BrandSerialized<TType, TValue> = TType & {
	[serialized]: TValue;
};

export type TsonAsyncStringifier = <TValue>(
	value: TValue,
	space?: number,
) => TsonAsyncStringifierIterable<TValue>;
export type TsonAsyncIndex = TsonBranded<number, "AsyncRegistered">;

export interface TsonTransformerSerializeDeserializeAsync<
	TValue,
	TSerializedValue,
> {
	async: true;
	/**
	 * From JSON-serializable value
	 */
	deserialize: (opts: {
		/**
		 * Close the controller for the ReadableStream of values
		 */
		close: () => void;
		/**
		 * Reader for the ReadableStream of values
		 */
		reader: ReadableStreamDefaultReader<
			TSerializedValue | TsonStreamInterruptedError
		>;
	}) => TValue;

	/**
	 * The key to use when serialized
	 */
	key: string;
	/**
	 * JSON-serializable value
	 */
	serializeIterator: (opts: {
		/**
		 * The value we're serializing
		 */
		value: TValue;
	}) => AsyncIterable<TSerializedValue>;
}

export interface TsonAsyncType<TValue, TSerializedValue>
	extends TsonTransformerSerializeDeserializeAsync<TValue, TSerializedValue>,
		TsonTypeTesterCustom {}
export interface TsonAsyncOptions {
	/**
	 * The nonce function every time we start serializing a new object
	 * Should return a unique value every time it's called
	 * @default `${crypto.randomUUID} if available, otherwise a random string generated by Math.random`
	 */
	nonce?: () => number | string;
	/**
	 * Customize a text decoder if your runtime doesn't support the `TextDecoder` API
	 * @default TextDecoder
	 */
	textDecoder?: TextDecoderEsque;

	/**
	 * The list of types to use
	 */
	types: (
		| TsonAsyncType<any, any>
		| TsonType<any, any>
		| TsonType<any, never>
	)[];
}
