import { Buffer } from 'node:buffer';
import { PonyfillIteratorObject } from './IteratorObject.js';
import { PonyfillReadableStream } from './ReadableStream.js';
export class PonyfillFormData {
    map = new Map();
    append(name, value, fileName) {
        let values = this.map.get(name);
        if (!values) {
            values = [];
            this.map.set(name, values);
        }
        const entry = isBlob(value)
            ? getNormalizedFile(name, value, fileName)
            : value;
        values.push(entry);
    }
    delete(name) {
        this.map.delete(name);
    }
    get(name) {
        const values = this.map.get(name);
        return values ? values[0] : null;
    }
    getAll(name) {
        return this.map.get(name) || [];
    }
    has(name) {
        return this.map.has(name);
    }
    set(name, value, fileName) {
        const entry = isBlob(value)
            ? getNormalizedFile(name, value, fileName)
            : value;
        this.map.set(name, [entry]);
    }
    [Symbol.iterator]() {
        return this._entries();
    }
    *_entries() {
        for (const [key, values] of this.map) {
            for (const value of values) {
                yield [key, value];
            }
        }
    }
    entries() {
        return new PonyfillIteratorObject(this._entries(), 'FormDataIterator');
    }
    _keys() {
        return this.map.keys();
    }
    keys() {
        return new PonyfillIteratorObject(this._keys(), 'FormDataIterator');
    }
    *_values() {
        for (const values of this.map.values()) {
            for (const value of values) {
                yield value;
            }
        }
    }
    values() {
        return new PonyfillIteratorObject(this._values(), 'FormDataIterator');
    }
    forEach(callback) {
        for (const [key, value] of this) {
            callback(value, key, this);
        }
    }
}
export function getStreamFromFormData(formData, boundary = '---') {
    let entriesIterator;
    let sentInitialHeader = false;
    let currentAsyncIterator;
    let hasBefore = false;
    function handleNextEntry(controller) {
        const { done, value } = entriesIterator.next();
        if (done) {
            controller.enqueue(Buffer.from(`\r\n--${boundary}--\r\n`));
            return controller.close();
        }
        if (hasBefore) {
            controller.enqueue(Buffer.from(`\r\n--${boundary}\r\n`));
        }
        if (value) {
            const [key, blobOrString] = value;
            if (typeof blobOrString === 'string') {
                controller.enqueue(Buffer.from(`Content-Disposition: form-data; name="${key}"\r\n\r\n`));
                controller.enqueue(Buffer.from(blobOrString));
            }
            else {
                let filenamePart = '';
                if (blobOrString.name) {
                    filenamePart = `; filename="${blobOrString.name}"`;
                }
                controller.enqueue(Buffer.from(`Content-Disposition: form-data; name="${key}"${filenamePart}\r\n`));
                controller.enqueue(Buffer.from(`Content-Type: ${blobOrString.type || 'application/octet-stream'}\r\n\r\n`));
                const entryStream = blobOrString.stream();
                currentAsyncIterator = entryStream[Symbol.asyncIterator]();
            }
            hasBefore = true;
        }
    }
    return new PonyfillReadableStream({
        start: () => {
            entriesIterator = formData.entries();
        },
        pull: controller => {
            if (!sentInitialHeader) {
                sentInitialHeader = true;
                return controller.enqueue(Buffer.from(`--${boundary}\r\n`));
            }
            if (currentAsyncIterator) {
                return currentAsyncIterator.next().then(({ done, value }) => {
                    if (done) {
                        currentAsyncIterator = undefined;
                    }
                    if (value) {
                        return controller.enqueue(value);
                    }
                    else {
                        return handleNextEntry(controller);
                    }
                });
            }
            return handleNextEntry(controller);
        },
        cancel: err => {
            entriesIterator?.return?.(err);
            currentAsyncIterator?.return?.(err);
        },
    });
}
function getNormalizedFile(name, blob, fileName) {
    Object.defineProperty(blob, 'name', {
        configurable: true,
        enumerable: true,
        value: fileName || blob.name || name,
    });
    return blob;
}
function isBlob(value) {
    return value?.arrayBuffer != null;
}
