"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.usersService = void 0;
const bcryptjs_1 = __importDefault(require("bcryptjs"));
const database_1 = require("../../config/database");
const error_handler_1 = require("../../middleware/error-handler");
const roles_1 = require("../../utils/roles");
const provisioning_service_1 = require("../../services/user-provisioning/provisioning.service");
const logger_1 = require("../../utils/logger");
const userSelect = {
    id: true,
    email: true,
    name: true,
    phone: true,
    pronouns: true,
    role: true,
    roles: true,
    status: true,
    permissions: true,
    createdVia: true,
    expiresAt: true,
    expireDays: true,
    lastLoginAt: true,
    emailVerified: true,
    createdAt: true,
    updatedAt: true,
};
exports.usersService = {
    async findAll(filters) {
        const { page, limit, search, role, status } = filters;
        const skip = (page - 1) * limit;
        const where = {};
        if (search) {
            where.OR = [
                { email: { contains: search, mode: 'insensitive' } },
                { name: { contains: search, mode: 'insensitive' } },
            ];
        }
        if (role)
            where.role = role;
        if (status)
            where.status = status;
        const [users, total] = await Promise.all([
            database_1.prisma.user.findMany({
                where,
                select: userSelect,
                skip,
                take: limit,
                orderBy: { createdAt: 'desc' },
            }),
            database_1.prisma.user.count({ where }),
        ]);
        return {
            users,
            pagination: {
                page,
                limit,
                total,
                totalPages: Math.ceil(total / limit),
            },
        };
    },
    async findById(id) {
        const user = await database_1.prisma.user.findUnique({
            where: { id },
            select: userSelect,
        });
        if (!user) {
            throw new error_handler_1.AppError(404, 'User not found', 'USER_NOT_FOUND');
        }
        return user;
    },
    async create(data) {
        const existing = await database_1.prisma.user.findUnique({ where: { email: data.email } });
        if (existing) {
            throw new error_handler_1.AppError(409, 'Email already registered', 'EMAIL_EXISTS');
        }
        const hashedPassword = await bcryptjs_1.default.hash(data.password, 12);
        // Compute roles array and primary role
        const roles = data.roles && data.roles.length > 0
            ? data.roles
            : [data.role || 'USER'];
        const primaryRole = (0, roles_1.getPrimaryRole)(roles);
        const user = await database_1.prisma.user.create({
            data: {
                ...data,
                password: hashedPassword,
                role: primaryRole,
                roles: JSON.parse(JSON.stringify(roles)),
                emailVerified: true, // Admin-created users are pre-verified
                expiresAt: data.expiresAt ? new Date(data.expiresAt) : undefined,
            },
            select: userSelect,
        });
        // Fire-and-forget: auto-link to existing Contact with matching email
        database_1.prisma.contact.findFirst({
            where: { email: { equals: data.email, mode: 'insensitive' }, userId: null, mergedIntoId: null },
        }).then(async (existingContact) => {
            if (existingContact) {
                await database_1.prisma.contact.update({ where: { id: existingContact.id }, data: { userId: user.id } });
                logger_1.logger.info(`Auto-linked contact ${existingContact.id} to new user ${user.id}`);
            }
        }).catch(err => {
            logger_1.logger.warn('Auto-link contact on user creation failed:', err);
        });
        // Fire-and-forget: provision to eager services
        provisioning_service_1.userProvisioningService.onUserCreated(toCMUser(user)).catch(err => {
            logger_1.logger.warn('User provisioning hook (create) failed:', err);
        });
        return user;
    },
    async update(id, data) {
        const existing = await database_1.prisma.user.findUnique({ where: { id } });
        if (!existing) {
            throw new error_handler_1.AppError(404, 'User not found', 'USER_NOT_FOUND');
        }
        if (data.email && data.email !== existing.email) {
            const emailTaken = await database_1.prisma.user.findUnique({ where: { email: data.email } });
            if (emailTaken) {
                throw new error_handler_1.AppError(409, 'Email already in use', 'EMAIL_EXISTS');
            }
        }
        const updateData = { ...data };
        if (data.password) {
            updateData.password = await bcryptjs_1.default.hash(data.password, 12);
        }
        if (data.expiresAt !== undefined) {
            updateData.expiresAt = data.expiresAt ? new Date(data.expiresAt) : null;
        }
        // Sync roles and primary role
        if (data.roles) {
            updateData.roles = JSON.parse(JSON.stringify(data.roles));
            updateData.role = (0, roles_1.getPrimaryRole)(data.roles);
        }
        else if (data.role) {
            // If only primary role changed, update roles array too
            const currentRoles = Array.isArray(existing.roles) ? existing.roles : [existing.role];
            if (!currentRoles.includes(data.role)) {
                updateData.roles = JSON.parse(JSON.stringify([...currentRoles, data.role]));
            }
        }
        const user = await database_1.prisma.user.update({
            where: { id },
            data: updateData,
            select: userSelect,
        });
        // Invalidate sessions when user is deactivated or banned
        const deactivatedStatuses = ['INACTIVE', 'BANNED', 'PENDING_APPROVAL', 'PENDING_VERIFICATION'];
        if (data.status && deactivatedStatuses.includes(data.status)) {
            await database_1.prisma.refreshToken.deleteMany({ where: { userId: id } });
        }
        // Invalidate sessions when password is changed by admin
        if (data.password) {
            await database_1.prisma.refreshToken.deleteMany({ where: { userId: id } });
        }
        // Fire-and-forget: sync changes to provisioned services
        provisioning_service_1.userProvisioningService.onUserUpdated(toCMUser(user), data).catch(err => {
            logger_1.logger.warn('User provisioning hook (update) failed:', err);
        });
        return user;
    },
    async delete(id) {
        const existing = await database_1.prisma.user.findUnique({ where: { id } });
        if (!existing) {
            throw new error_handler_1.AppError(404, 'User not found', 'USER_NOT_FOUND');
        }
        // Fire-and-forget: deactivate in provisioned services BEFORE deleting
        const cmUser = toCMUser(existing);
        provisioning_service_1.userProvisioningService.onUserDeactivated(cmUser).catch(err => {
            logger_1.logger.warn('User provisioning hook (delete) failed:', err);
        });
        await database_1.prisma.user.delete({ where: { id } });
    },
};
/** Convert a Prisma User (or select result) to the CMUser shape needed by provisioners */
function toCMUser(user) {
    return {
        id: user.id,
        email: user.email,
        name: user.name,
        role: user.role,
        roles: user.roles,
        status: user.status,
        permissions: user.permissions,
    };
}
//# sourceMappingURL=users.service.js.map