"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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AnnouncementsService = void 0;
const common_1 = require("@nestjs/common");
const client_1 = require("@prisma/client");
const audit_service_1 = require("../audit/audit.service");
const estate_scope_1 = require("../common/estate-scope");
const plan_limits_1 = require("../common/plan-limits");
const prisma_service_1 = require("../database/prisma.service");
const blockedCampaignTerms = [
    'advert',
    'buy now',
    'campaign',
    'election',
    'vote for',
    'political',
    'promo',
    'discount sale',
    'sponsored',
    'external business'
];
const announcementCategories = [
    { label: 'Security', category: client_1.AnnouncementCategory.SECURITY },
    { label: 'Maintenance', category: client_1.AnnouncementCategory.MAINTENANCE },
    { label: 'Estate News', category: client_1.AnnouncementCategory.NEWS },
    { label: 'Events', category: client_1.AnnouncementCategory.EVENTS },
    { label: 'Utility Notice', category: client_1.AnnouncementCategory.NOTICES },
    { label: 'Parking', category: client_1.AnnouncementCategory.NOTICES, metadataKey: 'PARKING' },
    { label: 'Visitor Policy', category: client_1.AnnouncementCategory.NOTICES, metadataKey: 'VISITOR_POLICY' },
    { label: 'Emergency', category: client_1.AnnouncementCategory.EMERGENCY }
];
let AnnouncementsService = class AnnouncementsService {
    prisma;
    audit;
    constructor(prisma, audit) {
        this.prisma = prisma;
        this.audit = audit;
    }
    findAll(user, estateId) {
        const scopedEstateId = (0, estate_scope_1.resolveEstateScope)(user, estateId);
        return this.prisma.estateAnnouncement.findMany({
            where: {
                estateId: scopedEstateId,
                status: client_1.AnnouncementStatus.PUBLISHED,
                OR: [{ expiresAt: null }, { expiresAt: { gt: new Date() } }]
            },
            orderBy: { publishedAt: 'desc' },
            take: 100,
            include: { author: { select: { id: true, name: true, role: true } } }
        });
    }
    findOne(user, id) {
        const estateId = (0, estate_scope_1.resolveEstateScope)(user, undefined);
        return this.prisma.estateAnnouncement
            .findFirst({
            where: {
                id,
                estateId,
                status: client_1.AnnouncementStatus.PUBLISHED,
                OR: [{ expiresAt: null }, { expiresAt: { gt: new Date() } }]
            },
            include: {
                author: { select: { id: true, name: true, role: true } },
                notifications: {
                    where: { userId: user.id },
                    select: { id: true, status: true, readAt: true },
                    take: 1
                }
            }
        })
            .then((announcement) => {
            if (!announcement) {
                throw new common_1.NotFoundException('Announcement not found');
            }
            return announcement;
        });
    }
    getOptions() {
        return {
            categories: announcementCategories,
            priorities: Object.values(client_1.AnnouncementPriority),
            contentControls: {
                adsAllowed: false,
                titleMaxLength: 80,
                bodyMaxLength: 700,
                flaggedKeywordApproval: true,
                auditLogs: true
            }
        };
    }
    async create(dto, user) {
        const estateId = (0, estate_scope_1.resolveEstateScope)(user, dto.estateId);
        this.assertRoleCanSend(user.role, dto);
        const blocked = this.findBlockedTerm(`${dto.title} ${dto.body}`);
        if (blocked) {
            await this.audit.record({
                estateId,
                actorType: 'USER',
                actorId: user.id,
                action: 'ANNOUNCEMENT_BLOCKED_FOR_REVIEW',
                metadata: { term: blocked, category: dto.category, title: dto.title }
            });
            throw new common_1.BadRequestException('Announcement appears promotional or unrelated and has been blocked for review');
        }
        await this.assertPlanLimit(estateId, dto);
        const targetRoles = (dto.targetRoles?.length ? dto.targetRoles : [client_1.UserRole.RESIDENT]).map(String);
        const deliveryChannels = dto.deliveryChannels?.length ? dto.deliveryChannels : [client_1.NotificationChannel.IN_APP, client_1.NotificationChannel.PUSH];
        const announcement = await this.prisma.$transaction(async (tx) => {
            const created = await tx.estateAnnouncement.create({
                data: {
                    estateId,
                    authorId: user.id,
                    title: dto.title.trim(),
                    body: dto.body.trim(),
                    category: dto.category,
                    priority: dto.priority ?? client_1.AnnouncementPriority.NORMAL,
                    status: client_1.AnnouncementStatus.PUBLISHED,
                    targetRoles,
                    targetUserIds: dto.targetUserIds ?? [],
                    deliveryChannels,
                    publishedAt: new Date(),
                    expiresAt: dto.expiresAt,
                    metadata: this.toJson({
                        ...(dto.metadata ?? {}),
                        emergencyDelivery: dto.priority === client_1.AnnouncementPriority.EMERGENCY || dto.category === client_1.AnnouncementCategory.EMERGENCY
                    })
                }
            });
            const recipientOr = [{ role: { in: targetRoles } }];
            if (dto.targetUserIds?.length) {
                recipientOr.push({ id: { in: dto.targetUserIds } });
            }
            const users = await tx.user.findMany({
                where: {
                    estateId,
                    status: 'ACTIVE',
                    OR: recipientOr
                },
                select: { id: true, email: true }
            });
            await tx.appNotification.createMany({
                data: users.flatMap((recipient) => deliveryChannels.map((channel) => ({
                    estateId,
                    userId: recipient.id,
                    announcementId: created.id,
                    channel,
                    status: client_1.NotificationStatus.PENDING,
                    title: created.title,
                    body: created.body,
                    data: {
                        category: created.category,
                        priority: created.priority,
                        deepLink: '/announcements',
                        emailAvailable: Boolean(recipient.email)
                    }
                })))
            });
            return created;
        });
        await this.audit.record({
            estateId,
            actorType: 'USER',
            actorId: user.id,
            action: 'ESTATE_ANNOUNCEMENT_SENT',
            metadata: {
                announcementId: announcement.id,
                category: announcement.category,
                priority: announcement.priority,
                channels: deliveryChannels
            }
        });
        return announcement;
    }
    assertRoleCanSend(role, dto) {
        if (role === client_1.UserRole.ESTATE_ADMIN || role === client_1.UserRole.SUPER_ADMIN) {
            return;
        }
        if (role === client_1.UserRole.SECURITY_SUPERVISOR) {
            const allowed = [
                client_1.AnnouncementCategory.SECURITY,
                client_1.AnnouncementCategory.EMERGENCY,
                client_1.AnnouncementCategory.NOTICES,
                client_1.AnnouncementCategory.MAINTENANCE
            ];
            if (!allowed.includes(dto.category)) {
                throw new common_1.ForbiddenException('Supervisors can only send security, gate, maintenance, notice, and emergency messages');
            }
            return;
        }
        if (role === client_1.UserRole.GUARD && (dto.category === client_1.AnnouncementCategory.EMERGENCY || dto.priority === client_1.AnnouncementPriority.EMERGENCY)) {
            return;
        }
        throw new common_1.ForbiddenException('This role cannot send estate announcements');
    }
    async assertPlanLimit(estateId, dto) {
        const estate = await this.prisma.estate.findUnique({
            where: { id: estateId },
            select: { subscriptionPlan: { select: { limits: true } } }
        });
        const limits = estate?.subscriptionPlan?.limits;
        const isEmergency = dto.category === client_1.AnnouncementCategory.EMERGENCY || dto.priority === client_1.AnnouncementPriority.EMERGENCY;
        const limitKey = isEmergency ? 'sosAlertsPerMonth' : 'announcementsPerWeek';
        const periodStart = isEmergency ? (0, plan_limits_1.startOfCurrentMonth)() : (0, plan_limits_1.startOfCurrentWeek)();
        const limit = (0, plan_limits_1.getNumericPlanLimit)(limits, limitKey);
        const monthlyAnnouncementLimit = !isEmergency ? (0, plan_limits_1.getNumericPlanLimit)(limits, 'announcementsPerMonth') : null;
        if (limit !== null) {
            const used = await this.prisma.estateAnnouncement.count({
                where: {
                    estateId,
                    createdAt: { gte: periodStart },
                    ...(isEmergency
                        ? { OR: [{ category: client_1.AnnouncementCategory.EMERGENCY }, { priority: client_1.AnnouncementPriority.EMERGENCY }] }
                        : {})
                }
            });
            if (used >= limit) {
                throw new common_1.BadRequestException('Announcement limit for this plan has been reached');
            }
        }
        if (monthlyAnnouncementLimit !== null) {
            const used = await this.prisma.estateAnnouncement.count({
                where: { estateId, createdAt: { gte: (0, plan_limits_1.startOfCurrentMonth)() } }
            });
            if (used >= monthlyAnnouncementLimit) {
                throw new common_1.BadRequestException('Monthly announcement limit for this plan has been reached');
            }
        }
    }
    findBlockedTerm(value) {
        const normalized = value.toLowerCase();
        return blockedCampaignTerms.find((term) => normalized.includes(term));
    }
    toJson(value) {
        return JSON.parse(JSON.stringify(value));
    }
};
exports.AnnouncementsService = AnnouncementsService;
exports.AnnouncementsService = AnnouncementsService = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [prisma_service_1.PrismaService,
        audit_service_1.AuditService])
], AnnouncementsService);
//# sourceMappingURL=announcements.service.js.map