"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 NotificationsService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.NotificationsService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const notification_entity_1 = require("../database/entities/notification.entity");
const notification_template_entity_1 = require("../database/entities/notification-template.entity");
const business_settings_entity_1 = require("../database/entities/business-settings.entity");
const system_settings_entity_1 = require("../database/entities/system-settings.entity");
const config_service_1 = require("../common/config/config.service");
let NotificationsService = NotificationsService_1 = class NotificationsService {
    constructor(notificationRepo, templateRepo, businessSettingsRepo, systemSettingsRepo, configService) {
        this.notificationRepo = notificationRepo;
        this.templateRepo = templateRepo;
        this.businessSettingsRepo = businessSettingsRepo;
        this.systemSettingsRepo = systemSettingsRepo;
        this.configService = configService;
        this.logger = new common_1.Logger(NotificationsService_1.name);
        const nodeEnv = this.configService.get('NODE_ENV', 'development');
        this.isDemo = nodeEnv === 'demo';
        this.isDev = nodeEnv === 'development';
    }
    shouldMock() {
        return this.isDemo || this.isDev;
    }
    async sendSms(phoneNumber, message, businessId) {
        if (this.shouldMock()) {
            this.logger.log(`[MOCK SMS] To: ${phoneNumber} | Message: ${message} | Business: ${businessId || 'N/A'}`);
            return this.createNotificationRecord(notification_entity_1.NotificationType.SMS, phoneNumber, message, undefined, businessId, notification_entity_1.NotificationStatus.SENT);
        }
        try {
            const provider = await this.getSmsProvider(businessId);
            if (provider === 'twilio') {
                await this.sendViaTwilio(phoneNumber, message, businessId);
            }
            else {
                await this.sendViaAfricasTalking(phoneNumber, message, businessId);
            }
            this.logger.log(`SMS sent to ${phoneNumber} via ${provider}`);
            return this.createNotificationRecord(notification_entity_1.NotificationType.SMS, phoneNumber, message, undefined, businessId, notification_entity_1.NotificationStatus.SENT);
        }
        catch (error) {
            this.logger.error(`SMS failed to ${phoneNumber}: ${error.message}`, error.stack);
            return this.createNotificationRecord(notification_entity_1.NotificationType.SMS, phoneNumber, message, error.message, businessId, notification_entity_1.NotificationStatus.FAILED);
        }
    }
    async sendEmail(to, subject, body, businessId) {
        if (this.shouldMock()) {
            this.logger.log(`[MOCK EMAIL] To: ${to} | Subject: ${subject} | Business: ${businessId || 'N/A'}`);
            return this.createNotificationRecord(notification_entity_1.NotificationType.EMAIL, to, body, undefined, businessId, notification_entity_1.NotificationStatus.SENT, subject);
        }
        try {
            await this.sendViaSmtp(to, subject, body, businessId);
            this.logger.log(`Email sent to ${to}`);
            return this.createNotificationRecord(notification_entity_1.NotificationType.EMAIL, to, body, undefined, businessId, notification_entity_1.NotificationStatus.SENT, subject);
        }
        catch (error) {
            this.logger.error(`Email failed to ${to}: ${error.message}`, error.stack);
            return this.createNotificationRecord(notification_entity_1.NotificationType.EMAIL, to, body, error.message, businessId, notification_entity_1.NotificationStatus.FAILED, subject);
        }
    }
    async sendWhatsApp(phoneNumber, message, businessId) {
        if (this.shouldMock()) {
            const waLink = `https://wa.me/${phoneNumber}?text=${encodeURIComponent(message)}`;
            this.logger.log(`[MOCK WHATSAPP] To: ${phoneNumber} | Link: ${waLink} | Business: ${businessId || 'N/A'}`);
            return this.createNotificationRecord(notification_entity_1.NotificationType.WHATSAPP, phoneNumber, message, undefined, businessId, notification_entity_1.NotificationStatus.SENT);
        }
        try {
            const useBusinessApi = await this.getSettingValue(businessId, 'whatsapp_business_api_enabled', 'false') === 'true';
            if (useBusinessApi) {
                await this.sendViaWhatsAppBusinessApi(phoneNumber, message, businessId);
            }
            else {
                const waLink = `https://wa.me/${phoneNumber}?text=${encodeURIComponent(message)}`;
                this.logger.log(`WhatsApp link generated: ${waLink}`);
            }
            return this.createNotificationRecord(notification_entity_1.NotificationType.WHATSAPP, phoneNumber, message, undefined, businessId, notification_entity_1.NotificationStatus.SENT);
        }
        catch (error) {
            this.logger.error(`WhatsApp failed to ${phoneNumber}: ${error.message}`, error.stack);
            return this.createNotificationRecord(notification_entity_1.NotificationType.WHATSAPP, phoneNumber, message, error.message, businessId, notification_entity_1.NotificationStatus.FAILED);
        }
    }
    async sendInApp(userId, title, body, businessId) {
        this.logger.log(`[IN_APP] User: ${userId} | Title: ${title} | Business: ${businessId || 'N/A'}`);
        return this.createNotificationRecord(notification_entity_1.NotificationType.IN_APP, userId, body, undefined, businessId, notification_entity_1.NotificationStatus.SENT, title, userId);
    }
    async sendPaymentSuccess(phoneNumber, amount, packageName, receiptNumber, businessId) {
        const template = await this.compileTemplate('payment_success', businessId, {
            amount: amount.toString(),
            packageName,
            receiptNumber,
        });
        const message = template || `Payment of KES ${amount} for ${packageName} successful. Receipt: ${receiptNumber}`;
        await this.sendSms(phoneNumber, message, businessId);
        const customerEmail = await this.getCustomerEmail(phoneNumber, businessId);
        if (customerEmail) {
            const emailSubject = `Payment Confirmed - ${receiptNumber}`;
            const emailBody = template || `Your payment of KES ${amount} for ${packageName} has been received. Receipt: ${receiptNumber}`;
            await this.sendEmail(customerEmail, emailSubject, emailBody, businessId);
        }
    }
    async sendPaymentFailed(phoneNumber, reason, businessId) {
        const template = await this.compileTemplate('payment_failed', businessId, {
            reason,
        });
        const message = template || `Your payment was not successful. Reason: ${reason}. Please try again.`;
        await this.sendSms(phoneNumber, message, businessId);
    }
    async sendPackageActivated(phoneNumber, packageName, expiryTime, businessId) {
        const template = await this.compileTemplate('package_activated', businessId, {
            packageName,
            expiryTime: expiryTime.toISOString(),
        });
        const message = template || `Your ${packageName} package has been activated. Expires: ${expiryTime.toLocaleString()}`;
        await this.sendSms(phoneNumber, message, businessId);
    }
    async sendPackageExpiryWarning(phoneNumber, packageName, expiryTime, businessId) {
        const template = await this.compileTemplate('package_expiry_warning', businessId, {
            packageName,
            expiryTime: expiryTime.toISOString(),
        });
        const message = template || `Your ${packageName} package expires at ${expiryTime.toLocaleString()}. Renew now to stay connected.`;
        await this.sendSms(phoneNumber, message, businessId);
    }
    async sendPackageExpired(phoneNumber, packageName, businessId) {
        const template = await this.compileTemplate('package_expired', businessId, {
            packageName,
        });
        const message = template || `Your ${packageName} package has expired. Please purchase a new package to continue using the service.`;
        await this.sendSms(phoneNumber, message, businessId);
    }
    async sendDataAlmostFinished(phoneNumber, remainingMb, businessId) {
        const template = await this.compileTemplate('data_almost_finished', businessId, {
            remainingMb: remainingMb.toString(),
        });
        const message = template || `You have only ${remainingMb}MB remaining. Top up now to avoid interruption.`;
        await this.sendSms(phoneNumber, message, businessId);
    }
    async sendRouterOffline(businessId, routerName) {
        const ownerPhone = await this.getBusinessOwnerPhone(businessId);
        if (!ownerPhone)
            return;
        const template = await this.compileTemplate('router_offline', businessId, {
            routerName,
        });
        const message = template || `ALERT: Router "${routerName}" is offline. Please check the device immediately.`;
        await this.sendSms(ownerPhone, message, businessId);
    }
    async sendSubscriptionExpiring(businessId, daysRemaining) {
        const ownerPhone = await this.getBusinessOwnerPhone(businessId);
        if (!ownerPhone)
            return;
        const template = await this.compileTemplate('subscription_expiring', businessId, {
            daysRemaining: daysRemaining.toString(),
        });
        const message = template || `Your subscription expires in ${daysRemaining} days. Renew now to avoid service interruption.`;
        await this.sendSms(ownerPhone, message, businessId);
        const ownerEmail = await this.getBusinessOwnerEmail(businessId);
        if (ownerEmail) {
            await this.sendEmail(ownerEmail, 'Subscription Expiring Soon', message, businessId);
        }
    }
    async getTemplates(businessId) {
        return this.templateRepo.find({
            where: { businessId },
            order: { createdAt: 'DESC' },
        });
    }
    async updateTemplate(id, dto, businessId) {
        const template = await this.templateRepo.findOne({ where: { id, businessId } });
        if (!template) {
            throw new common_1.NotFoundException(`Template with ID ${id} not found`);
        }
        if (dto.key !== undefined)
            template.key = dto.key;
        if (dto.name !== undefined)
            template.name = dto.name;
        if (dto.type !== undefined)
            template.type = dto.type;
        if (dto.subject !== undefined)
            template.subject = dto.subject;
        if (dto.bodyTemplate !== undefined)
            template.bodyTemplate = dto.bodyTemplate;
        if (dto.variables !== undefined)
            template.variables = dto.variables;
        return this.templateRepo.save(template);
    }
    async createTemplate(dto, businessId) {
        const template = this.templateRepo.create({
            businessId,
            key: dto.key,
            name: dto.name,
            type: dto.type,
            subject: dto.subject,
            bodyTemplate: dto.bodyTemplate,
            variables: dto.variables || [],
        });
        return this.templateRepo.save(template);
    }
    async getNotificationHistory(businessId, filters) {
        const page = filters.page ?? 1;
        const limit = filters.limit ?? 20;
        const where = { businessId };
        if (filters.type) {
            where.type = filters.type;
        }
        if (filters.status) {
            where.status = filters.status;
        }
        if (filters.startDate && filters.endDate) {
            where.createdAt = (0, typeorm_2.Between)(filters.startDate, filters.endDate);
        }
        else if (filters.startDate) {
            where.createdAt = (0, typeorm_2.MoreThanOrEqual)(filters.startDate);
        }
        else if (filters.endDate) {
            where.createdAt = (0, typeorm_2.LessThanOrEqual)(filters.endDate);
        }
        const [data, total] = await this.notificationRepo.findAndCount({
            where,
            order: { createdAt: 'DESC' },
            skip: (page - 1) * limit,
            take: limit,
        });
        return { data, total, page, limit, totalPages: Math.ceil(total / limit) };
    }
    async compileTemplate(templateKey, businessId, variables) {
        const template = await this.templateRepo.findOne({
            where: { key: templateKey, businessId },
        });
        if (!template) {
            return null;
        }
        let body = template.bodyTemplate;
        for (const [key, value] of Object.entries(variables)) {
            body = body.replace(new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, 'g'), value);
        }
        if (template.subject) {
            let subject = template.subject;
            for (const [key, value] of Object.entries(variables)) {
                subject = subject.replace(new RegExp(`\\{\\{\\s*${key}\\s*\\}\\}`, 'g'), value);
            }
            template.subject = subject;
        }
        return body;
    }
    async createNotificationRecord(type, recipient, body, errorMessage, businessId, status = notification_entity_1.NotificationStatus.PENDING, subject, userId) {
        const notification = this.notificationRepo.create({
            businessId: businessId || '00000000-0000-0000-0000-000000000000',
            userId: userId || null,
            type,
            recipient,
            subject: subject || null,
            body,
            status,
            sentAt: status === notification_entity_1.NotificationStatus.SENT ? new Date() : null,
            errorMessage: errorMessage || null,
        });
        return this.notificationRepo.save(notification);
    }
    async getSmsProvider(businessId) {
        if (businessId) {
            const setting = await this.businessSettingsRepo.findOne({
                where: { businessId, key: 'sms_provider' },
            });
            if (setting)
                return setting.value;
        }
        const sysSetting = await this.systemSettingsRepo.findOne({
            where: { key: 'default_sms_provider' },
        });
        return sysSetting?.value || 'africastalking';
    }
    async sendViaTwilio(phoneNumber, message, businessId) {
        const accountSid = await this.getSettingValue(businessId, 'twilio_account_sid', '');
        const authToken = await this.getSettingValue(businessId, 'twilio_auth_token', '');
        const fromNumber = await this.getSettingValue(businessId, 'twilio_from_number', '');
        if (!accountSid || !authToken || !fromNumber) {
            throw new Error('Twilio configuration is incomplete');
        }
        this.logger.log(`[TWILIO] Sending SMS to ${phoneNumber} from ${fromNumber}`);
    }
    async sendViaAfricasTalking(phoneNumber, message, businessId) {
        const username = await this.getSettingValue(businessId, 'africastalking_username', '');
        const apiKey = await this.getSettingValue(businessId, 'africastalking_api_key', '');
        const fromShortCode = await this.getSettingValue(businessId, 'africastalking_shortcode', '');
        if (!username || !apiKey) {
            throw new Error('Africas Talking configuration is incomplete');
        }
        this.logger.log(`[AFRICASTALKING] Sending SMS to ${phoneNumber} from ${fromShortCode || 'default'}`);
    }
    async sendViaSmtp(to, subject, body, businessId) {
        const host = await this.getSettingValue(businessId, 'smtp_host', '');
        const port = await this.getSettingValue(businessId, 'smtp_port', '587');
        const user = await this.getSettingValue(businessId, 'smtp_user', '');
        const pass = await this.getSettingValue(businessId, 'smtp_password', '');
        if (!host || !user || !pass) {
            throw new Error('SMTP configuration is incomplete');
        }
        this.logger.log(`[SMTP] Sending email to ${to} via ${host}:${port}`);
    }
    async sendViaWhatsAppBusinessApi(phoneNumber, message, businessId) {
        const phoneNumberId = await this.getSettingValue(businessId, 'whatsapp_phone_number_id', '');
        const accessToken = await this.getSettingValue(businessId, 'whatsapp_access_token', '');
        if (!phoneNumberId || !accessToken) {
            throw new Error('WhatsApp Business API configuration is incomplete');
        }
        this.logger.log(`[WHATSAPP_API] Sending message to ${phoneNumber}`);
    }
    async getSettingValue(businessId, key, defaultValue) {
        if (businessId) {
            const setting = await this.businessSettingsRepo.findOne({
                where: { businessId, key },
            });
            if (setting)
                return setting.value;
        }
        const sysSetting = await this.systemSettingsRepo.findOne({ where: { key } });
        return sysSetting?.value || defaultValue;
    }
    async getCustomerEmail(phoneNumber, businessId) {
        return null;
    }
    async getBusinessOwnerPhone(businessId) {
        return null;
    }
    async getBusinessOwnerEmail(businessId) {
        return null;
    }
};
exports.NotificationsService = NotificationsService;
exports.NotificationsService = NotificationsService = NotificationsService_1 = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(notification_entity_1.Notification)),
    __param(1, (0, typeorm_1.InjectRepository)(notification_template_entity_1.NotificationTemplate)),
    __param(2, (0, typeorm_1.InjectRepository)(business_settings_entity_1.BusinessSettings)),
    __param(3, (0, typeorm_1.InjectRepository)(system_settings_entity_1.SystemSettings)),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        config_service_1.ConfigService])
], NotificationsService);
