"use strict";
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());
    });
};
const { Role, getRole, isBuiltin, getAllRoles, } = require("@devlego/backend-core/roles");
const { generateRoleID, getUserMetadataParams, InternalTables, } = require("../../db/utils");
const { getAppDB } = require("@devlego/backend-core/context");
const { events } = require("@devlego/backend-core");
const UpdateRolesOptions = {
    CREATED: "created",
    REMOVED: "removed",
};
function updateRolesOnUserTable(db, roleId, updateOption) {
    return __awaiter(this, void 0, void 0, function* () {
        const table = yield db.get(InternalTables.USER_METADATA);
        const schema = table.schema;
        const remove = updateOption === UpdateRolesOptions.REMOVED;
        let updated = false;
        for (let prop of Object.keys(schema)) {
            if (prop === "roleId") {
                updated = true;
                const constraints = schema[prop].constraints;
                const indexOf = constraints.inclusion.indexOf(roleId);
                if (remove && indexOf !== -1) {
                    constraints.inclusion.splice(indexOf, 1);
                }
                else if (!remove && indexOf === -1) {
                    constraints.inclusion.push(roleId);
                }
                break;
            }
        }
        if (updated) {
            yield db.put(table);
        }
    });
}
exports.fetch = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        ctx.body = yield getAllRoles();
    });
};
exports.find = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        ctx.body = yield getRole(ctx.params.roleId);
    });
};
exports.save = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        const db = getAppDB();
        let { _id, name, inherits, permissionId } = ctx.request.body;
        let isCreate = false;
        if (!_id) {
            _id = generateRoleID();
            isCreate = true;
        }
        else if (isBuiltin(_id)) {
            ctx.throw(400, "Cannot update builtin roles.");
        }
        const role = new Role(_id, name)
            .addPermission(permissionId)
            .addInheritance(inherits);
        if (ctx.request.body._rev) {
            role._rev = ctx.request.body._rev;
        }
        const result = yield db.put(role);
        if (isCreate) {
            yield events.role.created(role);
        }
        else {
            yield events.role.updated(role);
        }
        yield updateRolesOnUserTable(db, _id, UpdateRolesOptions.CREATED);
        role._rev = result.rev;
        ctx.body = role;
        ctx.message = `Role '${role.name}' created successfully.`;
    });
};
exports.destroy = function (ctx) {
    return __awaiter(this, void 0, void 0, function* () {
        const db = getAppDB();
        const roleId = ctx.params.roleId;
        const role = yield db.get(roleId);
        if (isBuiltin(roleId)) {
            ctx.throw(400, "Cannot delete builtin role.");
        }
        // first check no users actively attached to role
        const users = (yield db.allDocs(getUserMetadataParams(null, {
            include_docs: true,
        }))).rows.map(row => row.doc);
        const usersWithRole = users.filter(user => user.roleId === roleId);
        if (usersWithRole.length !== 0) {
            ctx.throw(400, "Cannot delete role when it is in use.");
        }
        yield db.remove(roleId, ctx.params.rev);
        yield events.role.deleted(role);
        yield updateRolesOnUserTable(db, ctx.params.roleId, UpdateRolesOptions.REMOVED);
        ctx.message = `Role ${ctx.params.roleId} deleted successfully`;
        ctx.status = 200;
    });
};
