'use strict';

const crypto = require('crypto');
const axios  = require('axios');
const { query, queryOne } = require('../config/db');

// ── Deliver webhook to all registered endpoints for a user ────────
async function deliverWebhook(userId, payload) {
  const hooks = await query(
    'SELECT * FROM webhooks WHERE user_id = ? AND status = "active"',
    [userId]
  );

  for (const hook of hooks) {
    let events = [];
    try { events = typeof hook.events === 'string' ? JSON.parse(hook.events) : hook.events; } catch (e) {}

    if (events.length > 0 && !events.includes(payload.event)) continue;

    await dispatchToEndpoint(hook, payload);
  }
}

// ── Dispatch to a single endpoint ────────────────────────────────
async function dispatchToEndpoint(hook, payload, attempt = 1) {
  const body      = JSON.stringify({ ...payload, timestamp: Math.floor(Date.now() / 1000) });
  const sig       = crypto.createHmac('sha256', hook.secret).update(body).digest('hex');
  const startTime = Date.now();

  let logId = null;

  // Create log entry
  const [logResult] = await query(
    `INSERT INTO webhook_logs (webhook_id, invoice_id, event, payload, attempt, status)
     VALUES (?, ?, ?, ?, ?, "pending")`,
    [hook.id, payload.invoice_id || null, payload.event, body, attempt]
  );
  logId = logResult.insertId;

  try {
    const response = await axios.post(hook.url, body, {
      timeout: 10000,
      headers: {
        'Content-Type':          'application/json',
        'X-ChainPay-Signature':  sig,
        'X-ChainPay-Event':      payload.event,
        'X-ChainPay-Timestamp':  String(Math.floor(Date.now() / 1000)),
        'User-Agent':            'ChainPay-Webhook/1.0 (chainpay.store)',
      },
      validateStatus: () => true, // Don't throw on non-2xx
    });

    const duration  = Date.now() - startTime;
    const success   = response.status >= 200 && response.status < 300;
    const status    = success ? 'delivered' : 'failed';
    const resBody   = String(response.data || '').slice(0, 500);

    await query(
      'UPDATE webhook_logs SET status = ?, response_status = ?, response_body = ?, duration_ms = ? WHERE id = ?',
      [status, response.status, resBody, duration, logId]
    );

    // Retry on failure (max 3 attempts, exponential backoff)
    if (!success && attempt < 3) {
      const delay = Math.pow(2, attempt) * 5000; // 10s, 20s
      setTimeout(() => dispatchToEndpoint(hook, payload, attempt + 1), delay);
    }

    return { success, status: response.status };

  } catch (err) {
    const duration = Date.now() - startTime;
    await query(
      'UPDATE webhook_logs SET status = "failed", response_body = ?, duration_ms = ? WHERE id = ?',
      [err.message.slice(0, 500), duration, logId]
    );

    if (attempt < 3) {
      const delay = Math.pow(2, attempt) * 5000;
      setTimeout(() => dispatchToEndpoint(hook, payload, attempt + 1), delay);
    }

    return { success: false, error: err.message };
  }
}

// ── Test webhook (ping) ───────────────────────────────────────────
async function testWebhook(hookId) {
  const hook = await queryOne('SELECT * FROM webhooks WHERE id = ?', [hookId]);
  if (!hook) return { ok: false, error: 'Webhook not found.' };

  const result = await dispatchToEndpoint(hook, {
    event:      'test.ping',
    invoice_id: null,
    message:    'This is a test webhook from chainpay.store',
  });

  return { ok: result.success, ...result };
}

// ── Verify incoming webhook signature ────────────────────────────
function verifySignature(rawBody, signature, secret) {
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

module.exports = { deliverWebhook, dispatchToEndpoint, testWebhook, verifySignature };
