import { injectUnsubscribeFooter, generateUnsubscribeUrl, getUnsubscribeHeaders } from './email-utils.js';
export class EmailQueueWorker {
    db;
    ses;
    rateLimit;
    jwtSecret;
    serverUrl;
    broadcast;
    interval = null;
    running = false;
    tickCount = 0;
    constructor(config) {
        this.db = config.db;
        this.ses = config.ses;
        this.rateLimit = config.rateLimit;
        this.jwtSecret = config.jwtSecret;
        this.serverUrl = config.serverUrl;
        this.broadcast = config.broadcast;
    }
    start() {
        if (this.interval)
            return;
        this.recover();
        this.interval = setInterval(() => { this.tick().catch(() => { }); }, 1000);
    }
    stop() {
        if (this.interval) {
            clearInterval(this.interval);
            this.interval = null;
        }
    }
    recover() {
        this.db.prepare("UPDATE email_queue SET status = 'pending' WHERE status = 'sending' AND attempts < 3").run();
        this.db.prepare("UPDATE email_queue SET status = 'failed' WHERE status = 'sending' AND attempts >= 3").run();
    }
    async tick() {
        if (this.running)
            return;
        this.running = true;
        try {
            const items = this.db.prepare(`
        SELECT eq.* FROM email_queue eq
        JOIN email_campaigns ec ON eq.campaign_id = ec.id
        WHERE eq.status = 'pending' AND ec.status = 'sending'
        ORDER BY eq.scheduled_at ASC, eq.created_at ASC
        LIMIT ?
      `).all(this.rateLimit);
            if (items.length === 0) {
                this.running = false;
                return;
            }
            const campaignCache = new Map();
            const getCampaign = (id) => {
                if (!campaignCache.has(id)) {
                    campaignCache.set(id, this.db.prepare('SELECT id, subject, body_html, body_text, from_email, from_name, status FROM email_campaigns WHERE id = ?').get(id));
                }
                return campaignCache.get(id);
            };
            for (const item of items) {
                const campaign = getCampaign(item.campaign_id);
                if (campaign.status !== 'sending')
                    continue;
                this.db.prepare("UPDATE email_queue SET status = 'sending' WHERE id = ?").run(item.id);
                try {
                    const unsubUrl = generateUnsubscribeUrl(item.to_email, this.jwtSecret, this.serverUrl);
                    const html = injectUnsubscribeFooter(campaign.body_html, unsubUrl);
                    const headers = getUnsubscribeHeaders(unsubUrl);
                    const result = await this.ses.sendEmail({
                        to: item.to_email,
                        from: campaign.from_email,
                        fromName: campaign.from_name ?? '',
                        subject: campaign.subject,
                        htmlBody: html,
                        textBody: campaign.body_text ?? '',
                        headers,
                    });
                    this.db.transaction(() => {
                        this.db.prepare("UPDATE email_queue SET status = 'sent', ses_message_id = ?, sent_at = ?, attempts = attempts + 1 WHERE id = ?")
                            .run(result.messageId, new Date().toISOString(), item.id);
                        this.db.prepare('UPDATE email_campaigns SET sent_count = sent_count + 1 WHERE id = ?').run(item.campaign_id);
                    })();
                }
                catch (err) {
                    const newAttempts = item.attempts + 1;
                    const newStatus = newAttempts >= 3 ? 'failed' : 'pending';
                    this.db.transaction(() => {
                        this.db.prepare('UPDATE email_queue SET status = ?, attempts = ?, error = ? WHERE id = ?')
                            .run(newStatus, newAttempts, err.message, item.id);
                        if (newStatus === 'failed') {
                            this.db.prepare('UPDATE email_campaigns SET failed_count = failed_count + 1 WHERE id = ?').run(item.campaign_id);
                        }
                    })();
                }
            }
            // Check campaign completion
            const campaignIds = [...new Set(items.map(i => i.campaign_id))];
            for (const cid of campaignIds) {
                const pending = this.db.prepare("SELECT COUNT(*) as count FROM email_queue WHERE campaign_id = ? AND status IN ('pending', 'sending')").get(cid);
                if (pending.count === 0) {
                    this.db.prepare("UPDATE email_campaigns SET status = 'sent', completed_at = ? WHERE id = ? AND status = 'sending'")
                        .run(new Date().toISOString(), cid);
                }
            }
            // Broadcast progress every 5 ticks
            this.tickCount++;
            if (this.broadcast && this.tickCount % 5 === 0) {
                for (const cid of campaignIds) {
                    const stats = this.db.prepare('SELECT sent_count, failed_count, total_recipients, status FROM email_campaigns WHERE id = ?').get(cid);
                    if (stats)
                        this.broadcast({ type: 'email:progress', campaignId: cid, sent: stats.sent_count, failed: stats.failed_count, total: stats.total_recipients });
                }
            }
        }
        finally {
            this.running = false;
        }
    }
}
//# sourceMappingURL=email-queue-worker.js.map