import { NextResponse } from 'next/server' import { wallets } from '@/lib/mock-data' import { calculateChurnScore, detectPreChurn } from '@/lib/agent-engine' import { sendCustomEvent, isTorqueConfigured, sendTelegramAlert } from '@/lib/torque-mcp' import { pushEvent } from '@/lib/event-store' import { recordIntervention, recordRecovery } from '@/lib/attribution-store' function walletToSignals(w: typeof wallets[0]) { const daysMatch = w.lastActive.match(/(\d+)d/) const daysInactive = daysMatch ? parseInt(daysMatch[1]) : 0 return { daysInactive, volumeDropPct: w.streak === 0 ? 80 : Math.max(0, (10 - Math.min(w.streak, 10)) * 8), uniqueProtocols: w.protocols.length, currentStreak: w.streak, hasLiquidation: false, } } export async function POST() { const configured = isTorqueConfigured() const detections: Array<{ wallet: string; risk: string; score: number; eventName: string; eventSent: boolean; eventId?: string; error?: string; preChurn?: boolean; walletType?: string }> = [] for (const wallet of wallets) { const signals = walletToSignals(wallet) const { score, risk } = calculateChurnScore(signals) // Pre-churn early warning: fire inactivity_detected before full churn threshold const { isPreChurn, walletType } = detectPreChurn( signals.daysInactive, signals.currentStreak, wallet.protocols ) if (risk === 'critical' || risk === 'high' || risk === 'medium') { const eventName = risk === 'critical' || risk === 'high' ? 'churn_risk_high' : 'churn_risk_medium' const result = configured ? await sendCustomEvent(wallet.address, eventName, { risk, score, daysInactive: signals.daysInactive, protocols: wallet.protocols, walletType, detectedBy: 'flowstate-ai-agent', }) : { success: false, error: 'TORQUE_INGEST_KEY not configured' } if (result.success && result.eventId) { pushEvent({ ingestionId: result.eventId, wallet: wallet.address, eventName, risk, score, firedAt: new Date().toISOString(), source: 'scan', }) recordIntervention(wallet.address, eventName, score) } detections.push({ wallet: wallet.address, risk, score, eventName, eventSent: result.success, eventId: result.eventId, error: result.error, walletType, }) } else if (isPreChurn) { // Early warning: soft nudge before reaching churn threshold const result = configured ? await sendCustomEvent(wallet.address, 'inactivity_detected', { risk: 'pre_churn', score, daysInactive: signals.daysInactive, walletType, preChurnWarning: true, detectedBy: 'flowstate-ai-agent', }) : { success: false, error: 'TORQUE_INGEST_KEY not configured' } if (result.success && result.eventId) { pushEvent({ ingestionId: result.eventId, wallet: wallet.address, eventName: 'inactivity_detected', risk: 'low', score, firedAt: new Date().toISOString(), source: 'scan', }) } detections.push({ wallet: wallet.address, risk: 'pre_churn', score, eventName: 'inactivity_detected', eventSent: result.success, eventId: result.eventId, error: result.error, preChurn: true, walletType, }) } } // Telegram alert when ≥5 critical wallets detected in one scan const criticalCount = detections.filter(d => d.risk === 'critical').length if (criticalCount >= 5) { const fired = detections.filter(d => d.eventSent).length await sendTelegramAlert( `🚨 ${criticalCount} critical wallets detected in scan\n` + `📡 ${fired}/${detections.length} Torque events confirmed\n` + `⏱ ${new Date().toLocaleTimeString('en-US', { hour12: false })} UTC` ) } return NextResponse.json({ detections, count: detections.length, preChurnCount: detections.filter(d => d.preChurn).length, configured, timestamp: new Date().toISOString(), }) } export async function GET() { return NextResponse.json({ status: 'active', configured: isTorqueConfigured(), capabilities: [ 'churn_detection', 'pre_churn_early_warning', 'wallet_type_classification', 'auto_campaign_creation', 'comeback_detection', 'streak_tracking', 'recovery_attribution', 'telegram_alerts', ], }) }