"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UserService = void 0;
const common_1 = require("@nestjs/common");
const core_1 = require("@nestjs/core");
const typeorm_1 = require("@nestjs/typeorm");
const crud_service_1 = require("./crud.service");
const typeorm_2 = require("typeorm");
const user_entity_1 = require("../entities/user.entity");
const error_messages_1 = require("../constants/error-messages");
const user_repository_1 = require("../repository/user.repository");
const role_metadata_repository_1 = require("../repository/role-metadata.repository");
const hashing_service_1 = require("./hashing.service");
let UserService = class UserService extends crud_service_1.CRUDService {
    buildFacebookUsernameBase(name) {
        const normalized = (name || "")
            .trim()
            .toLowerCase()
            .replace(/[^a-z0-9]+/g, "_")
            .replace(/^_+|_+$/g, "");
        return normalized || "facebook_user";
    }
    async resolveUniqueUsername(preferredUsername) {
        let candidate = preferredUsername;
        let suffix = 0;
        while (await this.repo.findOne({ where: { username: candidate } })) {
            suffix += 1;
            candidate = `${preferredUsername}_${suffix}`;
        }
        if (candidate) {
            return candidate;
        }
    }
    constructor(hashingService, entityManager, repo, nonSecurityRuleAwareRepo, roleRepository, moduleRef) {
        super(entityManager, repo, "user", "solid-core", moduleRef);
        this.hashingService = hashingService;
        this.entityManager = entityManager;
        this.repo = repo;
        this.nonSecurityRuleAwareRepo = nonSecurityRuleAwareRepo;
        this.roleRepository = roleRepository;
        this.moduleRef = moduleRef;
    }
    async delete(id, solidRequestContext = {}) {
        if (solidRequestContext?.activeUser?.sub === id) {
            throw new common_1.BadRequestException(error_messages_1.ERROR_MESSAGES.DELETE_SELF_NOT_ALLOWED);
        }
        return super.delete(id, solidRequestContext);
    }
    async deleteMany(ids, solidRequestContext = {}) {
        if (!ids || ids.length === 0) {
            throw new Error(error_messages_1.ERROR_MESSAGES.DELETE_IDS_REQUIRED);
        }
        if (solidRequestContext?.activeUser?.sub &&
            ids.includes(solidRequestContext.activeUser.id)) {
            throw new common_1.BadRequestException(error_messages_1.ERROR_MESSAGES.DELETE_SELF_NOT_ALLOWED);
        }
        return super.deleteMany(ids, solidRequestContext);
    }
    async findOneByEmail(email) {
        return await this.repo.findOne({
            where: {
                email: email,
            },
            relations: {},
        });
    }
    async findOneByAccessCode(accessCode) {
        return await this.repo.findOne({
            where: {
                accessCode: accessCode,
            },
            relations: {},
        });
    }
    async findOneByUsername(username) {
        return await this.repo.findOne({
            where: {
                username: username,
            },
            relations: {},
        });
    }
    async updateUser(id, updateDto, files, solidRequestContext = {}) {
        const user = await this.repo.findOne({
            where: { id: id },
            relations: {
                roles: true,
            },
        });
        if (!user) {
            throw new Error(error_messages_1.ERROR_MESSAGES.USER_NOT_FOUND);
        }
        if (updateDto.roles != null) {
            await this.addRolesToUser(user.username, updateDto.roles);
        }
        await this.update(id, updateDto, files, true);
    }
    async addRoleToUser(username, roleName) {
        const user = await this.repo.findOne({
            where: { username: username },
            relations: {
                roles: true,
            },
        });
        if (!user) {
            throw new Error(error_messages_1.ERROR_MESSAGES.USER_NOT_FOUND_BY_USERNAME(username));
        }
        const role = await this.roleRepository.findOne({
            where: { name: roleName },
        });
        if (!role) {
            throw new Error(error_messages_1.ERROR_MESSAGES.ROLE_NOT_FOUND(roleName));
        }
        if (user.roles && user.roles.length > 0) {
            user.roles.push(role);
        }
        else {
            user.roles = [role];
        }
        return await this.repo.save(user);
    }
    async addRolesToUser(username, roleNames) {
        const user = await this.nonSecurityRuleAwareRepo.findOne({
            where: { username: username },
            relations: { roles: true },
        });
        if (!user) {
            throw new Error(error_messages_1.ERROR_MESSAGES.USER_NOT_FOUND_BY_USERNAME(username));
        }
        const roles = await this.roleRepository.find({
            where: roleNames.map((roleName) => ({ name: roleName })),
        });
        if (roles.length !== roleNames.length) {
            const foundRoleNames = roles.map((role) => role.name);
            const missingRoles = roleNames.filter((roleName) => !foundRoleNames.includes(roleName));
            throw new Error(error_messages_1.ERROR_MESSAGES.ROLES_NOT_FOUND(missingRoles));
        }
        const currentRoles = user.roles.map((role) => role.name);
        const rolesToAdd = roles.filter((role) => !currentRoles.includes(role.name));
        const rolesToRemove = user.roles.filter((role) => !roleNames.includes(role.name));
        if (rolesToAdd.length > 0) {
            user.roles.push(...rolesToAdd);
        }
        if (rolesToRemove.length > 0) {
            user.roles = user.roles.filter((role) => !rolesToRemove.includes(role));
        }
        return await this.nonSecurityRuleAwareRepo.save(user);
    }
    async removeRoleFromUser(username, roleName) {
        const user = await this.repo.findOne({
            where: {
                username: username,
            },
            relations: {
                roles: true,
            },
        });
        if (!user) {
            throw new Error(error_messages_1.ERROR_MESSAGES.USER_NOT_FOUND_BY_USERNAME(username));
        }
        user.roles = user.roles.filter((role) => role.name !== roleName);
        return await this.repo.save(user);
    }
    async resolveUserOnOauthGoogle(oauthUserDto) {
        const user = await this.repo.findOne({
            where: {
                email: oauthUserDto.email,
            },
            relations: {
                roles: true,
            },
        });
        if (!user) {
            const user = new user_entity_1.User();
            user.username = oauthUserDto.email;
            user.email = oauthUserDto.email;
            user.fullName = oauthUserDto.name;
            user.lastLoginProvider = oauthUserDto.provider;
            user.accessCode = oauthUserDto.accessCode;
            user.googleAccessToken = oauthUserDto.accessToken;
            user.googleId = oauthUserDto.providerId;
            user.googleProfilePicture = oauthUserDto.picture;
            const savedUser = await this.repo.save(user);
            await this.initializeRolesForNewUser([this.settingService.getConfigValue("defaultRole")], savedUser);
        }
        else {
            const entity = await this.repo.preload({
                id: user.id,
                lastLoginProvider: oauthUserDto.provider,
                accessCode: oauthUserDto.accessCode,
                googleAccessToken: oauthUserDto.accessToken,
                googleId: oauthUserDto.providerId,
                googleProfilePicture: oauthUserDto.picture,
            });
            await this.repo.save(entity);
        }
        return user;
    }
    async resolveUserOnOauthFacebook(oauthUserDto) {
        const normalizedEmail = oauthUserDto.email?.trim().toLowerCase();
        let user = null;
        if (oauthUserDto.providerId) {
            user = await this.repo.findOne({
                where: {
                    facebookId: oauthUserDto.providerId,
                },
                relations: {
                    roles: true,
                },
            });
        }
        if (!user) {
            const facebookProviderFallback = `facebook_${oauthUserDto.providerId}`;
            const facebookNameUsername = this.buildFacebookUsernameBase(oauthUserDto.name);
            let username = facebookNameUsername;
            let email = normalizedEmail;
            if (normalizedEmail) {
                const existingByEmail = await this.repo.findOne({
                    where: { email: normalizedEmail },
                });
                if (existingByEmail) {
                    username = facebookNameUsername;
                    email = null;
                }
            }
            username = await this.resolveUniqueUsername(username);
            const newUser = new user_entity_1.User();
            newUser.username = username;
            newUser.email = email;
            newUser.fullName = oauthUserDto.name;
            newUser.lastLoginProvider = oauthUserDto.provider;
            newUser.accessCode = oauthUserDto.accessCode;
            newUser.facebookAccessToken = oauthUserDto.accessToken;
            newUser.facebookId = oauthUserDto.providerId;
            newUser.facebookProfilePicture = oauthUserDto.picture;
            const savedUser = await this.repo.save(newUser);
            await this.initializeRolesForNewUser([this.settingService.getConfigValue("defaultRole")], savedUser);
            return savedUser;
        }
        else {
            const entity = await this.repo.preload({
                id: user.id,
                lastLoginProvider: oauthUserDto.provider,
                accessCode: oauthUserDto.accessCode,
                facebookAccessToken: oauthUserDto.accessToken,
                facebookId: oauthUserDto.providerId,
                facebookProfilePicture: oauthUserDto.picture,
            });
            await this.repo.save(entity);
            return entity;
        }
    }
    async resolveUserOnOauthMicrosoft(oauthUserDto) {
        const user = await this.repo.findOne({
            where: {
                email: oauthUserDto.email,
            },
            relations: {
                roles: true,
            },
        });
        if (!user) {
            const newUser = new user_entity_1.User();
            newUser.username = oauthUserDto.email;
            newUser.email = oauthUserDto.email;
            newUser.fullName = oauthUserDto.name;
            newUser.lastLoginProvider = oauthUserDto.provider;
            newUser.accessCode = oauthUserDto.accessCode;
            newUser.microsoftAccessToken = oauthUserDto.accessToken;
            newUser.microsoftId = oauthUserDto.providerId;
            newUser.microsoftProfilePicture = oauthUserDto.picture;
            const savedUser = await this.repo.save(newUser);
            await this.initializeRolesForNewUser([this.settingService.getConfigValue("defaultRole")], savedUser);
        }
        else {
            const entity = await this.repo.preload({
                id: user.id,
                lastLoginProvider: oauthUserDto.provider,
                accessCode: oauthUserDto.accessCode,
                microsoftAccessToken: oauthUserDto.accessToken,
                microsoftId: oauthUserDto.providerId,
                microsoftProfilePicture: oauthUserDto.picture,
            });
            await this.repo.save(entity);
        }
        return user;
    }
    async findUsersByRole(roleName, relations = {}) {
        return await this.repo.find({
            where: {
                roles: {
                    name: roleName,
                },
            },
            relations: relations,
        });
    }
    async checkIfPermissionExists(query, activeUser) {
        const matchingPermssions = activeUser.permissions.filter((p) => query.permissionNames.includes(p));
        return matchingPermssions;
    }
    async initializeRolesForNewUser(roles, user) {
        if (!user.id) {
            throw new common_1.BadRequestException(error_messages_1.ERROR_MESSAGES.USER_MISSING_ID);
        }
        let userRoles = [];
        userRoles.push("Internal User");
        if (roles) {
            userRoles = [...userRoles, ...roles];
        }
        userRoles = Array.from(new Set([...userRoles]));
        if (userRoles.length > 0) {
            await this.addRolesToUser(user.username, userRoles);
        }
    }
    async hashPassword(password) {
        const hashedPassword = await this.hashingService.hash(password);
        return {
            password: hashedPassword,
            passwordScheme: this.hashingService.name(),
            passwordSchemeVersion: this.hashingService.currentVersion(),
        };
    }
};
exports.UserService = UserService;
exports.UserService = UserService = __decorate([
    (0, common_1.Injectable)(),
    __param(1, (0, typeorm_1.InjectEntityManager)()),
    __param(3, (0, typeorm_1.InjectRepository)(user_entity_1.User, "default")),
    __metadata("design:paramtypes", [hashing_service_1.HashingService,
        typeorm_2.EntityManager,
        user_repository_1.UserRepository,
        typeorm_2.Repository,
        role_metadata_repository_1.RoleMetadataRepository,
        core_1.ModuleRef])
], UserService);
//# sourceMappingURL=user.service.js.map