"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.responsesService = void 0;
const crypto_1 = require("crypto");
const client_1 = require("@prisma/client");
const database_1 = require("../../../config/database");
const error_handler_1 = require("../../../middleware/error-handler");
const email_service_1 = require("../../../services/email.service");
const notification_queue_service_1 = require("../../../services/notification-queue.service");
const notification_helper_1 = require("../../../services/notification.helper");
const env_1 = require("../../../config/env");
const logger_1 = require("../../../utils/logger");
const metrics_1 = require("../../../utils/metrics");
const rocketchat_webhook_service_1 = require("../../../services/rocketchat-webhook.service");
const VERIFICATION_EXPIRY_DAYS = 30;
exports.responsesService = {
    // --- Public ---
    async submitResponse(slug, data, senderIp) {
        const campaign = await database_1.prisma.campaign.findUnique({
            where: { slug },
            select: { id: true, slug: true, title: true, status: true, showResponseWall: true },
        });
        if (!campaign) {
            throw new error_handler_1.AppError(404, 'Campaign not found', 'CAMPAIGN_NOT_FOUND');
        }
        if (campaign.status !== client_1.CampaignStatus.ACTIVE) {
            throw new error_handler_1.AppError(400, 'Campaign is not active', 'CAMPAIGN_NOT_ACTIVE');
        }
        if (!campaign.showResponseWall) {
            throw new error_handler_1.AppError(400, 'Response wall is not enabled for this campaign', 'RESPONSE_WALL_DISABLED');
        }
        let verificationToken = null;
        let verificationSentAt = null;
        if (data.sendVerification && data.representativeEmail) {
            verificationToken = (0, crypto_1.randomBytes)(32).toString('hex');
            verificationSentAt = new Date();
        }
        const response = await database_1.prisma.representativeResponse.create({
            data: {
                campaignId: campaign.id,
                campaignSlug: campaign.slug,
                representativeName: data.representativeName,
                representativeTitle: data.representativeTitle,
                representativeLevel: data.representativeLevel,
                representativeEmail: data.representativeEmail,
                responseType: data.responseType,
                responseText: data.responseText,
                userComment: data.userComment,
                submittedByName: data.submittedByName,
                submittedByEmail: data.submittedByEmail,
                isAnonymous: data.isAnonymous,
                status: client_1.ResponseStatus.PENDING,
                verificationToken,
                verificationSentAt,
                submittedIp: senderIp,
            },
        });
        if (verificationToken && data.representativeEmail) {
            const baseUrl = env_1.env.API_URL;
            await email_service_1.emailService.sendResponseVerification({
                recipientEmail: data.representativeEmail,
                campaignTitle: campaign.title,
                responseType: data.responseType,
                responseText: data.responseText,
                submitterName: data.isAnonymous ? 'Anonymous' : (data.submittedByName || 'Anonymous'),
                verificationUrl: `${baseUrl}/api/responses/${response.id}/verify/${verificationToken}`,
                reportUrl: `${baseUrl}/api/responses/${response.id}/report/${verificationToken}`,
            });
        }
        (0, metrics_1.recordResponseSubmission)();
        // Notification: admin response submitted alert
        try {
            if (await (0, notification_helper_1.isNotificationEnabled)('notifyAdminResponseSubmitted')) {
                const adminEmails = await (0, notification_helper_1.getAdminEmailsByRole)([client_1.UserRole.SUPER_ADMIN, client_1.UserRole.INFLUENCE_ADMIN]);
                if (adminEmails.length > 0) {
                    const adminUrl = `${env_1.env.ADMIN_URL || 'http://localhost:3000'}/app/influence/responses`;
                    await notification_queue_service_1.notificationQueueService.enqueue({
                        type: 'admin-response-submitted',
                        adminEmails,
                        campaignTitle: campaign.title,
                        representativeName: data.representativeName,
                        responseType: data.responseType,
                        submitterName: data.isAnonymous ? 'Anonymous' : (data.submittedByName || 'Anonymous'),
                        adminUrl,
                    });
                }
            }
        }
        catch (err) {
            logger_1.logger.error('Failed to enqueue response submitted notification:', err);
        }
        // Notify Rocket.Chat
        rocketchat_webhook_service_1.rocketchatWebhookService.onCampaignResponseSubmitted({
            campaignTitle: campaign.title,
            representativeName: data.representativeName,
        }).catch(() => { });
        return {
            id: response.id,
            status: response.status,
            verificationSent: !!verificationToken,
        };
    },
    async listApproved(slug, filters) {
        const { page, limit, sort, level } = filters;
        const skip = (page - 1) * limit;
        const where = {
            campaignSlug: slug,
            status: client_1.ResponseStatus.APPROVED,
        };
        if (level)
            where.representativeLevel = level;
        let orderBy;
        switch (sort) {
            case 'upvotes':
                orderBy = { upvoteCount: 'desc' };
                break;
            case 'verified':
                orderBy = { isVerified: 'desc' };
                break;
            default:
                orderBy = { createdAt: 'desc' };
        }
        const [responses, total] = await Promise.all([
            database_1.prisma.representativeResponse.findMany({
                where,
                skip,
                take: limit,
                orderBy,
                select: {
                    id: true,
                    representativeName: true,
                    representativeTitle: true,
                    representativeLevel: true,
                    responseType: true,
                    responseText: true,
                    userComment: true,
                    submittedByName: true,
                    isAnonymous: true,
                    isVerified: true,
                    verifiedAt: true,
                    upvoteCount: true,
                    createdAt: true,
                },
            }),
            database_1.prisma.representativeResponse.count({ where }),
        ]);
        return {
            responses,
            pagination: {
                page,
                limit,
                total,
                totalPages: Math.ceil(total / limit),
            },
        };
    },
    async getStats(slug) {
        const where = { campaignSlug: slug, status: client_1.ResponseStatus.APPROVED };
        const [total, verified, upvoteSum, byLevel] = await Promise.all([
            database_1.prisma.representativeResponse.count({ where }),
            database_1.prisma.representativeResponse.count({ where: { ...where, isVerified: true } }),
            database_1.prisma.representativeResponse.aggregate({ where, _sum: { upvoteCount: true } }),
            database_1.prisma.representativeResponse.groupBy({
                by: ['representativeLevel'],
                where,
                _count: true,
            }),
        ]);
        const levelBreakdown = {};
        for (const row of byLevel) {
            levelBreakdown[row.representativeLevel] = row._count;
        }
        return {
            total,
            verified,
            totalUpvotes: upvoteSum._sum.upvoteCount || 0,
            byLevel: levelBreakdown,
        };
    },
    async upvote(responseId, userIp, userId) {
        // Verify response exists and is approved
        const response = await database_1.prisma.representativeResponse.findUnique({
            where: { id: responseId },
            select: { id: true, status: true },
        });
        if (!response)
            throw new error_handler_1.AppError(404, 'Response not found', 'RESPONSE_NOT_FOUND');
        if (response.status !== client_1.ResponseStatus.APPROVED)
            throw new error_handler_1.AppError(400, 'Response is not approved', 'RESPONSE_NOT_APPROVED');
        try {
            await database_1.prisma.responseUpvote.create({
                data: {
                    responseId,
                    userId: userId || null,
                    upvotedIp: !userId ? (userIp || null) : null,
                },
            });
            await database_1.prisma.representativeResponse.update({
                where: { id: responseId },
                data: { upvoteCount: { increment: 1 } },
            });
            return { success: true };
        }
        catch (err) {
            if (err.code === 'P2002') {
                return { success: false, alreadyUpvoted: true };
            }
            throw err;
        }
    },
    async removeUpvote(responseId, userIp, userId) {
        const where = { responseId };
        if (userId) {
            where.userId = userId;
        }
        else if (userIp) {
            where.upvotedIp = userIp;
        }
        else {
            return { success: false };
        }
        const deleted = await database_1.prisma.responseUpvote.deleteMany({ where });
        if (deleted.count > 0) {
            await database_1.prisma.representativeResponse.update({
                where: { id: responseId },
                data: {
                    upvoteCount: { decrement: 1 },
                },
            });
        }
        return { success: deleted.count > 0 };
    },
    async verify(responseId, token) {
        const response = await database_1.prisma.representativeResponse.findUnique({
            where: { id: responseId },
            select: {
                id: true,
                verificationToken: true,
                verificationSentAt: true,
                representativeEmail: true,
                campaign: { select: { title: true } },
            },
        });
        if (!response || response.verificationToken !== token) {
            return { success: false, reason: 'Invalid verification link' };
        }
        // Check 30-day expiry
        if (response.verificationSentAt) {
            const daysSinceSent = (Date.now() - response.verificationSentAt.getTime()) / (1000 * 60 * 60 * 24);
            if (daysSinceSent > VERIFICATION_EXPIRY_DAYS) {
                return { success: false, reason: 'Verification link has expired' };
            }
        }
        await database_1.prisma.representativeResponse.update({
            where: { id: responseId },
            data: {
                isVerified: true,
                verifiedAt: new Date(),
                verifiedBy: response.representativeEmail || 'Representative',
                status: client_1.ResponseStatus.APPROVED,
            },
        });
        return { success: true, campaignTitle: response.campaign.title };
    },
    async report(responseId, token) {
        const response = await database_1.prisma.representativeResponse.findUnique({
            where: { id: responseId },
            select: {
                id: true,
                verificationToken: true,
                representativeEmail: true,
                campaign: { select: { title: true } },
            },
        });
        if (!response || response.verificationToken !== token) {
            return { success: false, reason: 'Invalid verification link' };
        }
        await database_1.prisma.representativeResponse.update({
            where: { id: responseId },
            data: {
                status: client_1.ResponseStatus.REJECTED,
                isVerified: false,
                verifiedBy: `Disputed by ${response.representativeEmail || 'representative'}`,
            },
        });
        return { success: true, campaignTitle: response.campaign.title };
    },
    // --- Admin ---
    async findAll(filters) {
        const { page, limit, status, campaignId, search } = filters;
        const skip = (page - 1) * limit;
        const where = {};
        if (status)
            where.status = status;
        if (campaignId)
            where.campaignId = campaignId;
        if (search) {
            where.OR = [
                { representativeName: { contains: search, mode: 'insensitive' } },
                { responseText: { contains: search, mode: 'insensitive' } },
                { submittedByName: { contains: search, mode: 'insensitive' } },
            ];
        }
        const [responses, total] = await Promise.all([
            database_1.prisma.representativeResponse.findMany({
                where,
                skip,
                take: limit,
                orderBy: { createdAt: 'desc' },
                select: {
                    id: true,
                    representativeName: true,
                    representativeTitle: true,
                    representativeLevel: true,
                    representativeEmail: true,
                    responseType: true,
                    responseText: true,
                    userComment: true,
                    submittedByName: true,
                    submittedByEmail: true,
                    isAnonymous: true,
                    status: true,
                    isVerified: true,
                    verifiedAt: true,
                    verifiedBy: true,
                    upvoteCount: true,
                    createdAt: true,
                    campaign: { select: { id: true, title: true, slug: true } },
                },
            }),
            database_1.prisma.representativeResponse.count({ where }),
        ]);
        return {
            responses,
            pagination: {
                page,
                limit,
                total,
                totalPages: Math.ceil(total / limit),
            },
        };
    },
    async updateStatus(id, data) {
        const existing = await database_1.prisma.representativeResponse.findUnique({ where: { id } });
        if (!existing)
            throw new error_handler_1.AppError(404, 'Response not found', 'RESPONSE_NOT_FOUND');
        return database_1.prisma.representativeResponse.update({
            where: { id },
            data: { status: data.status },
        });
    },
    async deleteResponse(id) {
        const existing = await database_1.prisma.representativeResponse.findUnique({ where: { id } });
        if (!existing)
            throw new error_handler_1.AppError(404, 'Response not found', 'RESPONSE_NOT_FOUND');
        await database_1.prisma.representativeResponse.delete({ where: { id } });
    },
    async resendVerification(id) {
        const response = await database_1.prisma.representativeResponse.findUnique({
            where: { id },
            select: {
                id: true,
                representativeEmail: true,
                representativeName: true,
                responseType: true,
                responseText: true,
                submittedByName: true,
                isAnonymous: true,
                verificationToken: true,
                campaign: { select: { title: true } },
            },
        });
        if (!response)
            throw new error_handler_1.AppError(404, 'Response not found', 'RESPONSE_NOT_FOUND');
        if (!response.representativeEmail) {
            throw new error_handler_1.AppError(400, 'No representative email on record', 'NO_REPRESENTATIVE_EMAIL');
        }
        // Regenerate token
        const verificationToken = response.verificationToken || (0, crypto_1.randomBytes)(32).toString('hex');
        await database_1.prisma.representativeResponse.update({
            where: { id },
            data: {
                verificationToken,
                verificationSentAt: new Date(),
            },
        });
        const baseUrl = env_1.env.API_URL;
        await email_service_1.emailService.sendResponseVerification({
            recipientEmail: response.representativeEmail,
            campaignTitle: response.campaign.title,
            responseType: response.responseType,
            responseText: response.responseText,
            submitterName: response.isAnonymous ? 'Anonymous' : (response.submittedByName || 'Anonymous'),
            verificationUrl: `${baseUrl}/api/responses/${id}/verify/${verificationToken}`,
            reportUrl: `${baseUrl}/api/responses/${id}/report/${verificationToken}`,
        });
        return { success: true };
    },
};
//# sourceMappingURL=responses.service.js.map