/**
 * Alert Engine — Monitors key metrics and triggers alerts
 * Uses node-cron for scheduling, SQLite for storage, email for notifications
 */
const cron = require('node-cron');
const db = require('../db-local');
const pool = require('../db-rds');
const { sendEmail } = require('./channels');

// Insert alert into SQLite
function createAlert(name, severity, message, data = null) {
  const stmt = db.prepare(`
    INSERT INTO alerts (name, severity, message, data)
    VALUES (?, ?, ?, ?)
  `);
  const result = stmt.run(name, severity, message, data ? JSON.stringify(data) : null);
  console.log(`[Alert] ${severity} - ${name}: ${message}`);
  return result.lastInsertRowid;
}

// Get recent alerts
function getAlerts(hours = 24, unackedOnly = false) {
  let sql = `SELECT * FROM alerts WHERE created_at >= datetime('now', '-${Math.round(hours)} hours')`;
  if (unackedOnly) sql += ' AND acknowledged = 0';
  sql += ' ORDER BY created_at DESC';
  return db.prepare(sql).all();
}

// Acknowledge alert
function acknowledgeAlert(id) {
  return db.prepare('UPDATE alerts SET acknowledged = 1 WHERE id = ?').run(id);
}

// Get alert history
function getAlertHistory(days = 7) {
  return db.prepare(`
    SELECT * FROM alerts
    WHERE created_at >= datetime('now', '-${Math.round(days)} days')
    ORDER BY created_at DESC
    LIMIT 200
  `).all();
}

// Count unacknowledged alerts
function getUnackedCount() {
  const row = db.prepare("SELECT COUNT(*) as count FROM alerts WHERE acknowledged = 0 AND created_at >= datetime('now', '-24 hours')").get();
  return row?.count || 0;
}

// --- Alert Rules ---

const alertRules = [
  {
    name: 'volume_spike',
    schedule: '*/15 * * * *', // Every 15 minutes
    severity: 'P1',
    async check() {
      const conn = await pool.getConnection();
      try {
        // Compare today's volume to 7-day average
        const [today] = await conn.execute(`
          SELECT COUNT(*) as qtd, ROUND(COALESCE(SUM(amount_usd), 0), 2) as vol
          FROM br_transaction_to_usa WHERE DATE(created_at) = CURDATE()
        `);
        const [avg7] = await conn.execute(`
          SELECT COUNT(*) / 7.0 as avg_qtd, ROUND(COALESCE(SUM(amount_usd), 0) / 7.0, 2) as avg_vol
          FROM br_transaction_to_usa
          WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND DATE(created_at) < CURDATE()
        `);
        const todayVol = Number(today[0]?.vol) || 0;
        const avgVol = Number(avg7[0]?.avg_vol) || 0;
        if (avgVol > 0 && todayVol > avgVol * 2) {
          return {
            triggered: true,
            message: `Volume spike: $${Math.round(todayVol).toLocaleString()} today vs $${Math.round(avgVol).toLocaleString()} avg (${Math.round(todayVol / avgVol * 100)}% of avg)`,
            data: { todayVol, avgVol, ratio: Math.round(todayVol / avgVol * 100) }
          };
        }
        return { triggered: false };
      } finally {
        conn.release();
      }
    }
  },
  {
    name: 'spread_anomaly',
    schedule: '0 */2 * * *', // Every 2 hours
    severity: 'P2',
    async check() {
      const conn = await pool.getConnection();
      try {
        const [today] = await conn.execute(`
          SELECT ROUND(AVG((exchange_rate - ptax) / exchange_rate * 100), 2) as avg_spread
          FROM br_transaction_to_usa WHERE DATE(created_at) = CURDATE() AND ptax > 0
        `);
        const [avg30] = await conn.execute(`
          SELECT ROUND(AVG((exchange_rate - ptax) / exchange_rate * 100), 2) as avg_spread
          FROM br_transaction_to_usa
          WHERE created_at >= DATE_SUB(CURDATE(), INTERVAL 30 DAY) AND DATE(created_at) < CURDATE() AND ptax > 0
        `);
        const todaySpread = Number(today[0]?.avg_spread) || 0;
        const avgSpread = Number(avg30[0]?.avg_spread) || 0;
        if (avgSpread > 0 && Math.abs(todaySpread - avgSpread) > avgSpread * 0.5) {
          return {
            triggered: true,
            message: `Spread anomaly: ${todaySpread}% today vs ${avgSpread}% 30d avg (${Math.round((todaySpread - avgSpread) / avgSpread * 100)}% deviation)`,
            data: { todaySpread, avgSpread }
          };
        }
        return { triggered: false };
      } finally {
        conn.release();
      }
    }
  },
  {
    name: 'large_transaction',
    schedule: '*/10 * * * *', // Every 10 minutes
    severity: 'P0',
    async check() {
      const conn = await pool.getConnection();
      try {
        // Check for transactions > $50K in the last 10 minutes
        const [rows] = await conn.execute(`
          SELECT t.id, c.nome, t.amount_usd, t.created_at
          FROM br_transaction_to_usa t
          INNER JOIN conta c ON c.id_conta = t.id_conta
          WHERE t.created_at >= DATE_SUB(NOW(), INTERVAL 10 MINUTE)
            AND t.amount_usd >= 50000
          ORDER BY t.amount_usd DESC
        `);
        if (rows.length > 0) {
          const txList = rows.map(r => `${r.nome}: $${Number(r.amount_usd).toLocaleString()}`).join(', ');
          return {
            triggered: true,
            message: `Large transaction(s) detected: ${txList}`,
            data: { count: rows.length, transactions: rows.map(r => ({ id: r.id, nome: r.nome, usd: Number(r.amount_usd) })) }
          };
        }
        return { triggered: false };
      } finally {
        conn.release();
      }
    }
  },
  {
    name: 'failed_tx_spike',
    schedule: '*/30 * * * *', // Every 30 minutes
    severity: 'P1',
    async check() {
      const conn = await pool.getConnection();
      try {
        const [stats] = await conn.execute(`
          SELECT
            COUNT(*) as total,
            SUM(CASE WHEN status NOT IN ('boleto_pago','finalizado')
              AND (date_sent_usa IS NULL OR date_sent_usa = '0000-00-00 00:00:00') THEN 1 ELSE 0 END) as failed
          FROM br_transaction_to_usa
          WHERE DATE(created_at) = CURDATE()
        `);
        const total = Number(stats[0]?.total) || 0;
        const failed = Number(stats[0]?.failed) || 0;
        const rate = total > 0 ? (failed / total * 100) : 0;
        if (total >= 10 && rate > 5) {
          return {
            triggered: true,
            message: `Failed tx spike: ${failed}/${total} (${rate.toFixed(1)}%) today`,
            data: { total, failed, rate: Math.round(rate * 10) / 10 }
          };
        }
        return { triggered: false };
      } finally {
        conn.release();
      }
    }
  },
  {
    name: 'provider_inactivity',
    schedule: '0 9,14 * * 1-5', // 9am and 2pm weekdays
    severity: 'P2',
    async check() {
      const conn = await pool.getConnection();
      try {
        // Providers with no activity in last 24h that had activity in previous 7 days
        const [inactive] = await conn.execute(`
          SELECT pm.provider, MAX(t.created_at) as last_tx,
            TIMESTAMPDIFF(HOUR, MAX(t.created_at), NOW()) as hours_inactive
          FROM br_transaction_to_usa t
          INNER JOIN br_payment_methods pm ON t.payment_method_id = pm.id
          WHERE t.created_at >= DATE_SUB(CURDATE(), INTERVAL 8 DAY)
            AND pm.provider IN ('dlocal','bexs','braza','bs2','ouribank','msb')
          GROUP BY pm.provider
          HAVING MAX(t.created_at) < DATE_SUB(NOW(), INTERVAL 24 HOUR)
        `);
        if (inactive.length > 0) {
          const list = inactive.map(r => `${r.provider} (${r.hours_inactive}h)`).join(', ');
          return {
            triggered: true,
            message: `Provider inactivity: ${list}`,
            data: { providers: inactive.map(r => ({ provider: r.provider, hours: Number(r.hours_inactive) })) }
          };
        }
        return { triggered: false };
      } finally {
        conn.release();
      }
    }
  }
];

// Dedup: don't fire same alert within cooldown period
const COOLDOWN_MS = 60 * 60 * 1000; // 1 hour
const _lastFired = {};

async function runRule(rule) {
  try {
    const result = await rule.check();
    if (!result.triggered) return;

    // Cooldown check
    const lastTime = _lastFired[rule.name] || 0;
    if (Date.now() - lastTime < COOLDOWN_MS) {
      console.log(`[Alert] ${rule.name} in cooldown, skipping`);
      return;
    }
    _lastFired[rule.name] = Date.now();

    // Store in SQLite
    createAlert(rule.name, rule.severity, result.message, result.data);

    // Send email notification
    const emailTo = process.env.ALERT_EMAIL_TO;
    if (emailTo) {
      const severityEmoji = { P0: 'CRITICAL', P1: 'WARNING', P2: 'INFO' };
      await sendEmail(
        emailTo,
        `[${rule.severity}] ${severityEmoji[rule.severity] || ''} ${rule.name}`,
        `
          <h3 style="color: ${rule.severity === 'P0' ? '#D93025' : rule.severity === 'P1' ? '#E8710A' : '#1A73E8'};">
            ${rule.severity} - ${rule.name}
          </h3>
          <p style="font-size: 15px; margin: 12px 0;">${result.message}</p>
          <p style="font-size: 12px; color: #666;">
            ${new Date().toISOString()} | <a href="${process.env.BASE_URL || 'http://localhost:3080'}/admin/bi">Open BI Dashboard</a>
          </p>
        `
      );
    }
  } catch (err) {
    console.error(`[Alert] Rule ${rule.name} error:`, err.message);
  }
}

/**
 * Start alert engine — schedules all cron jobs
 */
function startAlertEngine() {
  console.log('[Alert Engine] Starting with', alertRules.length, 'rules');

  alertRules.forEach(rule => {
    cron.schedule(rule.schedule, () => runRule(rule));
    console.log(`[Alert Engine] Scheduled: ${rule.name} (${rule.schedule}) [${rule.severity}]`);
  });

  // Run all rules once on startup (after 30s delay to let DB warm up)
  setTimeout(() => {
    console.log('[Alert Engine] Running initial check...');
    alertRules.forEach(rule => runRule(rule));
  }, 30000);
}

module.exports = {
  startAlertEngine,
  createAlert,
  getAlerts,
  acknowledgeAlert,
  getAlertHistory,
  getUnackedCount
};
