/**
 * @module ext/polyfills.js
 * @author Krzyszot Putyra
 * @date Apr 01, 2025
 * @description Polyfills for ES2025 functions required by this package
 * 
 * Set:
 * - isSubsetOf @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/isSubsetOf}
 * - isSupersetOf @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/isSupersetOf}
 * 
 * Promise:
 * - withResolvers @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers}
 */


if (!Set.prototype.isSubsetOf) {

    Object.defineProperties(Set.prototype, {
        isSubsetOf: {
            enumerable: false,
            configurable: true,
            value(s) {
                for(const x of this)
                    if (!s.has(x)) return false;
                return true;
            }
        },
        isSupersetOf: {
            enumerable: false,
            configurable: true,
            value(s) {
                for(const x of s)
                    if (!this.has(x)) return false;
                return true;
            }
        }
    })
}

if (!Promise.withResolvers) {

    Object.defineProperty(Promise, 'withResorvers', {
        enumerable: false,
        configurable: true,
        value() {
            const descriptor = {};
            descriptor.promise = new Promise((res, rej) => {
                descriptor.resolve = res;
                descriptor.reject = rej;
            });
            return descriptor;
        }
    })

}

// Older versions of EventTarget does not handle signals

const signalNotRespected = (function() {

    // We are not going to implement the entire Abort API
    if (typeof AbortController === 'undefined') {
        return false;
    }

    const target = new EventTarget();
    const controller = new AbortController();
    let executed = false;

    target.addEventListener('test', evt => { executed = true; }, { signal: controller.signal });
    controller.abort();
    target.dispatchEvent(new Event('test'));

    return executed;
}());

if (signalNotRespected) {

    const addEventListener = EventTarget.prototype.addEventListener;
    Object.defineProperty(EventTarget.prototype, 'addEventListener', {
        enumerable: false,
        configurable: true,
        value(type, handler, options={}) {
            const { signal=null, ...otherOptions } = options;
            if (signal && signal instanceof AbortSignal) {
                if (signal.aborted) return;
                const cleanUp = () => {
                    this.removeEventListener(type, handler, otherOptions);
                    signal.removeEventListener('abort', cleanUp);
                }
                signal.addEventListener('abort', cleanUp, { once: true });
            }
            addEventListener.bind(this)(type, handler, otherOptions);
        }
    });
    
}
