import axios from "axios";
import crypto from "crypto";
import db from "../models/index.js";

const { Merchant } = db;

class WebhookService {
  /**
   * Notify merchant of an event via their configured webhook URL.
   * This handles creating the log and triggering the delivery.
   */
  static async notifyMerchant(merchantId, event, data, customerId = null) {
    let webhookLog;
    try {
      const merchant = await Merchant.findByPk(merchantId);
      if (!merchant) return;

      // Ensure data object contains both transactionId and transaction_id for key compatibility
      const rawData = data || {};
      const txId = rawData.transactionId || rawData.transaction_id || rawData.reference || null;

      const normalizedData = {
        ...rawData,
        transactionId: txId,
        transaction_id: txId,
        reference: rawData.reference || txId,
      };

      if (!merchant.webhookUrl) {
          await db.MerchantWebhookLog.create({
              merchantId,
              customerId,
              event,
              webhookUrl: null,
              payload: { event, merchantId, data: normalizedData, timestamp: new Date().toISOString() },
              status: "skipped",
              response: "No webhook URL configured for merchant"
          });
          return;
      }

      const payload = {
        event,
        merchantId,
        timestamp: new Date().toISOString(),
        data: normalizedData,
      };

      // --- 1. Create the log entry first (Status: pending) ---
      webhookLog = await db.MerchantWebhookLog.create({
          merchantId,
          customerId,
          event,
          webhookUrl: merchant.webhookUrl,
          payload,
          status: "pending",
          attempts: 0
      });

      // --- 2. Trigger Delivery ---
      return await this.executeDelivery(webhookLog);

    } catch (error) {
      console.error(`❌ Webhook Service Error for merchant ${merchantId}:`, error.message);
    }
  }

  /**
   * Performs the actual HTTP call and updates the log.
   * Can be called for initial attempt or by a retry worker.
   */
  static async executeDelivery(webhookLog) {
    const maxLocalRetries = 2; // Initial attempts in-request
    let lastError = null;

    // --- 1. Fetch the Merchant's current Webhook URL and Keys for HMAC Signature ---
    const merchant = await Merchant.findByPk(webhookLog.merchantId, { attributes: ["webhookUrl", "publicKey"] });
    const targetUrl = merchant?.webhookUrl || webhookLog.webhookUrl;

    if (!targetUrl) {
        const errorMsg = "No webhook URL found for merchant during delivery";
        console.error(`❌ ${errorMsg} (Log ID: ${webhookLog.id})`);
        await webhookLog.update({ status: "failed", response: errorMsg });
        return false;
    }

    const payloadString = JSON.stringify(webhookLog.payload || {});
    const signature = crypto.createHmac("sha256", merchant?.publicKey || "airpero_secret").update(payloadString).digest("hex");

    for (let i = 0; i < maxLocalRetries; i++) {
        webhookLog.attempts += 1;
        await webhookLog.save();

        try {
            console.log(`📡 Sending ${webhookLog.event} (Attempt ${webhookLog.attempts}) to ${targetUrl}`);
            
            const response = await axios.post(targetUrl, webhookLog.payload, {
                headers: {
                    "Content-Type": "application/json",
                    "X-Airpero-Event": webhookLog.event,
                    "X-Airpero-Attempt": webhookLog.attempts.toString(),
                    "X-Airpero-Signature": signature,
                },
                timeout: 10000,
            });

            await webhookLog.update({ 
                status: "delivered", 
                webhookUrl: targetUrl, // Update the log with the URL we actually used
                response: `HTTP ${response.status}: ${JSON.stringify(response.data).slice(0, 500)}` 
            });

            console.log(`✅ Webhook delivered to ${targetUrl}`);
            return true;

        } catch (error) {
            lastError = error;
            const errorMsg = error.response ? `HTTP ${error.response.status}: ${JSON.stringify(error.response.data)}` : error.message;
            console.error(`⚠️ Attempt ${webhookLog.attempts} failed for ${webhookLog.event}:`, errorMsg);
            
            await webhookLog.update({ 
                status: "failed", 
                webhookUrl: targetUrl, // Update the log with the URL we tried
                response: errorMsg.slice(0, 500) 
            });

            // If it's not the last attempt, wait a bit
            if (i < maxLocalRetries - 1) {
                const delay = 1000 * (i + 1); // 1s then 2s
                await new Promise(resolve => setTimeout(resolve, delay));
            }
        }
    }

    return false;
  }
}

export default WebhookService;
