"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.AdminUsersController = void 0;
const common_1 = require("@nestjs/common");
const common_2 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const admin_session_guard_1 = require("../guards/admin-session.guard");
const admin_user_dto_1 = require("../dto/admin-user.dto");
const user_service_1 = require("../../user/services/user.service");
const tenant_service_1 = require("../../tenant/services/tenant.service");
const mfa_secret_entity_1 = require("../../auth/entities/mfa-secret.entity");
const auth_constants_1 = require("../../auth.constants");
const typeorm_3 = require("typeorm");
const mfa_service_1 = require("../../auth/services/mfa.service");
const session_manager_service_1 = require("../../session/services/session-manager.service");
let AdminUsersController = class AdminUsersController {
    users;
    tenantService;
    mfaService;
    sessionManager;
    mfaSecretRepository;
    constructor(users, tenantService, mfaService, sessionManager, mfaSecretRepository) {
        this.users = users;
        this.tenantService = tenantService;
        this.mfaService = mfaService;
        this.sessionManager = sessionManager;
        this.mfaSecretRepository = mfaSecretRepository;
    }
    async ensureUserExists(id) {
        const user = await this.users.getUserById(id);
        if (!user) {
            throw new common_2.NotFoundException('User not found');
        }
        return user;
    }
    toSessionResponse(session) {
        return {
            id: session.id,
            deviceName: session.deviceName || 'Unknown device',
            userAgent: session.userAgent,
            ipAddress: session.ipAddress,
            lastActive: session.lastActive,
            createdAt: session.createdAt,
            expiresAt: session.expiresAt,
        };
    }
    buildStatusFilter(status) {
        if (!status)
            return {};
        switch (status) {
            case 'active':
                return { isActive: true };
            case 'inactive':
                return { isActive: false };
            case 'verified':
                return { isVerified: true };
            case 'unverified':
                return { isVerified: false };
            default:
                return {};
        }
    }
    escapeLikePattern(value) {
        return value.replace(/[%_\\]/g, '\\$&');
    }
    async listUsers(page, limit, search, status, tenantId, roleName) {
        let pageNum = parseInt(page || '1', 10);
        let limitNum = parseInt(limit || '10', 10);
        if (isNaN(pageNum) || pageNum < 1) {
            pageNum = 1;
        }
        if (isNaN(limitNum) || limitNum <= 0) {
            limitNum = 10;
        }
        if (limitNum > 100) {
            limitNum = 100;
        }
        const skip = (pageNum - 1) * limitNum;
        const baseFilter = {
            ...this.buildStatusFilter(status),
        };
        if (tenantId && tenantId.trim()) {
            baseFilter.tenantId = tenantId.trim();
        }
        if (roleName && roleName.trim()) {
            baseFilter.roles = { name: roleName.trim() };
        }
        let where = baseFilter;
        if (search && search.trim()) {
            const escapedSearch = this.escapeLikePattern(search.trim());
            const searchPattern = `%${escapedSearch}%`;
            where = [
                { ...baseFilter, email: (0, typeorm_3.Like)(searchPattern) },
                { ...baseFilter, phone: (0, typeorm_3.Like)(searchPattern) },
            ];
        }
        const [users, total] = await this.users.getUsersAndCount({
            where,
            relations: ['roles'],
            order: { createdAt: 'DESC' },
            skip,
            take: limitNum,
        });
        return {
            data: users.map((user) => this.toSafeUser(user)),
            meta: {
                page: pageNum,
                limit: limitNum,
                total,
                totalPages: Math.ceil(total / limitNum),
            },
        };
    }
    async createUser(dto) {
        const tenantId = await this.tenantService.resolveTenantId(dto.tenantId);
        const user = await this.users.createUser({
            email: dto.email,
            phone: dto.phone,
            tenantId,
            metadata: dto.metadata ?? {},
            isActive: dto.isActive ?? true,
            isVerified: dto.isVerified ?? false,
        });
        if (dto.password) {
            await user.setPassword(dto.password);
            await user.save();
        }
        if (dto.roles?.length) {
            await user.assignRolesWithMultipleGuard(dto.roles);
            await user.save();
        }
        return { user: this.toSafeUser(user) };
    }
    async getUser(id) {
        const user = await this.users.getUserById(id, {
            relations: ['roles', 'mfaSecrets', 'identities']
        });
        if (!user) {
            throw new common_2.NotFoundException('User not found');
        }
        const availableMethods = this.mfaService.getAvailableMethods();
        const [enabledMethods, sessions] = await Promise.all([
            this.mfaService.getEnabledMethods(user.id),
            this.sessionManager.getUserSessions(user.id),
        ]);
        const sortedSessions = sessions
            .sort((a, b) => {
            const aTime = a.lastActive?.getTime() ?? a.updatedAt?.getTime() ?? 0;
            const bTime = b.lastActive?.getTime() ?? b.updatedAt?.getTime() ?? 0;
            return bTime - aTime;
        })
            .map((session) => this.toSessionResponse(session));
        return {
            user: this.toSafeUser(user),
            loginMethods: {
                emailEnabled: !!user.email && !!user.emailVerifiedAt,
                phoneEnabled: !!user.phone && !!user.phoneVerifiedAt,
                hasPassword: !!user.passwordHash,
            },
            mfa: {
                isEnabled: user.isMfaEnabled,
                allowUserToggle: this.mfaService.mfaConfig?.allowUserToggle ?? false,
                availableMethods,
                enabledMethods,
                hasRecoveryCode: !!user.mfaRecoveryCode,
                totpDevices: user.mfaSecrets?.map((device) => ({
                    id: device.id,
                    deviceName: device.deviceName || 'Authenticator',
                    verified: device.verified,
                    lastUsedAt: device.lastUsedAt,
                    createdAt: device.createdAt,
                })) || [],
            },
            sessions: sortedSessions,
        };
    }
    async updateUser(id, dto) {
        let user = await this.users.getUserById(id, { relations: ['roles', 'identities'] });
        if (!user) {
            throw new common_2.NotFoundException('User not found');
        }
        const oldEmail = user.email;
        const oldPhone = user.phone;
        if (dto.isActive !== undefined || dto.isVerified !== undefined || dto.metadata !== undefined ||
            dto.isMfaEnabled !== undefined || dto.email !== undefined || dto.phone !== undefined) {
            const updates = {};
            if (dto.isActive !== undefined)
                updates.isActive = dto.isActive;
            if (dto.isVerified !== undefined)
                updates.isVerified = dto.isVerified;
            if (dto.metadata !== undefined)
                updates.metadata = dto.metadata;
            if (dto.isMfaEnabled !== undefined)
                updates.isMfaEnabled = dto.isMfaEnabled;
            if (dto.email !== undefined)
                updates.email = dto.email;
            if (dto.phone !== undefined)
                updates.phone = dto.phone;
            user = await this.users.updateUser(id, updates);
        }
        if (dto.email !== undefined && dto.email !== oldEmail) {
            if (oldEmail) {
                const oldEmailIdentity = user.identities?.find(i => i.provider === auth_constants_1.EMAIL_AUTH_PROVIDER && i.providerId === oldEmail);
                if (oldEmailIdentity) {
                    await oldEmailIdentity.remove();
                }
            }
            if (dto.email && user.emailVerifiedAt) {
                await user.findOrCreateIdentity(auth_constants_1.EMAIL_AUTH_PROVIDER, dto.email);
            }
        }
        if (dto.phone !== undefined && dto.phone !== oldPhone) {
            if (oldPhone) {
                const oldPhoneIdentity = user.identities?.find(i => i.provider === auth_constants_1.PHONE_AUTH_PROVIDER && i.providerId === oldPhone);
                if (oldPhoneIdentity) {
                    await oldPhoneIdentity.remove();
                }
            }
            if (dto.phone && user.phoneVerifiedAt) {
                await user.findOrCreateIdentity(auth_constants_1.PHONE_AUTH_PROVIDER, dto.phone);
            }
        }
        if (dto.emailLoginEnabled !== undefined) {
            if (dto.emailLoginEnabled) {
                if (!user.email) {
                    throw new common_2.NotFoundException('User has no email address');
                }
                user.emailVerifiedAt = user.emailVerifiedAt || new Date();
                await user.findOrCreateIdentity(auth_constants_1.EMAIL_AUTH_PROVIDER, user.email);
            }
            else {
                user.emailVerifiedAt = null;
                const emailIdentity = user.identities?.find(i => i.provider === auth_constants_1.EMAIL_AUTH_PROVIDER);
                if (emailIdentity) {
                    await emailIdentity.remove();
                }
            }
        }
        if (dto.phoneLoginEnabled !== undefined) {
            if (dto.phoneLoginEnabled) {
                if (!user.phone) {
                    throw new common_2.NotFoundException('User has no phone number');
                }
                user.phoneVerifiedAt = user.phoneVerifiedAt || new Date();
                await user.findOrCreateIdentity(auth_constants_1.PHONE_AUTH_PROVIDER, user.phone);
            }
            else {
                user.phoneVerifiedAt = null;
                const phoneIdentity = user.identities?.find(i => i.provider === auth_constants_1.PHONE_AUTH_PROVIDER);
                if (phoneIdentity) {
                    await phoneIdentity.remove();
                }
            }
        }
        if (dto.password) {
            await user.setPassword(dto.password);
        }
        if (dto.roles) {
            await user.assignRolesWithMultipleGuard(dto.roles);
        }
        await user.save();
        return { user: this.toSafeUser(user) };
    }
    async deleteTotpDevice(id, deviceId) {
        const user = await this.users.getUserById(id);
        if (!user) {
            throw new common_2.NotFoundException('User not found');
        }
        const device = await this.mfaSecretRepository.findOne({
            where: { id: deviceId, userId: user.id },
        });
        if (!device) {
            throw new common_2.NotFoundException('TOTP device not found');
        }
        await this.mfaSecretRepository.remove(device);
        return { message: 'TOTP device deleted successfully' };
    }
    async listSessions(id) {
        const user = await this.ensureUserExists(id);
        const sessions = await this.sessionManager.getUserSessions(user.id);
        return {
            data: sessions
                .sort((a, b) => {
                const aTime = a.lastActive?.getTime() ?? a.updatedAt?.getTime() ?? 0;
                const bTime = b.lastActive?.getTime() ?? b.updatedAt?.getTime() ?? 0;
                return bTime - aTime;
            })
                .map((session) => this.toSessionResponse(session)),
        };
    }
    async revokeSession(id, sessionId) {
        const user = await this.ensureUserExists(id);
        try {
            const session = await this.sessionManager.getSession(sessionId, false);
            if (session.userId !== user.id) {
                throw new common_2.NotFoundException('Session not found for this user');
            }
        }
        catch {
            throw new common_2.NotFoundException('Session not found for this user');
        }
        await this.sessionManager.revokeSession(sessionId);
        return { message: 'Session revoked successfully' };
    }
    async revokeAllSessions(id) {
        const user = await this.ensureUserExists(id);
        await this.sessionManager.revokeAllUserSessions(user.id);
        return { message: 'All sessions revoked successfully' };
    }
    async deleteUser(id) {
        await this.users.deleteUser(id);
        return { message: 'User removed' };
    }
    toSafeUser(user) {
        if (!user) {
            return null;
        }
        return {
            id: user.id,
            email: user.email,
            phone: user.phone,
            tenantId: user.tenantId,
            isActive: user.isActive,
            isVerified: user.isVerified,
            metadata: user.metadata ?? {},
            roles: user.roles?.map((role) => role.name) ?? [],
            createdAt: user.createdAt,
            updatedAt: user.updatedAt,
            emailVerifiedAt: user.emailVerifiedAt,
            phoneVerifiedAt: user.phoneVerifiedAt,
            isMfaEnabled: user.isMfaEnabled,
        };
    }
};
exports.AdminUsersController = AdminUsersController;
__decorate([
    (0, common_1.Get)(),
    __param(0, (0, common_1.Query)('page')),
    __param(1, (0, common_1.Query)('limit')),
    __param(2, (0, common_1.Query)('search')),
    __param(3, (0, common_1.Query)('status')),
    __param(4, (0, common_1.Query)('tenantId')),
    __param(5, (0, common_1.Query)('roleName')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String, String, String, String, String]),
    __metadata("design:returntype", Promise)
], AdminUsersController.prototype, "listUsers", null);
__decorate([
    (0, common_1.Post)(),
    __param(0, (0, common_1.Body)()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [admin_user_dto_1.AdminCreateUserDto]),
    __metadata("design:returntype", Promise)
], AdminUsersController.prototype, "createUser", null);
__decorate([
    (0, common_1.Get)(':id'),
    __param(0, (0, common_1.Param)('id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], AdminUsersController.prototype, "getUser", null);
__decorate([
    (0, common_1.Patch)(':id'),
    __param(0, (0, common_1.Param)('id')),
    __param(1, (0, common_1.Body)()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, admin_user_dto_1.AdminUpdateUserDto]),
    __metadata("design:returntype", Promise)
], AdminUsersController.prototype, "updateUser", null);
__decorate([
    (0, common_1.Delete)(':id/totp-devices/:deviceId'),
    __param(0, (0, common_1.Param)('id')),
    __param(1, (0, common_1.Param)('deviceId')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String]),
    __metadata("design:returntype", Promise)
], AdminUsersController.prototype, "deleteTotpDevice", null);
__decorate([
    (0, common_1.Get)(':id/sessions'),
    __param(0, (0, common_1.Param)('id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], AdminUsersController.prototype, "listSessions", null);
__decorate([
    (0, common_1.Delete)(':id/sessions/:sessionId'),
    __param(0, (0, common_1.Param)('id')),
    __param(1, (0, common_1.Param)('sessionId')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String]),
    __metadata("design:returntype", Promise)
], AdminUsersController.prototype, "revokeSession", null);
__decorate([
    (0, common_1.Delete)(':id/sessions'),
    __param(0, (0, common_1.Param)('id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], AdminUsersController.prototype, "revokeAllSessions", null);
__decorate([
    (0, common_1.Delete)(':id'),
    __param(0, (0, common_1.Param)('id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], AdminUsersController.prototype, "deleteUser", null);
exports.AdminUsersController = AdminUsersController = __decorate([
    (0, common_1.Controller)('auth/admin/api/users'),
    (0, common_1.UseGuards)(admin_session_guard_1.AdminSessionGuard),
    __param(4, (0, typeorm_1.InjectRepository)(mfa_secret_entity_1.NestAuthMFASecret)),
    __metadata("design:paramtypes", [user_service_1.UserService,
        tenant_service_1.TenantService,
        mfa_service_1.MfaService,
        session_manager_service_1.SessionManagerService,
        typeorm_2.Repository])
], AdminUsersController);
//# sourceMappingURL=admin-users.controller.js.map