"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HubItemEntity = void 0;
const arcgis_rest_portal_1 = require("@esri/arcgis-rest-portal");
const HubError_1 = require("../HubError");
const uploadImageResource_1 = require("../items/uploadImageResource");
const unshare_item_from_groups_1 = require("../items/unshare-item-from-groups");
const share_item_to_groups_1 = require("../items/share-item-to-groups");
const deleteItemThumbnail_1 = require("../items/deleteItemThumbnail");
const setItemThumbnail_1 = require("../items/setItemThumbnail");
const util_1 = require("../util");
const sharedWith_1 = require("./_internal/sharedWith");
const utils_1 = require("../discussions/utils");
const checkPermission_1 = require("../permissions/checkPermission");
const utils_2 = require("../permissions/utils");
const get_item_thumbnail_url_1 = require("../resources/get-item-thumbnail-url");
const FEATURED_IMAGE_FILENAME = "featuredImage.png";
/**
 * Base class for all Hub Entities backed by items
 */
class HubItemEntity {
    constructor(entity, context) {
        this.isDestroyed = false;
        this.thumbnailCache = null;
        this.context = context;
        this.entity = entity;
    }
    /**
     * Check if current user has a specific permission, accounting for
     * both system and entity level policies
     * @param permission
     * @returns
     */
    checkPermission(permission) {
        return (0, checkPermission_1.checkPermission)(permission, this.context, this.entity);
    }
    /**
     * Get all policies related to a specific permission
     * @param permission
     * @returns
     */
    getPermissionPolicies(permission) {
        const permissions = this.entity.permissions || [];
        return permissions.filter((p) => p.permission === permission);
    }
    /**
     * Add a policy to the entity
     * @param policy
     */
    addPermissionPolicy(policy) {
        this.entity.permissions = (0, utils_2.addPermissionPolicy)(this.entity.permissions, policy);
    }
    /**
     * Remove a policy from the entity
     * @param permission
     * @param id
     */
    removePermissionPolicy(permission, id) {
        this.entity.permissions = (0, utils_2.removePermissionPolicy)(this.entity.permissions, permission, id);
    }
    // Although we don't expose all the properties, we do expose a few for convenience
    /**
     * Return the entity id
     */
    get id() {
        return this.entity.id;
    }
    /**
     * Return the entity owner
     */
    get owner() {
        return this.entity.owner;
    }
    //#region IWithStoreBehavior
    /**
     * Return the backing entity as an object literal
     * @returns
     */
    toJson() {
        if (this.isDestroyed) {
            throw new Error("Entity is already destroyed.");
        }
        return (0, util_1.cloneObject)(this.entity);
    }
    /**
     * Can the current user edit the Entity?
     * In order to edit an item, the user must be the owner of the item
     * or be a member of a shared editing group, to which the item is shared.
     * @returns
     */
    get canEdit() {
        return this.entity.canEdit;
    }
    /**
     * Can the current user delete the Entity?
     * In order to delete an item, the user must be the owner of the item or a full org_admin
     * in the owner's organization.
     * @returns
     */
    get canDelete() {
        return this.entity.canDelete;
    }
    /**
     * The orgId of the Entity, if available
     * @returns the entity orgId, when available
     */
    get orgId() {
        return this.entity.orgId;
    }
    //#endregion IWithStoreBehavior
    //#region IWithSharingBehavior
    /**
     * Share the Entity with the specified group id
     * @param groupId
     */
    async shareWithGroup(groupId) {
        if (!this.context.currentUser) {
            throw new HubError_1.default("Share Item With Group", "Cannot share item with group when no user is logged in.");
        }
        await (0, share_item_to_groups_1.shareItemToGroups)(this.entity.id, [groupId], this.context.requestOptions, this.entity.owner);
    }
    /**
     * Share the Entity with the specified group ids
     * @param groupIds
     */
    async shareWithGroups(groupIds) {
        await (0, share_item_to_groups_1.shareItemToGroups)(this.entity.id, groupIds, this.context.requestOptions, this.entity.owner);
    }
    /**
     * Unshare the Entity with the specified group id
     * @param groupId
     */
    async unshareWithGroup(groupId) {
        await (0, unshare_item_from_groups_1.unshareItemFromGroups)(this.entity.id, [groupId], this.context.requestOptions, this.entity.owner);
    }
    /**
     * Unshare the Entity with the specified group ids
     * @param groupIds
     */
    async unshareWithGroups(groupIds) {
        await (0, unshare_item_from_groups_1.unshareItemFromGroups)(this.entity.id, groupIds, this.context.requestOptions, this.entity.owner);
    }
    /**
     * Set the access level of the backing item
     * @param access
     */
    async setAccess(access) {
        await (0, arcgis_rest_portal_1.setItemAccess)({
            id: this.entity.id,
            access,
            owner: this.entity.owner,
            authentication: this.context.session,
        });
        // if this succeeded, update the entity
        this.entity.access = access;
    }
    /**
     * Returns the followers group
     */
    async getFollowersGroup() {
        let group;
        try {
            if (this.entity.followersGroupId) {
                group = await (0, arcgis_rest_portal_1.getGroup)(this.entity.followersGroupId, this.context.userRequestOptions);
            }
        }
        catch (_error) {
            group = null;
        }
        return group;
    }
    /**
     * Sets the access level of the followers group
     * @param access
     */
    async setFollowersGroupAccess(access) {
        await (0, arcgis_rest_portal_1.updateGroup)({
            group: {
                id: this.entity.followersGroupId,
                access,
            },
            authentication: this.context.session,
        });
    }
    /**
     * Sets whether or not the followers group is discussable
     * @param isDiscussable
     */
    async setFollowersGroupIsDiscussable(isDiscussable) {
        const group = await this.getFollowersGroup();
        const typeKeywords = (0, utils_1.setDiscussableKeyword)(group.typeKeywords, isDiscussable);
        await (0, arcgis_rest_portal_1.updateGroup)({
            group: {
                id: group.id,
                typeKeywords,
            },
            authentication: this.context.session,
        });
    }
    /**
     * Return a list of groups the Entity is shared to.
     * @returns
     */
    async sharedWith() {
        // delegate to a util that merges the three arrays returned from the api, into a single array
        return (0, sharedWith_1.sharedWith)(this.entity.id, this.context.requestOptions);
    }
    //#endregion
    /**
     * Hook that subclasses should call to invoke shared post-save behavior
     */
    async afterSave() {
        // Handle Thumbnails
        // check if there is a thumbnail in the cache
        // if we're not making changes to the thumbnail, this prop will not be defined
        if (this.thumbnailCache) {
            if (this.thumbnailCache.clear) {
                await (0, deleteItemThumbnail_1.deleteItemThumbnail)(this.entity.id, this.entity.owner, this.context.userRequestOptions);
                this.thumbnailCache = null;
                this.entity.thumbnail = null;
                this.entity.thumbnailUrl = null;
            }
            else {
                // save the thumbnail
                await (0, setItemThumbnail_1.setItemThumbnail)(this.entity.id, this.thumbnailCache.file, this.thumbnailCache.filename, this.context.userRequestOptions, this.entity.owner);
                // Note: updating the thumbnail alone does not update the modified date of the item
                // thus we can just set props on the entity w/o re-fetching
                this.entity.thumbnail = `thumbnail/${this.thumbnailCache.filename}`;
                this.entity.thumbnailUrl = this.getThumbnailUrl();
                // clear the thumbnail cache
                this.thumbnailCache = null;
            }
        }
    }
    //#region IWithThumbnailBehavior
    /**
     * Store thumbnail information to be sent with the next `.save()` call
     * @param file
     * @param filename
     */
    setThumbnail(file, filename) {
        // subclass is responsible for handling the implementation during the `.save()` call
        this.thumbnailCache = { file, filename };
    }
    /**
     * Clear the thumbnail from the item, if one exists. Persisted on next `.save()` call
     */
    clearThumbnail() {
        this.thumbnailCache = { clear: true };
    }
    /**
     * Return the full url to the thumbnail, optionally with a width parameter
     * @param width
     */
    getThumbnailUrl(width = 200) {
        const minimalItem = {
            id: this.entity.id,
            access: this.entity.access,
            thumbnail: this.entity.thumbnail,
        };
        const opts = {
            token: this.context.session.token,
            width,
        };
        return (0, get_item_thumbnail_url_1.getItemThumbnailUrl)(minimalItem, this.context.requestOptions, opts);
    }
    //#endregion IWithThumbnailBehavior
    //#region IWithFeaturedImageBehavior
    /**
     * Set a featured image on the Entity, if one already exists it is cleared out before the new one is set
     * to keep the number of resources in control
     * @param file
     */
    async setFeaturedImage(file, clearExisting = false) {
        var _a;
        try {
            // If we have a featured image then clear it out.
            if (((_a = this.entity.view) === null || _a === void 0 ? void 0 : _a.featuredImageUrl) || clearExisting) {
                await this.clearFeaturedImage();
            }
            // add the new featured image
            const featuredImageUrl = await (0, uploadImageResource_1.uploadImageResource)(this.entity.id, this.entity.owner, file, FEATURED_IMAGE_FILENAME, this.context.userRequestOptions);
            // If successful, update the entity
            this.entity.view = Object.assign(Object.assign({}, this.entity.view), { featuredImageUrl });
            // save the entity
            await this.save();
        }
        catch (err) {
            // If the featured image url has been cleared, but the resource hasn't
            // Been removed then we'll get the following error message.
            // In that case, we'll try again with clearExisting set to true.
            if (err instanceof Error &&
                err.message === "CONT_00942: Resource already present") {
                return this.setFeaturedImage(file, true);
            }
            else {
                throw err;
            }
        }
    }
    /**
     * Remove the featured image from the item
     */
    async clearFeaturedImage() {
        try {
            // remove the resource
            const response = await (0, arcgis_rest_portal_1.removeItemResource)(Object.assign({ id: this.entity.id, owner: this.entity.owner, resource: FEATURED_IMAGE_FILENAME }, this.context.userRequestOptions));
            // if not successful throw an error
            if (response && !response.success) {
                throw new HubError_1.default("Clear Item Featured Image", "Unknown error clearing featured image.");
            }
            // If successful, clear the featured image url
            this.entity.view.featuredImageUrl = null;
            // save the entity
            await this.save();
        }
        catch (err) {
            if (err instanceof Error) {
                throw new HubError_1.default("Clear Item Featured Image", err.message, err);
            }
            else {
                throw new HubError_1.default("Clear Item Featured Image", "Unknown error clearing featured image.");
            }
        }
    }
    //#endregion IWithFeaturedImageBehavior
    /**
     * Updates the isDiscussable property
     * @param isDiscussable whether to enable or disable discussions
     */
    updateIsDiscussable(isDiscussable) {
        const typeKeywords = (0, utils_1.setDiscussableKeyword)(this.entity.typeKeywords, isDiscussable);
        this.update({ typeKeywords, isDiscussable });
    }
}
exports.HubItemEntity = HubItemEntity;
//# sourceMappingURL=HubItemEntity.js.map