"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 typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const user_entity_1 = require("../entities/user.entity");
const event_emitter_1 = require("@nestjs/event-emitter");
const auth_constants_1 = require("../../auth.constants");
const user_updated_event_1 = require("../events/user-updated.event");
const user_deleted_event_1 = require("../events/user-deleted.event");
const user_created_event_1 = require("../events/user-created.event");
const tenant_1 = require("../../tenant");
const debug_logger_service_1 = require("../../core/services/debug-logger.service");
const auth_config_service_1 = require("../../core/services/auth-config.service");
let UserService = class UserService {
    userRepository;
    tenantService;
    eventEmitter;
    authConfigService;
    debugLogger;
    constructor(userRepository, tenantService, eventEmitter, authConfigService, debugLogger) {
        this.userRepository = userRepository;
        this.tenantService = tenantService;
        this.eventEmitter = eventEmitter;
        this.authConfigService = authConfigService;
        this.debugLogger = debugLogger;
    }
    async createUser(data, context) {
        this.debugLogger.logFunctionEntry('createUser', 'UserService', { email: data.email, phone: data.phone, hasPassword: !!data.password });
        try {
            const { email, phone } = data;
            let { tenantId = null } = data;
            tenantId = await this.tenantService.resolveTenantId(tenantId);
            data.tenantId = tenantId;
            if (email) {
                const existingUser = await this.getUserByEmail(email, tenantId);
                if (existingUser) {
                    this.debugLogger.warn('User with email already exists', 'UserService', { email, tenantId });
                    throw new common_1.ConflictException({
                        message: 'User with this email already exists',
                        code: 'USER_ALREADY_EXISTS'
                    });
                }
            }
            if (phone) {
                const existingUser = await this.getUserByPhone(phone, tenantId);
                if (existingUser) {
                    this.debugLogger.warn('User with phone already exists', 'UserService', { phone, tenantId });
                    throw new common_1.ConflictException({
                        message: 'User with this phone number already exists',
                        code: 'USER_ALREADY_EXISTS'
                    });
                }
            }
            const config = this.authConfigService.getConfig();
            if (config.user?.beforeCreate) {
                this.debugLogger.debug('Applying user.beforeCreate hook', 'UserService');
                data = await config.user.beforeCreate(data, context);
            }
            this.debugLogger.debug('Creating new user entity', 'UserService');
            const user = this.userRepository.create(data);
            if (data.password) {
                await user.setPassword(data.password);
            }
            await this.userRepository.save(user);
            this.debugLogger.info('User created successfully', 'UserService', { userId: user.id });
            const normalizedEmail = email?.toLowerCase().trim();
            if (normalizedEmail && config.emailAuth?.enabled !== false) {
                await user.findOrCreateIdentity(auth_constants_1.EMAIL_AUTH_PROVIDER, normalizedEmail);
            }
            if (phone && config.phoneAuth?.enabled === true) {
                await user.findOrCreateIdentity(auth_constants_1.PHONE_AUTH_PROVIDER, phone);
            }
            this.debugLogger.debug('Emitting user created event', 'UserService', { userId: user.id });
            await this.eventEmitter.emitAsync(auth_constants_1.NestAuthEvents.USER_CREATED, new user_created_event_1.UserCreatedEvent({
                user,
                input: context,
                tenantId: user.tenantId
            }));
            if (config.user?.afterCreate) {
                this.debugLogger.debug('Applying user.afterCreate hook', 'UserService', { userId: user.id });
                await config.user.afterCreate(user, context);
            }
            this.debugLogger.logFunctionExit('createUser', 'UserService', { userId: user.id });
            return user;
        }
        catch (error) {
            this.debugLogger.logError(error, 'createUser', { email: data.email, phone: data.phone });
            throw error;
        }
    }
    async getUserById(id, options) {
        this.debugLogger.debug('Getting user by ID', 'UserService', { userId: id });
        if (!id) {
            this.debugLogger.warn('No user ID provided', 'UserService');
            return null;
        }
        const user = await this.userRepository.findOne({
            ...(options ? options : {}),
            where: { id }
        });
        if (!user) {
            this.debugLogger.warn('User not found', 'UserService', { userId: id });
            return null;
        }
        this.debugLogger.debug('User found', 'UserService', { userId: user.id });
        return user;
    }
    async getUserByEmail(email, tenantId, options) {
        this.debugLogger.debug('Getting user by email', 'UserService', { email: !!email, tenantId });
        tenantId = await this.tenantService.resolveTenantId(tenantId || null);
        if (!email) {
            this.debugLogger.warn('No email provided for user lookup', 'UserService');
            return null;
        }
        const normalizedEmail = email.toLowerCase().trim();
        const user = await this.userRepository.findOne({
            ...(options ? options : {}),
            where: {
                email: normalizedEmail,
                tenantId: tenantId || (0, typeorm_2.IsNull)()
            }
        });
        if (user) {
            this.debugLogger.debug('User found by email', 'UserService', { userId: user.id, tenantId });
        }
        else {
            this.debugLogger.debug('No user found with email', 'UserService', { tenantId });
        }
        return user;
    }
    async getUserByPhone(phone, tenantId, options) {
        this.debugLogger.debug('Getting user by phone', 'UserService', { phone: !!phone, tenantId });
        tenantId = await this.tenantService.resolveTenantId(tenantId || null);
        if (!phone) {
            this.debugLogger.warn('No phone provided for user lookup', 'UserService');
            return null;
        }
        const user = await this.userRepository.findOne({
            ...(options ? options : {}),
            where: {
                phone,
                tenantId: tenantId || (0, typeorm_2.IsNull)()
            }
        });
        if (user) {
            this.debugLogger.debug('User found by phone', 'UserService', { userId: user.id, tenantId });
        }
        else {
            this.debugLogger.debug('No user found with phone', 'UserService', { tenantId });
        }
        return user;
    }
    async getUsers(options) {
        return this.userRepository.find(options);
    }
    async getUsersByTenant(tenantId, options) {
        tenantId = await this.tenantService.resolveTenantId(tenantId || null);
        if (!tenantId) {
            return [];
        }
        return this.userRepository.find({
            ...(options ? options : {}),
            where: {
                tenantId,
                ...(options?.where ? options.where : {})
            }
        });
    }
    async updateUser(id, data) {
        this.debugLogger.logFunctionEntry('updateUser', 'UserService', { userId: id, fields: Object.keys(data) });
        try {
            const user = await this.getUserById(id);
            if (!user) {
                this.debugLogger.error('User not found for update', 'UserService', { userId: id });
                throw new common_1.NotFoundException({
                    message: `User with ID ${id} not found`,
                    code: 'USER_NOT_FOUND'
                });
            }
            if (data.email || data.phone) {
                this.debugLogger.debug('Checking for conflicts during user update', 'UserService', { userId: id, email: !!data.email, phone: !!data.phone });
                let existingUser = null;
                if (data.phone) {
                    existingUser = await this.userRepository.findOne({
                        where: { phone: data.phone, tenantId: user.tenantId, id: (0, typeorm_2.Not)(user.id) }
                    });
                }
                if (!existingUser && data.email) {
                    existingUser = await this.userRepository.findOne({
                        where: { email: data.email, tenantId: user.tenantId, id: (0, typeorm_2.Not)(user.id) }
                    });
                }
                if (existingUser) {
                    this.debugLogger.warn('Conflict detected during user update', 'UserService', { userId: id, conflictingUserId: existingUser.id });
                    throw new common_1.ConflictException({
                        message: `User with ${data.email ? `email ${data.email}` : ''}${data.email && data.phone ? ' or ' : ''}${data.phone ? `phone ${data.phone}` : ''} already exists.`,
                        code: 'USER_ALREADY_EXISTS'
                    });
                }
            }
            delete data.tenantId;
            this.debugLogger.debug('Updating user data', 'UserService', { userId: id, fields: Object.keys(data) });
            Object.assign(user, data);
            const updatedUser = await this.userRepository.save(user);
            this.debugLogger.info('User updated successfully', 'UserService', { userId: updatedUser.id });
            const config = this.authConfigService.getConfig();
            if (data.email && config.emailAuth?.enabled !== false) {
                this.debugLogger.debug('Updating email identity', 'UserService', { userId: id });
                await user.updateOrCreateIdentity(auth_constants_1.EMAIL_AUTH_PROVIDER, { providerId: data.email });
            }
            if (data.phone && config.phoneAuth?.enabled === true) {
                this.debugLogger.debug('Updating phone identity', 'UserService', { userId: id });
                await user.updateOrCreateIdentity(auth_constants_1.PHONE_AUTH_PROVIDER, { providerId: data.phone });
            }
            this.debugLogger.debug('Emitting user updated event', 'UserService', { userId: updatedUser.id });
            await this.eventEmitter.emitAsync(auth_constants_1.NestAuthEvents.USER_UPDATED, new user_updated_event_1.UserUpdatedEvent({
                user: updatedUser,
                tenantId: updatedUser.tenantId,
                updatedFields: Object.keys(data)
            }));
            this.debugLogger.logFunctionExit('updateUser', 'UserService', { userId: updatedUser.id });
            return updatedUser;
        }
        catch (error) {
            this.debugLogger.logError(error, 'updateUser', { userId: id, fields: Object.keys(data) });
            throw error;
        }
    }
    async deleteUser(id) {
        this.debugLogger.logFunctionEntry('deleteUser', 'UserService', { userId: id });
        try {
            const user = await this.getUserById(id);
            if (!user) {
                this.debugLogger.error('User not found for deletion', 'UserService', { userId: id });
                throw new common_1.NotFoundException({
                    message: `User with ID ${id} not found`,
                    code: 'USER_NOT_FOUND'
                });
            }
            this.debugLogger.debug('Emitting user deleted event', 'UserService', { userId: id });
            await this.eventEmitter.emitAsync(auth_constants_1.NestAuthEvents.USER_DELETED, new user_deleted_event_1.UserDeletedEvent({
                user,
                tenantId: user.tenantId
            }));
            this.debugLogger.debug('Deleting user from database', 'UserService', { userId: id });
            await this.userRepository.remove(user);
            this.debugLogger.info('User deleted successfully', 'UserService', { userId: id });
            this.debugLogger.logFunctionExit('deleteUser', 'UserService', { userId: id });
        }
        catch (error) {
            this.debugLogger.logError(error, 'deleteUser', { userId: id });
            throw error;
        }
    }
    async verifyUser(id, verificationType) {
        const user = await this.getUserById(id);
        if (!user) {
            throw new common_1.NotFoundException({
                message: `User with ID ${id} not found`,
                code: 'USER_NOT_FOUND'
            });
        }
        if (verificationType === 'email') {
            user.emailVerifiedAt = new Date();
        }
        else if (verificationType === 'phone') {
            user.phoneVerifiedAt = new Date();
        }
        user.isVerified = true;
        return this.userRepository.save(user);
    }
    async unverifyUser(id, verificationType) {
        const user = await this.getUserById(id);
        if (!user) {
            throw new common_1.NotFoundException({
                message: `User with ID ${id} not found`,
                code: 'USER_NOT_FOUND'
            });
        }
        if (verificationType === 'email') {
            user.emailVerifiedAt = null;
        }
        else if (verificationType === 'phone') {
            user.phoneVerifiedAt = null;
        }
        user.isVerified = false;
        user.isVerified = Boolean(user.emailVerifiedAt || user.phoneVerifiedAt);
        return this.userRepository.save(user);
    }
    async updateUserStatus(id, isActive) {
        const user = await this.getUserById(id);
        if (!user) {
            throw new common_1.NotFoundException({
                message: `User with ID ${id} not found`,
                code: 'USER_NOT_FOUND'
            });
        }
        user.isActive = isActive;
        const updatedUser = await this.userRepository.save(user);
        this.debugLogger.debug('Emitting user updated event (status change)', 'UserService', { userId: id, isActive });
        await this.eventEmitter.emitAsync(auth_constants_1.NestAuthEvents.USER_UPDATED, new user_updated_event_1.UserUpdatedEvent({
            user: updatedUser,
            tenantId: updatedUser.tenantId,
            updatedFields: ['isActive']
        }));
        return updatedUser;
    }
    async updateUserMetadata(id, metadata) {
        const user = await this.getUserById(id);
        if (!user) {
            throw new common_1.NotFoundException({
                message: `User with ID ${id} not found`,
                code: 'USER_NOT_FOUND'
            });
        }
        user.metadata = {
            ...user.metadata,
            ...metadata
        };
        const updatedUser = await this.userRepository.save(user);
        this.debugLogger.debug('Emitting user updated event (metadata)', 'UserService', { userId: id });
        await this.eventEmitter.emitAsync(auth_constants_1.NestAuthEvents.USER_UPDATED, new user_updated_event_1.UserUpdatedEvent({
            user: updatedUser,
            tenantId: updatedUser.tenantId,
            updatedFields: ['metadata']
        }));
        return updatedUser;
    }
    async countUsers(options) {
        return this.userRepository.count(options);
    }
    async getUsersAndCount(options) {
        return this.userRepository.findAndCount(options);
    }
    async getUsersByRole(roleName, guard) {
        this.debugLogger.debug('Getting users by role', 'UserService', { roleName, guard });
        const users = await this.userRepository
            .createQueryBuilder('user')
            .leftJoinAndSelect('user.roles', 'role')
            .where('role.name = :roleName', { roleName })
            .andWhere('role.guard = :guard', { guard })
            .getMany();
        this.debugLogger.debug('Found users with role', 'UserService', { roleName, count: users.length });
        return users;
    }
};
exports.UserService = UserService;
exports.UserService = UserService = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(user_entity_1.NestAuthUser)),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        tenant_1.TenantService,
        event_emitter_1.EventEmitter2,
        auth_config_service_1.AuthConfigService,
        debug_logger_service_1.DebugLoggerService])
], UserService);
//# sourceMappingURL=user.service.js.map