import { transaction } from '../db.mjs'; export function createCampaign(db, { nome, produto, segmentos, variantes, limiteDiarioInicial = 10, limiteDiarioTeto = 80 }) { return transaction(db, () => { const result = db.prepare( `INSERT INTO outreach_campaigns (nome, produto, segmentos_json, limite_diario_atual, limite_diario_teto) VALUES (?, ?, ?, ?, ?)` ).run(nome, produto, JSON.stringify(segmentos || []), limiteDiarioInicial, limiteDiarioTeto); const campaignId = Number(result.lastInsertRowid); const insertVariant = db.prepare( `INSERT INTO outreach_campaign_variants (campaign_id, code, mensagem) VALUES (?, ?, ?)` ); for (const variante of variantes) { insertVariant.run(campaignId, variante.code, variante.mensagem); } return getCampaign(db, campaignId); }); } export function getCampaign(db, id) { const row = db.prepare('SELECT * FROM outreach_campaigns WHERE id = ?').get(id); if (!row) return null; return { ...row, segmentos: JSON.parse(row.segmentos_json || '[]'), variants: listVariants(db, id) }; } export function listCampaigns(db) { return db.prepare('SELECT * FROM outreach_campaigns ORDER BY id DESC').all() .map((row) => ({ ...row, segmentos: JSON.parse(row.segmentos_json || '[]') })); } export function listVariants(db, campaignId) { return db.prepare( 'SELECT * FROM outreach_campaign_variants WHERE campaign_id = ? AND ativo = 1 ORDER BY code' ).all(campaignId); } // status: 'rascunho' -> 'rodando' -> 'pausado' <-> 'rodando' -> 'concluido'. // pause_reason so fica setado quando o novo status e 'pausado'. export function setCampaignStatus(db, campaignId, status, pauseReason = null) { if (status === 'rodando') { // When transitioning to 'rodando', stamp semana_iniciada_em only on first run (if currently NULL) db.prepare( `UPDATE outreach_campaigns SET status = ?, pause_reason = ?, semana_iniciada_em = COALESCE(semana_iniciada_em, datetime('now')), updated_at = datetime('now') WHERE id = ?` ).run(status, status === 'pausado' ? pauseReason : null, campaignId); } else { db.prepare( `UPDATE outreach_campaigns SET status = ?, pause_reason = ?, updated_at = datetime('now') WHERE id = ?` ).run(status, status === 'pausado' ? pauseReason : null, campaignId); } return getCampaign(db, campaignId); } // So avanca campanha que estiver de fato pausada — retomar uma campanha em // rascunho, por exemplo, deveria usar setCampaignStatus(db, id, 'rodando'). export function resumeCampaign(db, campaignId) { // Reseta semana_iniciada_em pra agora: uma campanha que ficou pausada (por // saude ruim ou motivo manual) por varios dias nao pode, ao ser retomada, // já satisfazer o "rodando ha 7+ dias" do escalateWeeklyLimits e dobrar o // limite diario logo de cara — isso inverteria a logica de seguranca. db.prepare( `UPDATE outreach_campaigns SET status = 'rodando', pause_reason = NULL, semana_iniciada_em = datetime('now'), updated_at = datetime('now') WHERE id = ? AND status = 'pausado'` ).run(campaignId); return getCampaign(db, campaignId); } // Escalonamento semanal: dobra o limite diario a cada 7 dias corridos rodando // sem pausa desde semana_iniciada_em, ate o teto da campanha. So campanhas com // status='rodando' avancam — uma pausada fica congelada ate o operador retomar. export function escalateWeeklyLimits(db, now = new Date()) { const campaigns = db.prepare( `SELECT id, limite_diario_atual, limite_diario_teto, semana_iniciada_em FROM outreach_campaigns WHERE status = 'rodando'` ).all(); let escaladas = 0; for (const campaign of campaigns) { if (!campaign.semana_iniciada_em) continue; const dias = (now.getTime() - new Date(campaign.semana_iniciada_em.replace(' ', 'T') + 'Z').getTime()) / 86400000; if (dias < 7) continue; const novoLimite = Math.min(campaign.limite_diario_teto, campaign.limite_diario_atual * 2); if (novoLimite === campaign.limite_diario_atual) continue; db.prepare( `UPDATE outreach_campaigns SET limite_diario_atual = ?, semana_iniciada_em = datetime('now'), updated_at = datetime('now') WHERE id = ?` ).run(novoLimite, campaign.id); escaladas += 1; } return escaladas; } export function isOptedOut(db, numero) { return Boolean(db.prepare('SELECT 1 FROM outreach_opt_out WHERE numero = ?').get(numero)); } export function addOptOut(db, numero, motivo = null) { db.prepare( `INSERT INTO outreach_opt_out (numero, motivo) VALUES (?, ?) ON CONFLICT(numero) DO UPDATE SET motivo = excluded.motivo` ).run(numero, motivo); } export function removeOptOut(db, numero) { db.prepare('DELETE FROM outreach_opt_out WHERE numero = ?').run(numero); }