import { DEFAULT_INITIATIVE } from "./defaults";
import { getEditorConfig } from "../core/schemas/getEditorConfig";
import { cloneObject } from "../util";
import { createInitiative, deleteInitiative, fetchInitiative, updateInitiative, } from "./HubInitiatives";
import { Catalog } from "../search/Catalog";
import { HubItemEntity } from "../core/HubItemEntity";
import { getEntityMetrics } from "../metrics/getEntityMetrics";
import { resolveMetric } from "../metrics/resolveMetric";
import { initiativeToCardModel } from "./view";
import { enrichEntity } from "../core/enrichEntity";
import { metricToEditor } from "../metrics/metricToEditor";
import { getGroup, updateGroup } from "@esri/arcgis-rest-portal";
import { convertGroupToHubGroup } from "../groups/_internal/convertGroupToHubGroup";
import { getEditorSlug } from "../core/_internal/getEditorSlug";
import { convertHubGroupToGroup } from "../groups/_internal/convertHubGroupToGroup";
import { hubItemEntityFromEditor } from "../core/_internal/hubItemEntityFromEditor";
import { getProp } from "../objects/get-prop";
import { getWithDefault } from "../objects/get-with-default";
import { setProp } from "../objects/set-prop";
/**
 * Hub Initiative Class
 */
export class HubInitiative extends HubItemEntity {
    /**
     * Private constructor so we don't have `new` all over the place. Allows for
     * more flexibility in how we create the HubInitiativeManager over time.
     * @param context
     */
    constructor(entity, context) {
        super(entity, context);
        this._catalog = Catalog.fromJson(entity.catalog, this.context);
    }
    /**
     * Catalog instance for this Initiative. Note: Do not hold direct references to this object; always access it from the Initiative.
     * @returns Catalog
     */
    get catalog() {
        return this._catalog;
    }
    /**
     * Create an instance from an IHubInitiative object
     * @param json - JSON object to create a HubInitiative from
     * @param context - ArcGIS context
     * @returns
     */
    static fromJson(json, context) {
        // merge what we have with the default values
        const pojo = this.applyDefaults(json, context);
        return new HubInitiative(pojo, context);
    }
    /**
     * Create a new HubInitiative, returning a HubInitiative instance.
     * Note: This does not persist the Initiative into the backing store
     * @param partialInitiative
     * @param context
     * @returns
     */
    static async create(partialInitiative, context, save = false) {
        const pojo = this.applyDefaults(partialInitiative, context);
        // return an instance of HubInitiative
        const instance = HubInitiative.fromJson(pojo, context);
        if (save) {
            await instance.save();
        }
        return instance;
    }
    /**
     * Fetch an Initiative from the backing store and return a HubInitiative instance.
     * @param identifier - slug or item id
     * @param context
     * @returns
     */
    static async fetch(identifier, context) {
        // fetch by id or slug
        try {
            const entity = await fetchInitiative(identifier, context.requestOptions);
            // create an instance of HubInitiative from the entity
            return HubInitiative.fromJson(entity, context);
        }
        catch (ex) {
            if (ex.message ===
                "CONT_0001: Item does not exist or is inaccessible.") {
                throw new Error(`Initiative not found.`);
            }
            else {
                throw ex;
            }
        }
    }
    static applyDefaults(partialInitiative, context) {
        // ensure we have the orgUrlKey
        if (!partialInitiative.orgUrlKey) {
            partialInitiative.orgUrlKey = context.portal.urlKey;
        }
        // extend the partial over the defaults
        const pojo = Object.assign(Object.assign({}, DEFAULT_INITIATIVE), partialInitiative);
        return pojo;
    }
    /**
     * Apply a new state to the instance
     * @param changes
     */
    update(changes) {
        if (this.isDestroyed) {
            throw new Error("HubInitiative is already destroyed.");
        }
        // merge partial onto existing entity
        this.entity = Object.assign(Object.assign({}, this.entity), changes);
        // update internal instances
        if (changes.catalog) {
            this._catalog = Catalog.fromJson(this.entity.catalog, this.context);
        }
    }
    /**
     * Save the HubInitiative to the backing store.
     * Currently Initiatives are stored as Items in Portal
     * @returns
     */
    async save() {
        if (this.isDestroyed) {
            throw new Error("HubInitiative is already destroyed.");
        }
        // get the catalog, and permission configs
        this.entity.catalog = this._catalog.toJson();
        if (this.entity.id) {
            // update it
            this.entity = await updateInitiative(this.entity, this.context.userRequestOptions);
        }
        else {
            // create it
            this.entity = await createInitiative(this.entity, this.context.userRequestOptions);
        }
        // call the after save hook on superclass
        await super.afterSave();
        return;
    }
    /**
     * Delete the HubInitiative from the store
     * set a flag to indicate that it is destroyed
     * @returns
     */
    async delete() {
        if (this.isDestroyed) {
            throw new Error("HubInitiative is already destroyed.");
        }
        this.isDestroyed = true;
        // Delegate to module fn
        await deleteInitiative(this.entity.id, this.context.userRequestOptions);
    }
    /**
     * Resolve a single metric for this metric
     * @param metricId
     * @returns
     */
    resolveMetric(metricId) {
        const metrics = getEntityMetrics(this.entity);
        const metric = metrics.find((m) => m.id === metricId);
        // TODO: Add caching
        if (metric) {
            return resolveMetric(metric, this.context);
        }
        else {
            throw new Error(`Metric ${metricId} not found.`);
        }
    }
    /**
     * Convert the initiative entity into a card view model that
     * can be consumed by the suite of hub gallery components
     *
     * @param opts view model options
     */
    convertToCardModel(opts) {
        return initiativeToCardModel(this.entity, this.context, opts);
    }
    /*
     * Get a specifc editor config for the HubInitiative entity.
     * @param i18nScope translation scope to be interpolated into the uiSchema
     * @param type editor type - corresonds to the returned uiSchema
     * @param options optional hash of dynamic uiSchema element options
     */
    async getEditorConfig(i18nScope, type) {
        // delegate to the schema subsystem
        return getEditorConfig(i18nScope, type, this.entity, this.context);
    }
    /**
     * Return the initiative as an editor object
     * @param editorContext
     * @returns
     */
    async toEditor(editorContext = {}, include = []) {
        // 1. optionally enrich entity and cast to editor
        const editor = include.length
            ? (await enrichEntity(cloneObject(this.entity), include, this.context.hubRequestOptions))
            : cloneObject(this.entity);
        // 2. handle metrics
        const metrics = getEntityMetrics(this.entity);
        const metric = metrics.find((m) => m.id === editorContext.metricId);
        const displays = getWithDefault(this.entity, "view.metricDisplays", []);
        const displayConfig = displays.find((display) => display.metricId === editorContext.metricId) || {};
        editor._metric = metricToEditor(metric, displayConfig);
        // 3. handle association group
        const assocGroupId = getProp(this.entity, "associations.groupId");
        if (assocGroupId) {
            const associationGroup = await getGroup(assocGroupId, this.context.requestOptions);
            const hubAssociationGroup = convertGroupToHubGroup(associationGroup, null, this.context.userRequestOptions);
            const _associations = {
                groupAccess: hubAssociationGroup.access,
                membershipAccess: hubAssociationGroup.membershipAccess,
            };
            editor._associations = _associations;
        }
        // 4. slug life
        editor._slug = getEditorSlug(this.entity);
        return editor;
    }
    /**
     * Load the initiative from the editor object
     * @param editor
     * @returns
     */
    async fromEditor(editor) {
        var _a;
        const _associations = editor._associations;
        delete editor._associations;
        // 1. delegate to an item-specific fromEditor util to
        // handle shared "fromEditor" logic
        const res = await hubItemEntityFromEditor(editor, this.context);
        const initiative = res.entity;
        // iterate over the res object keys and set the values
        // on the HubInitiative instance
        Object.entries(res).forEach(([key, value]) => {
            setProp(key, value, this);
        });
        // 2. handle initiative-specific operations
        // a. handle association group settings
        const assocGroupId = (_a = initiative.associations) === null || _a === void 0 ? void 0 : _a.groupId;
        if (assocGroupId && _associations) {
            const associationGroup = convertHubGroupToGroup(_associations);
            // handle group access
            if (_associations.groupAccess) {
                await updateGroup({
                    group: {
                        id: assocGroupId,
                        access: _associations.groupAccess,
                    },
                    authentication: this.context.hubRequestOptions.authentication,
                });
            }
            // handle membership access
            if (_associations.membershipAccess) {
                await updateGroup({
                    group: {
                        id: assocGroupId,
                        membershipAccess: associationGroup.membershipAccess,
                        clearEmptyFields: true,
                    },
                    authentication: this.context.hubRequestOptions.authentication,
                });
            }
        }
        // save or create the entity
        this.entity = initiative;
        await this.save();
        return this.entity;
    }
}
//# sourceMappingURL=HubInitiative.js.map