"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SUPPORTED_SPEC_VERSION = void 0;
const events_1 = require("events");
const events_2 = require("../events");
const adaptive_fetcher_1 = require("./adaptive-fetcher");
exports.SUPPORTED_SPEC_VERSION = '5.2.0';
class Repository extends events_1.EventEmitter {
    // Etag property for backward compatibility
    get etag() {
        var _a, _b;
        return ((_b = (_a = this.fetcher).getEtag) === null || _b === void 0 ? void 0 : _b.call(_a)) || undefined;
    }
    set etag(value) {
        var _a, _b;
        (_b = (_a = this.fetcher).setEtag) === null || _b === void 0 ? void 0 : _b.call(_a, value);
    }
    constructor({ url, appName, instanceId, connectionId, projectName, refreshInterval = 15000, timeout, headers, customHeadersFunction, httpOptions, namePrefix, tags, bootstrapProvider, bootstrapOverride = true, storageProvider, eventSource, mode, }) {
        super();
        this.ready = false;
        this.connected = false;
        this.stopped = false;
        this.data = {};
        this.enhanceStrategies = (strategies) => {
            return strategies === null || strategies === void 0 ? void 0 : strategies.map((strategy) => {
                const { segments, ...restOfStrategy } = strategy;
                const enhancedSegments = segments === null || segments === void 0 ? void 0 : segments.map((segment) => this.getSegment(segment));
                return { ...restOfStrategy, segments: enhancedSegments };
            });
        };
        this.appName = appName;
        this.url = url;
        this.projectName = projectName;
        this.bootstrapProvider = bootstrapProvider;
        this.bootstrapOverride = bootstrapOverride;
        this.storageProvider = storageProvider;
        this.segments = new Map();
        this.fetcher = new adaptive_fetcher_1.AdaptiveFetcher({
            url,
            appName,
            instanceId,
            connectionId,
            refreshInterval,
            timeout,
            headers,
            customHeadersFunction,
            httpOptions,
            namePrefix,
            tags,
            projectName,
            mode,
            eventSource,
            onSave: this.save.bind(this),
            onSaveDelta: this.saveDelta.bind(this),
        });
        this.setupFetchingStrategyEvents();
    }
    setupFetchingStrategyEvents() {
        this.fetcher.on(events_2.UnleashEvents.Error, (err) => this.emit(events_2.UnleashEvents.Error, err));
        this.fetcher.on(events_2.UnleashEvents.Warn, (msg) => this.emit(events_2.UnleashEvents.Warn, msg));
        this.fetcher.on(events_2.UnleashEvents.Unchanged, () => this.emit(events_2.UnleashEvents.Unchanged));
        this.fetcher.on(events_2.UnleashEvents.Mode, (data) => this.emit(events_2.UnleashEvents.Mode, data));
    }
    validateFeature(feature) {
        const errors = [];
        if (!Array.isArray(feature.strategies)) {
            errors.push(`feature.strategies should be an array, but was ${typeof feature.strategies}`);
        }
        if (feature.variants && !Array.isArray(feature.variants)) {
            errors.push(`feature.variants should be an array, but was ${typeof feature.variants}`);
        }
        if (typeof feature.enabled !== 'boolean') {
            errors.push(`feature.enabled should be an boolean, but was ${typeof feature.enabled}`);
        }
        if (errors.length > 0) {
            const err = new Error(errors.join(', '));
            this.emit(events_2.UnleashEvents.Error, err);
        }
    }
    async start() {
        await Promise.all([this.fetcher.start(), this.loadBackup(), this.loadBootstrap()]);
    }
    async loadBackup() {
        try {
            const content = await this.storageProvider.get(this.appName);
            if (this.ready) {
                return;
            }
            if (content && this.notEmpty(content)) {
                this.data = this.convertToMap(content.features);
                this.segments = this.createSegmentLookup(content.segments);
                this.setReady();
            }
        }
        catch (err) {
            this.emit(events_2.UnleashEvents.Warn, err);
        }
    }
    setReady() {
        const doEmitReady = this.ready === false;
        this.ready = true;
        if (doEmitReady) {
            process.nextTick(() => {
                this.emit(events_2.UnleashEvents.Ready);
            });
        }
    }
    createSegmentLookup(segments) {
        if (!segments) {
            return new Map();
        }
        return new Map(segments.map((segment) => [segment.id, segment]));
    }
    async save(response, fromApi) {
        if (this.stopped) {
            return;
        }
        if (fromApi) {
            this.connected = true;
            this.data = this.convertToMap(response.features);
            this.segments = this.createSegmentLookup(response.segments);
        }
        else if (!this.connected) {
            // Only allow bootstrap if not connected
            this.data = this.convertToMap(response.features);
            this.segments = this.createSegmentLookup(response.segments);
        }
        this.setReady();
        this.emit(events_2.UnleashEvents.Changed, [...response.features]);
        await this.storageProvider.set(this.appName, response);
    }
    async saveDelta(delta) {
        if (this.stopped) {
            return;
        }
        this.connected = true;
        delta.events.forEach((event) => {
            if (event.type === 'feature-updated') {
                this.data[event.feature.name] = event.feature;
            }
            else if (event.type === 'feature-removed') {
                delete this.data[event.featureName];
            }
            else if (event.type === 'segment-updated') {
                this.segments.set(event.segment.id, event.segment);
            }
            else if (event.type === 'segment-removed') {
                this.segments.delete(event.segmentId);
            }
            else if (event.type === 'hydration') {
                this.data = this.convertToMap(event.features);
                this.segments = this.createSegmentLookup(event.segments);
            }
        });
        this.setReady();
        this.emit(events_2.UnleashEvents.Changed, Object.values(this.data));
        await this.storageProvider.set(this.appName, {
            features: Object.values(this.data),
            segments: [...this.segments.values()],
            version: 0,
        });
    }
    notEmpty(content) {
        return content.features.length > 0;
    }
    async loadBootstrap() {
        try {
            const content = await this.bootstrapProvider.readBootstrap();
            if (!this.bootstrapOverride && this.ready) {
                // early exit if we already have backup data and should not override it.
                return;
            }
            if (content && this.notEmpty(content)) {
                await this.save(content, false);
            }
        }
        catch (err) {
            this.emit(events_2.UnleashEvents.Warn, `Unleash SDK was unable to load bootstrap.
Message: ${err.message}`);
        }
    }
    convertToMap(features) {
        const obj = features.reduce((o, feature) => {
            const a = { ...o };
            this.validateFeature(feature);
            a[feature.name] = feature;
            return a;
        }, {});
        return obj;
    }
    stop() {
        this.stopped = true;
        this.fetcher.stop();
        this.removeAllListeners();
    }
    getSegment(segmentId) {
        return this.segments.get(segmentId);
    }
    getToggle(name) {
        return this.data[name];
    }
    getToggles() {
        return Object.keys(this.data).map((key) => this.data[key]);
    }
    getTogglesWithSegmentData() {
        const toggles = this.getToggles();
        return toggles.map((toggle) => {
            const { strategies, ...restOfToggle } = toggle;
            return { ...restOfToggle, strategies: this.enhanceStrategies(strategies) };
        });
    }
    getMode() {
        return this.fetcher.getMode();
    }
    async setMode(mode) {
        await this.fetcher.setMode(mode);
    }
    // Compatibility methods for tests - delegate to fetching strategy
    getFailures() {
        return this.fetcher.getFailures();
    }
    nextFetch() {
        return this.fetcher.nextFetch();
    }
    async fetch() {
        return this.fetcher.fetch();
    }
}
exports.default = Repository;//# sourceMappingURL=https://main.vscode-cdn.net/sourcemaps/e0b7a2bcf575cfba10528c4e7c10bd3ce2d7769a/node_modules/unleash-client/lib/repository/index.js.map