"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserApi = void 0;
var tslib_1 = require("tslib");
var cache_decorators_1 = require("@d-fischer/cache-decorators");
var shared_utils_1 = require("@d-fischer/shared-utils");
var twitch_api_call_1 = require("twitch-api-call");
var HellFreezesOverError_1 = require("../../../Errors/HellFreezesOverError");
var NoSubscriptionProgramError_1 = require("../../../Errors/NoSubscriptionProgramError");
var UserTools_1 = require("../../../Toolkit/UserTools");
var BaseApi_1 = require("../../BaseApi");
var EmoteSetList_1 = require("../Channel/EmoteSetList");
var PrivilegedUser_1 = require("./PrivilegedUser");
var User_1 = require("./User");
var UserBlock_1 = require("./UserBlock");
var UserChatInfo_1 = require("./UserChatInfo");
var UserFollow_1 = require("./UserFollow");
var UserSubscription_1 = require("./UserSubscription");
/**
 * The API methods that deal with users.
 *
 * Can be accessed using `client.kraken.users` on an {@ApiClient} instance.
 *
 * ## Example
 * ```ts
 * const api = new ApiClient(new StaticAuthProvider(clientId, accessToken));
 * const user = await api.kraken.users.getUser('125328655');
 * ```
 */
var UserApi = /** @class */ (function (_super) {
    tslib_1.__extends(UserApi, _super);
    function UserApi() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this._userByNameCache = new Map();
        return _this;
    }
    /**
     * Retrieves the user data of the currently authenticated user.
     */
    UserApi.prototype.getMe = function () {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var _a;
            return tslib_1.__generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        _a = PrivilegedUser_1.PrivilegedUser.bind;
                        return [4 /*yield*/, this._client.callApi({ url: 'user', scope: 'user_read' })];
                    case 1: return [2 /*return*/, new (_a.apply(PrivilegedUser_1.PrivilegedUser, [void 0, _b.sent(), this._client]))()];
                }
            });
        });
    };
    /**
     * Retrieves the user data for the given user ID.
     *
     * @param userId The user ID you want to look up.
     */
    UserApi.prototype.getUser = function (userId) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var userData;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0: return [4 /*yield*/, this._client.callApi({ url: "users/" + UserTools_1.extractUserId(userId) })];
                    case 1:
                        userData = _a.sent();
                        if (!userData) {
                            throw new HellFreezesOverError_1.HellFreezesOverError('Could not get authenticated user');
                        }
                        return [2 /*return*/, new User_1.User(userData, this._client)];
                }
            });
        });
    };
    /**
     * Retrieves the user data for the given user name.
     *
     * @param userName The user name you want to look up.
     */
    UserApi.prototype.getUserByName = function (userName) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var users, user;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        // not using the decorator's cache here as users-by-name is slightly more complex to cache
                        this._cleanUserCache();
                        if (this._userByNameCache.has(userName)) {
                            return [2 /*return*/, this._userByNameCache.get(userName).value];
                        }
                        return [4 /*yield*/, this._client.callApi({
                                url: 'users',
                                query: { login: userName }
                            })];
                    case 1:
                        users = (_a.sent()).users;
                        if (users.length === 0) {
                            return [2 /*return*/, null];
                        }
                        user = new User_1.User(users[0], this._client);
                        this._userByNameCache.set(userName, {
                            value: user,
                            expires: Date.now() + 3600 * 1000
                        });
                        return [2 /*return*/, user];
                }
            });
        });
    };
    /**
     * Retrieves the user data for the given user names.
     *
     * @param userNames The user names you want to look up.
     */
    UserApi.prototype.getUsersByNames = function (userNames) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var cachedEntries, cachedObject, cachedUsers, toFetch, usersData, usersArr, users;
            var _this = this;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        this._cleanUserCache();
                        userNames = userNames.map(function (name) { return name.toLowerCase(); });
                        cachedEntries = Array.from(this._userByNameCache.entries()).filter(function (_a) {
                            var _b = tslib_1.__read(_a, 1), key = _b[0];
                            return userNames.includes(key);
                        });
                        cachedObject = shared_utils_1.entriesToObject(cachedEntries);
                        cachedUsers = shared_utils_1.mapObject(cachedObject, function (entry) { return entry.value; });
                        toFetch = userNames.filter(function (name) { return !(name in cachedUsers); });
                        if (!toFetch.length) {
                            return [2 /*return*/, cachedUsers];
                        }
                        return [4 /*yield*/, this._client.callApi({
                                url: 'users',
                                query: { login: toFetch.join(',') }
                            })];
                    case 1:
                        usersData = _a.sent();
                        usersArr = usersData.users.map(function (data) { return new User_1.User(data, _this._client); });
                        usersArr.forEach(function (user) {
                            return _this._userByNameCache.set(user.name, {
                                value: user,
                                expires: Date.now() + 3600 * 1000
                            });
                        });
                        users = shared_utils_1.indexBy(usersArr, 'name');
                        return [2 /*return*/, tslib_1.__assign(tslib_1.__assign({}, cachedUsers), users)];
                }
            });
        });
    };
    /**
     * Retrieves information about the user's chat appearance and privileges.
     *
     * @param user The user you want to get chat info for.
     */
    UserApi.prototype.getChatInfo = function (user) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var userId, data;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        userId = UserTools_1.extractUserId(user);
                        return [4 /*yield*/, this._client.callApi({ url: "users/" + userId + "/chat" })];
                    case 1:
                        data = _a.sent();
                        return [2 /*return*/, new UserChatInfo_1.UserChatInfo(data, this._client)];
                }
            });
        });
    };
    /**
     * Retrieves the emotes a given user can use.
     *
     * @param user The user you want to get emotes for.
     */
    UserApi.prototype.getUserEmotes = function (user) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var userId, data;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        userId = UserTools_1.extractUserId(user);
                        return [4 /*yield*/, this._client.callApi({
                                url: "users/" + userId + "/emotes",
                                scope: 'user_subscriptions'
                            })];
                    case 1:
                        data = _a.sent();
                        return [2 /*return*/, new EmoteSetList_1.EmoteSetList(data.emoticon_sets)];
                }
            });
        });
    };
    /**
     * Retrieves the subscription data for a given user to a given channel.
     *
     * @param user The user to retrieve the subscription data of.
     * @param toChannel The channel you want to retrieve the subscription data to.
     */
    UserApi.prototype.getSubscriptionData = function (user, toChannel) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var userId, channelId, _a, e_1;
            return tslib_1.__generator(this, function (_b) {
                switch (_b.label) {
                    case 0:
                        userId = UserTools_1.extractUserId(user);
                        channelId = UserTools_1.extractUserId(toChannel);
                        _b.label = 1;
                    case 1:
                        _b.trys.push([1, 3, , 4]);
                        _a = UserSubscription_1.UserSubscription.bind;
                        return [4 /*yield*/, this._client.callApi({
                                url: "users/" + userId + "/subscriptions/" + channelId,
                                scope: 'user_subscriptions'
                            })];
                    case 2: return [2 /*return*/, new (_a.apply(UserSubscription_1.UserSubscription, [void 0, _b.sent(), this._client]))()];
                    case 3:
                        e_1 = _b.sent();
                        if (e_1 instanceof twitch_api_call_1.HttpStatusCodeError) {
                            if (e_1.statusCode === 404) {
                                return [2 /*return*/, null];
                            }
                            else if (e_1.statusCode === 422) {
                                throw new NoSubscriptionProgramError_1.NoSubscriptionProgramError(channelId);
                            }
                        }
                        throw e_1;
                    case 4: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Get a list of channels a given user follows.
     *
     * @param user The user you want to retrieve the follows of.
     * @param page The result page you want to retrieve.
     * @param limit The number of results you want to retrieve.
     * @param orderBy The field to order by.
     * @param orderDirection The direction to order in - ascending or descending.
     */
    UserApi.prototype.getFollowedChannels = function (user, page, limit, orderBy, orderDirection) {
        if (limit === void 0) { limit = 25; }
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var userId, query, data;
            var _this = this;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        userId = UserTools_1.extractUserId(user);
                        query = {};
                        if (page) {
                            query.offset = ((page - 1) * limit).toString();
                        }
                        query.limit = limit.toString();
                        if (orderBy) {
                            query.sortby = orderBy;
                        }
                        if (orderDirection) {
                            query.direction = orderDirection;
                        }
                        return [4 /*yield*/, this._client.callApi({
                                url: "users/" + userId + "/follows/channels",
                                query: query
                            })];
                    case 1:
                        data = _a.sent();
                        return [2 /*return*/, data.follows.map(function (follow) { return new UserFollow_1.UserFollow(follow, _this._client); })];
                }
            });
        });
    };
    /**
     * Get follow data for a given user to a given channel.
     *
     * @param user The user you want to retrieve follow data of.
     * @param channel The channel you want to retrieve follow data to.
     */
    UserApi.prototype.getFollowedChannel = function (user, channel) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var userId, channelId, data, e_2;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        userId = UserTools_1.extractUserId(user);
                        channelId = UserTools_1.extractUserId(channel);
                        _a.label = 1;
                    case 1:
                        _a.trys.push([1, 3, , 4]);
                        return [4 /*yield*/, this._client.callApi({
                                url: "users/" + userId + "/follows/channels/" + channelId
                            })];
                    case 2:
                        data = _a.sent();
                        return [2 /*return*/, new UserFollow_1.UserFollow(data, this._client)];
                    case 3:
                        e_2 = _a.sent();
                        if (e_2 instanceof twitch_api_call_1.HttpStatusCodeError) {
                            if (e_2.statusCode === 404) {
                                return [2 /*return*/, null];
                            }
                        }
                        throw e_2;
                    case 4: return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Follows a given channel with a given user.
     *
     * @param user The user you want to follow with.
     * @param channel The channel to follow.
     * @param notifications Whether the user will receive notifications.
     */
    UserApi.prototype.followChannel = function (user, channel, notifications) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var userId, channelId, data;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        userId = UserTools_1.extractUserId(user);
                        channelId = UserTools_1.extractUserId(channel);
                        return [4 /*yield*/, this._client.callApi({
                                url: "users/" + userId + "/follows/channels/" + channelId,
                                method: 'PUT',
                                scope: 'user_follows_edit',
                                body: { notifications: Boolean(notifications).toString() }
                            })];
                    case 1:
                        data = _a.sent();
                        return [2 /*return*/, new UserFollow_1.UserFollow(data, this._client)];
                }
            });
        });
    };
    /**
     * Unfollows a given channel with a given user.
     *
     * @param user The user you want to unfollow with.
     * @param channel The channel to unfollow.
     */
    UserApi.prototype.unfollowChannel = function (user, channel) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var userId, channelId;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        userId = UserTools_1.extractUserId(user);
                        channelId = UserTools_1.extractUserId(channel);
                        return [4 /*yield*/, this._client.callApi({
                                url: "users/" + userId + "/follows/channels/" + channelId,
                                scope: 'user_follows_edit',
                                method: 'DELETE'
                            })];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    /**
     * Retrieves a list of users a given user has blocked.
     *
     * @param user The user you want to retrieve the block list of.
     * @param page The result page you want to retrieve.
     * @param limit The number of results you want to retrieve.
     */
    UserApi.prototype.getBlockedUsers = function (user, page, limit) {
        if (limit === void 0) { limit = 25; }
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var userId, query, data;
            var _this = this;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        userId = UserTools_1.extractUserId(user);
                        query = { limit: limit.toString() };
                        if (page) {
                            query.offset = ((page - 1) * limit).toString();
                        }
                        return [4 /*yield*/, this._client.callApi({
                                url: "users/" + userId + "/blocks",
                                query: query,
                                scope: 'user_blocks_read'
                            })];
                    case 1:
                        data = _a.sent();
                        return [2 /*return*/, data.blocks.map(function (block) { return new UserBlock_1.UserBlock(block, _this._client); })];
                }
            });
        });
    };
    /**
     * Blocks a given user with another given user.
     *
     * @param user The user you want to block with.
     * @param userToBlock The user to block.
     */
    UserApi.prototype.blockUser = function (user, userToBlock) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var userId, userIdToBlock, data;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        userId = UserTools_1.extractUserId(user);
                        userIdToBlock = UserTools_1.extractUserId(userToBlock);
                        return [4 /*yield*/, this._client.callApi({
                                url: "users/" + userId + "/blocks/" + userIdToBlock,
                                method: 'PUT',
                                scope: 'user_blocks_edit'
                            })];
                    case 1:
                        data = _a.sent();
                        return [2 /*return*/, new UserBlock_1.UserBlock(data, this._client)];
                }
            });
        });
    };
    /**
     * Unblocks a given user with another given user.
     *
     * @param user The user you want to unblock with.
     * @param userToUnblock The user to unblock.
     */
    UserApi.prototype.unblockUser = function (user, userToUnblock) {
        return tslib_1.__awaiter(this, void 0, void 0, function () {
            var userId, userIdToUnblock;
            return tslib_1.__generator(this, function (_a) {
                switch (_a.label) {
                    case 0:
                        userId = UserTools_1.extractUserId(user);
                        userIdToUnblock = UserTools_1.extractUserId(userToUnblock);
                        return [4 /*yield*/, this._client.callApi({
                                url: "users/" + userId + "/blocks/" + userIdToUnblock,
                                method: 'DELETE',
                                scope: 'user_blocks_edit'
                            })];
                    case 1:
                        _a.sent();
                        return [2 /*return*/];
                }
            });
        });
    };
    UserApi.prototype._cleanUserCache = function () {
        var _this = this;
        var now = Date.now();
        this._userByNameCache.forEach(function (val, key) {
            if (val.expires < now) {
                _this._userByNameCache.delete(key);
            }
        });
    };
    tslib_1.__decorate([
        cache_decorators_1.Cached(3600)
    ], UserApi.prototype, "getMe", null);
    tslib_1.__decorate([
        cache_decorators_1.Cached(3600)
    ], UserApi.prototype, "getUser", null);
    tslib_1.__decorate([
        cache_decorators_1.Cached(3600)
    ], UserApi.prototype, "getChatInfo", null);
    tslib_1.__decorate([
        cache_decorators_1.Cached(3600)
    ], UserApi.prototype, "getUserEmotes", null);
    tslib_1.__decorate([
        cache_decorators_1.Cached(3600)
    ], UserApi.prototype, "getSubscriptionData", null);
    tslib_1.__decorate([
        cache_decorators_1.Cached(300)
    ], UserApi.prototype, "getFollowedChannels", null);
    tslib_1.__decorate([
        cache_decorators_1.Cached(300)
    ], UserApi.prototype, "getFollowedChannel", null);
    tslib_1.__decorate([
        cache_decorators_1.ClearsCache('getFollowedChannels', 1),
        cache_decorators_1.ClearsCache('getFollowedChannel', 2)
    ], UserApi.prototype, "followChannel", null);
    tslib_1.__decorate([
        cache_decorators_1.ClearsCache('getFollowedChannels', 1),
        cache_decorators_1.ClearsCache('getFollowedChannel', 2)
    ], UserApi.prototype, "unfollowChannel", null);
    tslib_1.__decorate([
        cache_decorators_1.Cached(3600)
    ], UserApi.prototype, "getBlockedUsers", null);
    tslib_1.__decorate([
        cache_decorators_1.ClearsCache('getBlockedUsers', 1)
    ], UserApi.prototype, "blockUser", null);
    tslib_1.__decorate([
        cache_decorators_1.ClearsCache('getBlockedUsers', 1)
    ], UserApi.prototype, "unblockUser", null);
    UserApi = tslib_1.__decorate([
        cache_decorators_1.Cacheable
    ], UserApi);
    return UserApi;
}(BaseApi_1.BaseApi));
exports.UserApi = UserApi;
