// #region Imports
import { AsyncDirective as LitAsyncDirective, directive } from 'lit/async-directive.js';
// #endregion
export function isPromise(obj) {
    // allow any Promise/A+ compliant thenable.
    // It's up to the caller to ensure that obj.then conforms to the spec
    return obj && typeof obj.then === 'function';
}
export function isSubscribable(obj) {
    return obj && typeof obj.subscribe === 'function';
}
class SubscribableStrategy {
    createSubscription(async, updateLatestValue) {
        // Subscription can be side-effectful, and we don't want any signal reads which happen in the
        // side effect of the subscription to be tracked by a component's template when that
        // subscription is triggered via the async pipe. So we wrap the subscription in `untracked` to
        // decouple from the current reactive context.
        //
        // `untracked` also prevents signal _writes_ which happen in the subscription side effect from
        // being treated as signal writes during the template evaluation (which throws errors).
        return async.subscribe({
            next: updateLatestValue,
            error: (e) => {
                throw e;
            }
        });
    }
    dispose(subscription) {
        // See the comment in `createSubscription` above on the use of `untracked`.
        subscription.unsubscribe();
    }
}
class PromiseStrategy {
    createSubscription(async, updateLatestValue) {
        return async.then(updateLatestValue, (e) => {
            throw e;
        });
    }
    dispose(_subscription) { }
}
const PROMISE_STRATEGY = new PromiseStrategy();
const SUBSCRIBABLE_STRATEGY = new SubscribableStrategy();
// ---------------------------
class AsyncDirective extends LitAsyncDirective {
    // #region Fields
    _latestValue = null;
    _subscription = null;
    _obj = null;
    _strategy = null;
    // #endregion
    // #region Ctor
    /**
     * Constructs a new instance of the `AsyncDirective` class.
     *
     * @public
     */
    constructor(partInfo) {
        super(partInfo);
        this._latestValue = null;
    }
    // #endregion
    render(observable) {
        if (this.isConnected) {
            this.async(observable);
        }
        return this._latestValue;
    }
    disconnected() {
        if (this._subscription) {
            this.dispose();
        }
    }
    reconnected() {
        //     this.async(this._observable);
        console.warn('############ reconnected');
    }
    async(obj) {
        if (!this._obj) {
            if (obj) {
                this._obj = obj;
                this._strategy = this.selectStrategy(obj);
                this._subscription = this._strategy.createSubscription(obj, (value) => this.updateLatestValue(obj, value));
            }
            this.setValue(this._latestValue);
        }
        if (obj !== this._obj) {
            this.dispose();
            this.async(obj);
            return;
        }
        this.setValue(this._latestValue);
    }
    selectStrategy(obj) {
        if (isPromise(obj)) {
            return PROMISE_STRATEGY;
        }
        if (isSubscribable(obj)) {
            return SUBSCRIBABLE_STRATEGY;
        }
        throw new Error('Invalid argument');
    }
    dispose() {
        if (this._strategy && this._subscription) {
            this._strategy.dispose(this._subscription);
        }
        this._latestValue = null;
        this._subscription = null;
        this._obj = null;
    }
    updateLatestValue(async, value) {
        if (async === this._obj) {
            this._latestValue = value;
        }
    }
}
export const async = directive(AsyncDirective);
//# sourceMappingURL=AsyncDirective.js.map