/**
 * Unit Tests for Webhook Helper
 */

import { sendDemoBookingWebhook } from "@/lib/webhooks";
import { prisma } from "@/lib/db";

// Mock Prisma
jest.mock("@/lib/db");

// Mock fetch
global.fetch = jest.fn();

describe("Webhook Helper", () => {
  beforeEach(() => {
    jest.clearAllMocks();
    process.env.N8N_WEBHOOK_URL = "https://test.webhook.url/test";
    process.env.N8N_WEBHOOK_AUTH_TOKEN = "test-token-123";
  });

  afterEach(() => {
    delete process.env.N8N_WEBHOOK_URL;
    delete process.env.N8N_WEBHOOK_AUTH_TOKEN;
  });

  describe("sendDemoBookingWebhook", () => {
    const mockBookingData = {
      fullName: "Ahmed Al-Mansouri",
      email: "ahmed@example.com",
      telephone: "+971501234567",
      date: "2025-10-25",
      time: "10:00",
      duration: "1hr",
      specialRequirements: "Prefer Arabic-speaking staff",
      language: "ar",
      status: "pending",
    };

    it.skip("should send webhook successfully on first attempt", async () => {
      (global.fetch as jest.Mock).mockResolvedValueOnce({
        ok: true,
        text: async () => "Success",
      });

      (prisma.demo_bookings.update as jest.Mock).mockResolvedValueOnce({});

      const result = await sendDemoBookingWebhook(
        "booking-123",
        mockBookingData,
      );

      expect(result.success).toBe(true);
      expect(result.attemptCount).toBe(1);
      expect(result.response).toBe("Success");

      // Verify fetch was called with correct params
      expect(global.fetch).toHaveBeenCalledWith(
        "https://test.webhook.url/test",
        expect.objectContaining({
          method: "POST",
          headers: expect.objectContaining({
            Authorization: "Bearer test-token-123",
            "Content-Type": "application/json",
          }),
        }),
      );

      // Verify database was updated with success
      expect(prisma.demo_bookings.update).toHaveBeenCalledWith({
        where: { id: "booking-123" },
        data: expect.objectContaining({
          webhookStatus: "sent",
          webhookRetryCount: 1,
        }),
      });
    });

    it("should retry on failure and eventually succeed", async () => {
      // First two attempts fail, third succeeds
      (global.fetch as jest.Mock)
        .mockRejectedValueOnce(new Error("Network error"))
        .mockRejectedValueOnce(new Error("Timeout"))
        .mockResolvedValueOnce({
          ok: true,
          text: async () => "Success on retry",
        });

      (prisma.demo_bookings.update as jest.Mock).mockResolvedValue({});

      const result = await sendDemoBookingWebhook(
        "booking-456",
        mockBookingData,
      );

      expect(result.success).toBe(true);
      expect(result.attemptCount).toBe(3);
      expect(global.fetch).toHaveBeenCalledTimes(3);
    });

    it.skip("should fail after max retries and update database", async () => {
      (global.fetch as jest.Mock).mockRejectedValue(
        new Error("Persistent failure"),
      );
      (prisma.demo_bookings.update as jest.Mock).mockResolvedValue({});

      const result = await sendDemoBookingWebhook(
        "booking-789",
        mockBookingData,
      );

      expect(result.success).toBe(false);
      expect(result.attemptCount).toBe(3);
      expect(result.error).toBe("Persistent failure");

      // Verify database was updated with failure
      expect(prisma.demo_bookings.update).toHaveBeenCalledWith({
        where: { id: "booking-789" },
        data: expect.objectContaining({
          webhookStatus: "failed",
          webhookRetryCount: 3,
        }),
      });
    });

    it("should handle HTTP error responses", async () => {
      (global.fetch as jest.Mock).mockResolvedValue({
        ok: false,
        status: 500,
        statusText: "Internal Server Error",
      });

      (prisma.demo_bookings.update as jest.Mock).mockResolvedValue({});

      const result = await sendDemoBookingWebhook(
        "booking-error",
        mockBookingData,
      );

      expect(result.success).toBe(false);
      expect(result.error).toContain("500");
    });

    it("should fail gracefully when webhook URL is not configured", async () => {
      delete process.env.N8N_WEBHOOK_URL;

      const result = await sendDemoBookingWebhook(
        "booking-noconfig",
        mockBookingData,
      );

      expect(result.success).toBe(false);
      expect(result.error).toBe("Webhook configuration missing");
      expect(global.fetch).not.toHaveBeenCalled();
    });

    it("should include correct payload structure", async () => {
      (global.fetch as jest.Mock).mockResolvedValueOnce({
        ok: true,
        text: async () => "Success",
      });

      (prisma.demo_bookings.update as jest.Mock).mockResolvedValue({});

      await sendDemoBookingWebhook("booking-payload-test", mockBookingData);

      const fetchCall = (global.fetch as jest.Mock).mock.calls[0];
      const payload = JSON.parse(fetchCall[1].body);

      expect(payload).toMatchObject({
        event: "demo_booking_created",
        timestamp: expect.any(String),
        booking: {
          id: "booking-payload-test",
          fullName: "Ahmed Al-Mansouri",
          email: "ahmed@example.com",
          telephone: "+971501234567",
          date: "2025-10-25",
          time: "10:00",
          duration: "1hr",
          specialRequirements: "Prefer Arabic-speaking staff",
          language: "ar",
          status: "pending",
        },
      });
    });

    it("should handle timeout errors", async () => {
      (global.fetch as jest.Mock).mockImplementation(
        () =>
          new Promise((_, reject) => {
            const error = new Error("Timeout");
            error.name = "AbortError";
            setTimeout(() => reject(error), 100);
          }),
      );

      (prisma.demo_bookings.update as jest.Mock).mockResolvedValue({});

      const result = await sendDemoBookingWebhook(
        "booking-timeout",
        mockBookingData,
      );

      expect(result.success).toBe(false);
      expect(result.error).toBe("Request timeout");
    });
  });
});
