import { __awaiter } from "tslib";
//TODO: make no chace dynamic, observable
/**
 * Service used for loading dynamic items
 */
export class DynamicItemLoader {
    //######################### constructors #########################
    constructor(providers, extractors, validatorFn, logger, noCache) {
        var _a, _b;
        this.providers = providers;
        this.extractors = extractors;
        this.validatorFn = validatorFn;
        this.logger = logger;
        this.noCache = noCache;
        //######################### protected cache #########################
        /**
         * Cached dynamic items definition
         */
        this.cachedDynamicItems = {};
        //providers is not an array
        if (!Array.isArray(this.providers)) {
            (_a = this.logger) === null || _a === void 0 ? void 0 : _a.error('DynamicItemLoader: missing providers!');
            this.providers = [];
        }
        //extractors is not an array
        if (!Array.isArray(this.extractors)) {
            (_b = this.logger) === null || _b === void 0 ? void 0 : _b.error('DynamicItemLoader: missing extractors!');
            this.extractors = [];
        }
    }
    //######################### public methods #########################
    /**
     * Loads dynamic item, or null if not found
     * @param source - Definition of source for dynamic item
     */
    loadItem(source) {
        var _a, _b, _c, _d;
        return __awaiter(this, void 0, void 0, function* () {
            let dynamicModule = null;
            const cacheId = `${source.package}-${source.name}`;
            //try to get from cache
            if (cacheId in this.cachedDynamicItems && !this.noCache) {
                (_a = this.logger) === null || _a === void 0 ? void 0 : _a.verbose('DynamicItemLoader: Loading from cache {{@source}}', { source: { name: source.name, package: source.package } });
                return this.cachedDynamicItems[cacheId];
            }
            //loops all providers, return result from first that returns non null value
            for (const provider of this.providers) {
                const asyncDynamicModule = provider.tryToGet(source);
                if (asyncDynamicModule) {
                    dynamicModule = yield asyncDynamicModule;
                }
                else {
                    dynamicModule = null;
                }
                if (dynamicModule) {
                    break;
                }
            }
            //no module found
            if (!dynamicModule) {
                (_b = this.logger) === null || _b === void 0 ? void 0 : _b.debug('DynamicItemLoader: Failed to get dynamic module {{@source}}', { source: { name: source.name, package: source.package } });
                return null;
            }
            //loops all extractors, return result from first that returns non null value
            for (const extractor of this.extractors) {
                const dynamicItem = yield extractor.tryToExtract(dynamicModule);
                if (dynamicItem) {
                    this.cachedDynamicItems[cacheId] = this.validatorFn(dynamicItem) ? dynamicItem : null;
                    if (!this.cachedDynamicItems[cacheId]) {
                        (_c = this.logger) === null || _c === void 0 ? void 0 : _c.warn('DynamicItemLoader: Found dynamic item {{@source}} is not of requested type', { source: { name: source.name, package: source.package } });
                    }
                    return this.cachedDynamicItems[cacheId];
                }
            }
            (_d = this.logger) === null || _d === void 0 ? void 0 : _d.debug('DynamicItemLoader: Failed to extract dynamic item {{@source}}', { source: { name: source.name, package: source.package } });
            this.cachedDynamicItems[cacheId] = null;
            return null;
        });
    }
}
//# sourceMappingURL=dynamicItemLoader.service.js.map