"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); }
};
var AdminService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AdminService = void 0;
const common_1 = require("@nestjs/common");
const config_1 = require("@nestjs/config");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const user_entity_1 = require("../../database/entities/user.entity");
const group_entity_1 = require("../../database/entities/group.entity");
const group_member_entity_1 = require("../../database/entities/group-member.entity");
const expense_entity_1 = require("../../database/entities/expense.entity");
const settlement_entity_1 = require("../../database/entities/settlement.entity");
const group_invite_entity_1 = require("../../database/entities/group-invite.entity");
const admin_audit_log_entity_1 = require("../../database/entities/admin-audit-log.entity");
const enums_1 = require("../../common/enums");
let AdminService = AdminService_1 = class AdminService {
    constructor(userRepo, groupRepo, groupMemberRepo, expenseRepo, settlementRepo, groupInviteRepo, adminAuditLogRepo, config) {
        this.userRepo = userRepo;
        this.groupRepo = groupRepo;
        this.groupMemberRepo = groupMemberRepo;
        this.expenseRepo = expenseRepo;
        this.settlementRepo = settlementRepo;
        this.groupInviteRepo = groupInviteRepo;
        this.adminAuditLogRepo = adminAuditLogRepo;
        this.config = config;
        this.logger = new common_1.Logger(AdminService_1.name);
    }
    async onModuleInit() {
        const email = this.config.get('app.platformAdminEmail', '');
        if (!email)
            return;
        const user = await this.userRepo.findOne({ where: { email } });
        if (!user) {
            this.logger.warn(`PLATFORM_ADMIN_EMAIL=${email} — user not found yet`);
            return;
        }
        if (user.isPlatformAdmin)
            return;
        await this.userRepo.update({ id: user.id }, { isPlatformAdmin: true });
        this.logger.log(`Promoted ${email} to platform admin`);
    }
    async getMe(actor) {
        return {
            id: actor.id,
            email: actor.email,
            name: actor.name,
            isPlatformAdmin: actor.isPlatformAdmin,
        };
    }
    async getOverview() {
        const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
        const [totalUsers, activeUsers, unverifiedUsers, totalGroups, activeGroups, totalExpenses, expensesLast7Days, settlementsLast7Days, pendingInvites,] = await Promise.all([
            this.userRepo.count(),
            this.userRepo.count({ where: { isActive: true } }),
            this.userRepo.count({ where: { isEmailVerified: false, passwordHash: (0, typeorm_2.Not)((0, typeorm_2.IsNull)()) } }),
            this.groupRepo.count(),
            this.groupRepo.count({ where: { isActive: true } }),
            this.expenseRepo.count(),
            this.expenseRepo.count({ where: { createdAt: (0, typeorm_2.MoreThanOrEqual)(sevenDaysAgo) } }),
            this.settlementRepo.count({ where: { createdAt: (0, typeorm_2.MoreThanOrEqual)(sevenDaysAgo) } }),
            this.groupInviteRepo.count({ where: { status: enums_1.InviteStatus.Pending } }),
        ]);
        return {
            totalUsers,
            activeUsers,
            unverifiedUsers,
            totalGroups,
            activeGroups,
            totalExpenses,
            expensesLast7Days,
            settlementsLast7Days,
            pendingInvites,
        };
    }
    async listUsers(q, page, pageSize) {
        const trimmed = q.trim();
        const where = trimmed
            ? [
                { name: (0, typeorm_2.Like)(`%${trimmed}%`) },
                { email: (0, typeorm_2.Like)(`%${trimmed.toLowerCase()}%`) },
            ]
            : undefined;
        const [total, users] = await Promise.all([
            this.userRepo.count({ where: where }),
            this.userRepo.find({
                where: where,
                order: { createdAt: 'DESC' },
                skip: (page - 1) * pageSize,
                take: pageSize,
            }),
        ]);
        const items = await Promise.all(users.map(async (u) => ({
            id: u.id,
            email: u.email,
            name: u.name,
            isEmailVerified: u.isEmailVerified,
            isPlatformAdmin: u.isPlatformAdmin,
            isActive: u.isActive,
            createdAt: u.createdAt,
            groupCount: await this.groupMemberRepo.count({
                where: { userId: u.id, leftAt: (0, typeorm_2.IsNull)() },
            }),
        })));
        return { total, page, pageSize, items };
    }
    async getUser(id) {
        const user = await this.userRepo.findOne({ where: { id } });
        if (!user)
            throw new common_1.NotFoundException('User not found');
        const [groupCount, expenseCount, sentSettlements, receivedSettlements] = await Promise.all([
            this.groupMemberRepo.count({ where: { userId: id, leftAt: (0, typeorm_2.IsNull)() } }),
            this.expenseRepo.count({ where: { createdById: id } }),
            this.settlementRepo.count({ where: { payerId: id } }),
            this.settlementRepo.count({ where: { payeeId: id } }),
        ]);
        return {
            id: user.id,
            email: user.email,
            name: user.name,
            isEmailVerified: user.isEmailVerified,
            isPlatformAdmin: user.isPlatformAdmin,
            isActive: user.isActive,
            createdAt: user.createdAt,
            groupCount,
            expenseCount,
            settlementCount: sentSettlements + receivedSettlements,
        };
    }
    async updateUser(actor, id, dto) {
        const user = await this.userRepo.findOne({ where: { id } });
        if (!user)
            throw new common_1.NotFoundException('User not found');
        if (id === actor.id) {
            if (dto.isActive === false) {
                throw new common_1.BadRequestException('You cannot deactivate your own account');
            }
            if (dto.isPlatformAdmin === false) {
                throw new common_1.BadRequestException('You cannot remove your own platform admin role');
            }
        }
        if (dto.isPlatformAdmin === false && user.isPlatformAdmin) {
            const adminCount = await this.userRepo.count({
                where: { isPlatformAdmin: true, isActive: true },
            });
            if (adminCount <= 1) {
                throw new common_1.BadRequestException('Cannot remove the last platform admin');
            }
        }
        await this.userRepo.update({ id }, {
            ...(dto.isActive !== undefined && { isActive: dto.isActive }),
            ...(dto.isEmailVerified !== undefined && { isEmailVerified: dto.isEmailVerified }),
            ...(dto.isPlatformAdmin !== undefined && { isPlatformAdmin: dto.isPlatformAdmin }),
            ...(dto.isActive === false && { refreshTokenHash: null }),
        });
        await this.audit(actor.id, 'user.update', 'user', id, dto);
        return this.getUser(id);
    }
    async deleteUser(actor, id) {
        if (id === actor.id) {
            throw new common_1.BadRequestException('You cannot delete your own account from admin');
        }
        const user = await this.userRepo.findOne({ where: { id } });
        if (!user)
            throw new common_1.NotFoundException('User not found');
        const adminMemberships = await this.groupMemberRepo.find({
            where: {
                userId: id,
                role: enums_1.GroupRole.Admin,
                leftAt: (0, typeorm_2.IsNull)(),
                group: { isActive: true },
            },
        });
        for (const membership of adminMemberships) {
            const adminCount = await this.groupMemberRepo.count({
                where: { groupId: membership.groupId, role: enums_1.GroupRole.Admin, leftAt: (0, typeorm_2.IsNull)() },
            });
            if (adminCount === 1) {
                throw new common_1.BadRequestException('User is sole admin of a group — transfer admin first');
            }
        }
        await this.userRepo.delete({ id });
        await this.audit(actor.id, 'user.delete', 'user', id, { email: user.email });
        return { message: 'User deleted' };
    }
    async listGroups(q, page, pageSize) {
        const trimmed = q.trim();
        const where = trimmed
            ? [
                { name: (0, typeorm_2.Like)(`%${trimmed}%`) },
                { description: (0, typeorm_2.Like)(`%${trimmed}%`) },
            ]
            : undefined;
        const [total, groups] = await Promise.all([
            this.groupRepo.count({ where: where }),
            this.groupRepo.find({
                where: where,
                order: { createdAt: 'DESC' },
                skip: (page - 1) * pageSize,
                take: pageSize,
            }),
        ]);
        const items = await Promise.all(groups.map(async (g) => ({
            id: g.id,
            name: g.name,
            currency: g.currency,
            isActive: g.isActive,
            memberCount: await this.groupMemberRepo.count({
                where: { groupId: g.id, leftAt: (0, typeorm_2.IsNull)() },
            }),
            expenseCount: await this.expenseRepo.count({ where: { groupId: g.id } }),
            createdAt: g.createdAt,
        })));
        return { total, page, pageSize, items };
    }
    async getGroup(id) {
        const group = await this.groupRepo.findOne({ where: { id } });
        if (!group)
            throw new common_1.NotFoundException('Group not found');
        const members = await this.groupMemberRepo.find({
            where: { groupId: id, leftAt: (0, typeorm_2.IsNull)() },
            relations: { user: true },
        });
        const [memberCount, expenseCount, settlementCount] = await Promise.all([
            this.groupMemberRepo.count({ where: { groupId: id, leftAt: (0, typeorm_2.IsNull)() } }),
            this.expenseRepo.count({ where: { groupId: id } }),
            this.settlementRepo.count({ where: { groupId: id } }),
        ]);
        return {
            id: group.id,
            name: group.name,
            currency: group.currency,
            isActive: group.isActive,
            memberCount,
            expenseCount,
            settlementCount,
            createdAt: group.createdAt,
            members: members.map((m) => ({
                userId: m.user.id,
                name: m.user.name,
                email: m.user.email,
                role: m.role,
            })),
        };
    }
    async updateGroup(actor, id, dto) {
        const group = await this.groupRepo.findOne({ where: { id } });
        if (!group)
            throw new common_1.NotFoundException('Group not found');
        await this.groupRepo.update({ id }, {
            ...(dto.isActive !== undefined && { isActive: dto.isActive }),
        });
        await this.audit(actor.id, 'group.update', 'group', id, dto);
        return this.getGroup(id);
    }
    async deleteGroup(actor, id) {
        const group = await this.groupRepo.findOne({ where: { id } });
        if (!group)
            throw new common_1.NotFoundException('Group not found');
        await this.groupRepo.update({ id }, { isActive: false });
        await this.audit(actor.id, 'group.deactivate', 'group', id, { name: group.name });
        return { message: 'Group deactivated' };
    }
    async listAudit(page, pageSize) {
        const [total, logs] = await Promise.all([
            this.adminAuditLogRepo.count(),
            this.adminAuditLogRepo.find({
                order: { createdAt: 'DESC' },
                skip: (page - 1) * pageSize,
                take: pageSize,
                relations: { actor: true },
            }),
        ]);
        return {
            total,
            page,
            pageSize,
            items: logs.map((l) => ({
                id: l.id,
                actorId: l.actorId,
                actorName: l.actor.name,
                action: l.action,
                targetType: l.targetType,
                targetId: l.targetId,
                meta: l.meta,
                createdAt: l.createdAt,
            })),
        };
    }
    async audit(actorId, action, targetType, targetId, meta) {
        const entry = this.adminAuditLogRepo.create({
            actorId,
            action,
            targetType,
            targetId,
            meta: meta ? JSON.stringify(meta) : null,
        });
        await this.adminAuditLogRepo.save(entry);
    }
};
exports.AdminService = AdminService;
exports.AdminService = AdminService = AdminService_1 = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(user_entity_1.User)),
    __param(1, (0, typeorm_1.InjectRepository)(group_entity_1.Group)),
    __param(2, (0, typeorm_1.InjectRepository)(group_member_entity_1.GroupMember)),
    __param(3, (0, typeorm_1.InjectRepository)(expense_entity_1.Expense)),
    __param(4, (0, typeorm_1.InjectRepository)(settlement_entity_1.Settlement)),
    __param(5, (0, typeorm_1.InjectRepository)(group_invite_entity_1.GroupInvite)),
    __param(6, (0, typeorm_1.InjectRepository)(admin_audit_log_entity_1.AdminAuditLog)),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        config_1.ConfigService])
], AdminService);
//# sourceMappingURL=admin.service.js.map