var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { generateCharacterState } from '@dndbeyond/character-gfs';
import { ApiRequests } from "../api";
import { ApiAdapterUtils } from "../apiAdapter";
import { getCurrentRulesEngineConfig } from "../config/utils";
import { HelperUtils } from "../engine/Helper";
import { apiCreatorSelectors, rulesEngineSelectors } from "../selectors";
import { TypeScriptUtils } from "../utils";
import { FeatureManager } from './FeatureManager';
import { MessageManager } from './MessageManager';
export const featureMap = new Map();
export const subscriptionsMap = new Map();
const featureManagerMap = new Map();
export const getFeatureManager = (params) => {
    const { feature } = params;
    const featureId = feature.featureId; //TODO GFS Accessors?
    if (featureManagerMap.has(featureId)) {
        const FeatureManager = featureManagerMap.get(featureId);
        if (!FeatureManager) {
            throw new Error(`FeatureManager for feature ${featureId} is null`);
        }
        if (FeatureManager.params.feature === feature) {
            return FeatureManager;
        }
    }
    const newFeatureManager = new FeatureManager(params);
    featureManagerMap.set(featureId, newFeatureManager);
    return newFeatureManager;
};
// we want this cached across all instances of the FeaturesManager
let rootCharacterFeatureManagers = null; // needs
let allCharacterFeatureManagers = null; // needs
let availableFeaturesResponseData = null; // not required to bust
export class FeaturesManager extends MessageManager {
    constructor(params = {}) {
        super(params);
        // these values are used for caching and may need to be busted when the character changes
        this.randomId = 0; // helpful for debugging
        this.runSubscriptions = () => {
            subscriptionsMap.forEach((cb) => cb());
        };
        this.transformLoadedFeatures = (features) => {
            return features.map((feature) => {
                featureMap.set(feature.featureId, feature);
                return getFeatureManager({ feature, runSubscriptions: this.runSubscriptions, bustCache: this.bustCache });
            });
        };
        this.getAvailableFeatures = (additionalConfig) => __awaiter(this, void 0, void 0, function* () {
            var _a;
            if (availableFeaturesResponseData) {
                return availableFeaturesResponseData;
            }
            const loadFeatures = apiCreatorSelectors.makeLoadAvailableFeatures(this.state);
            const response = yield loadFeatures(additionalConfig);
            availableFeaturesResponseData = (_a = ApiAdapterUtils.getResponseData(response)) !== null && _a !== void 0 ? _a : [];
            availableFeaturesResponseData.map((feature) => {
                featureMap.set(feature.featureId, feature);
            });
            return availableFeaturesResponseData;
        });
        this.getFeaturesShoppe = (additionalConfig) => __awaiter(this, void 0, void 0, function* () {
            let data = yield this.getAvailableFeatures(additionalConfig);
            if (data) {
                return this.transformLoadedFeatures(data);
            }
            return [];
        });
        this.getAllCharacterFeatures = (rootCharacterFeatureIds) => __awaiter(this, void 0, void 0, function* () {
            var _b;
            if (allCharacterFeatureManagers) {
                return allCharacterFeatureManagers;
            }
            const allFeaturesAndSubFeatureResponse = yield ApiRequests.postFeaturesAndSubfeatures({
                featureIds: rootCharacterFeatureIds,
            });
            const allFeaturesAndSubFeatureData = (_b = ApiAdapterUtils.getResponseData(allFeaturesAndSubFeatureResponse)) !== null && _b !== void 0 ? _b : [];
            allFeaturesAndSubFeatureData.map((feature) => {
                featureMap.set(feature.featureId, feature);
            });
            const allFeaturesAndSubFeatureIds = allFeaturesAndSubFeatureData.map((feature) => feature.featureId);
            allCharacterFeatureManagers = allFeaturesAndSubFeatureIds
                .map((id) => {
                const feature = featureMap.get(id);
                return feature
                    ? getFeatureManager({ feature, runSubscriptions: this.runSubscriptions, bustCache: this.bustCache })
                    : null;
            })
                .filter(TypeScriptUtils.isNotNullOrUndefined);
            return allCharacterFeatureManagers || [];
        });
        this.bustCache = () => __awaiter(this, void 0, void 0, function* () {
            availableFeaturesResponseData = null;
            rootCharacterFeatureManagers = null;
            allCharacterFeatureManagers = null;
        });
        this.getCharacterFeatures = () => __awaiter(this, void 0, void 0, function* () {
            var _c;
            if (rootCharacterFeatureManagers) {
                return rootCharacterFeatureManagers;
            }
            const characterId = rulesEngineSelectors.getId(this.state);
            const characterFeatureResponse = yield ApiRequests.getCharacterFeatures({ characterId });
            const characterFeatureData = (_c = ApiAdapterUtils.getResponseData(characterFeatureResponse)) !== null && _c !== void 0 ? _c : [];
            const characterFeatureIds = characterFeatureData.map((feature) => feature.id);
            if (characterFeatureIds.length > 0) {
                yield this.getAvailableFeatures({
                    params: {
                        category: 'blessing',
                    },
                });
                yield this.getAllCharacterFeatures(characterFeatureIds);
            }
            rootCharacterFeatureManagers = characterFeatureIds
                .map((id) => {
                const feature = featureMap.get(id);
                return feature
                    ? getFeatureManager({ feature, runSubscriptions: this.runSubscriptions, bustCache: this.bustCache })
                    : null;
            })
                .filter(TypeScriptUtils.isNotNullOrUndefined);
            return rootCharacterFeatureManagers;
        });
        this.processCharacter = (generatedFeature) => {
            featureMap.set(generatedFeature.featureId, generatedFeature);
            return generateCharacterState({
                rootFeatureIds: [
                    generatedFeature.featureId,
                    ...((rootCharacterFeatureManagers === null || rootCharacterFeatureManagers === void 0 ? void 0 : rootCharacterFeatureManagers.map((featureManager) => featureManager.getId())) || []),
                ],
                featureMap,
            });
        };
        this.randomId = Math.floor(Math.random() * 1000000);
    }
}
// manage subscriptions
FeaturesManager.subscribeToUpdates = ({ onUpdate, shouldInit = true }) => {
    // TODO: maybe from lib.
    const mapKey = HelperUtils.generateGuid();
    // connect to redux for updates
    let unsubscribe;
    if (getCurrentRulesEngineConfig().store) {
        const store = getCurrentRulesEngineConfig().store;
        unsubscribe = store === null || store === void 0 ? void 0 : store.subscribe(() => {
            onUpdate();
        });
    }
    subscriptionsMap.set(mapKey, onUpdate);
    if (shouldInit) {
        onUpdate();
    }
    return () => {
        subscriptionsMap.delete(mapKey);
        if (unsubscribe) {
            unsubscribe();
        }
    };
};
