/**
 * Unit tests for /api/webhooks/whatsapp
 * Tests WhatsApp webhook verification (GET) and incoming messages (POST)
 */

// Mock all dependencies BEFORE imports
jest.mock("@/lib/logger", () => ({
  logger: {
    warn: jest.fn(),
    error: jest.fn(),
    info: jest.fn(),
    debug: jest.fn(),
    security: jest.fn(),
  },
}));

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

jest.mock("@/lib/services/org-whatsapp-keys.service", () => ({
  orgWhatsAppKeysService: {
    getOrgByVerifyToken: jest.fn(),
    markWebhookVerified: jest.fn(),
    getOrgByPhoneNumberId: jest.fn(),
    getDecryptedCredentials: jest.fn(),
  },
}));

jest.mock("@/lib/services/system-settings.service", () => ({
  systemSettingsService: {
    getN8NWebhookToken: jest.fn(),
  },
}));

jest.mock("@/lib/resilience", () => ({
  withWhatsAppGuard: jest
    .fn()
    .mockResolvedValue({ success: true, data: { ok: true } }),
}));

jest.mock("@upstash/ratelimit", () => ({
  Ratelimit: jest.fn().mockImplementation(() => ({
    limit: jest.fn().mockResolvedValue({
      success: true,
      limit: 100,
      remaining: 99,
      reset: Date.now() + 60000,
    }),
  })),
}));

jest.mock("@upstash/redis", () => ({
  Redis: jest.fn(),
}));

jest.mock("next/server", () => ({
  NextRequest: jest.fn(),
  NextResponse: {
    json: jest.fn((data, options) => ({
      status: options?.status || 200,
      headers: new Map(Object.entries(options?.headers || {})),
      json: async () => data,
    })),
  },
}));

import { NextRequest, NextResponse } from "next/server";
import { orgWhatsAppKeysService } from "@/lib/services/org-whatsapp-keys.service";

// Webhook route not yet implemented
describe.skip("WhatsApp Webhook", () => {
  let webhookModule: any;

  beforeAll(async () => {
    webhookModule = await import("@/app/api/webhooks/whatsapp/route");
  });

  beforeEach(() => {
    jest.clearAllMocks();
    (NextResponse.json as jest.Mock).mockImplementation((data, options) => ({
      status: options?.status || 200,
      headers: new Map(Object.entries(options?.headers || {})),
      json: async () => data,
    }));
  });

  describe("GET - Webhook Verification", () => {
    const mockGetRequest = (params: Record<string, string>) => {
      const searchParams = new URLSearchParams(params);
      return {
        nextUrl: { searchParams },
      } as unknown as NextRequest;
    };

    it("should return 400 if mode is not subscribe", async () => {
      const request = mockGetRequest({
        "hub.mode": "unsubscribe",
        "hub.verify_token": "test-token",
        "hub.challenge": "challenge123",
      });

      const response = await webhookModule.GET(request);
      expect(response.status).toBe(400);
    });

    it("should return 400 if token or challenge is missing", async () => {
      const request = mockGetRequest({
        "hub.mode": "subscribe",
      });

      const response = await webhookModule.GET(request);
      expect(response.status).toBe(400);
    });

    it("should return challenge when org verify token matches", async () => {
      (
        orgWhatsAppKeysService.getOrgByVerifyToken as jest.Mock
      ).mockResolvedValue({
        id: "org-1",
        name: "Test Org",
      });
      (
        orgWhatsAppKeysService.markWebhookVerified as jest.Mock
      ).mockResolvedValue(undefined);

      const request = mockGetRequest({
        "hub.mode": "subscribe",
        "hub.verify_token": "valid-token",
        "hub.challenge": "challenge-abc",
      });

      const response = await webhookModule.GET(request);
      expect(response.status).toBe(200);
    });

    it("should fall back to global verify token", async () => {
      (
        orgWhatsAppKeysService.getOrgByVerifyToken as jest.Mock
      ).mockResolvedValue(null);
      process.env.WHATSAPP_VERIFY_TOKEN = "global-token";

      const request = mockGetRequest({
        "hub.mode": "subscribe",
        "hub.verify_token": "global-token",
        "hub.challenge": "challenge-xyz",
      });

      const response = await webhookModule.GET(request);
      expect(response.status).toBe(200);

      delete process.env.WHATSAPP_VERIFY_TOKEN;
    });

    it("should return 403 if no token matches", async () => {
      (
        orgWhatsAppKeysService.getOrgByVerifyToken as jest.Mock
      ).mockResolvedValue(null);
      delete process.env.WHATSAPP_VERIFY_TOKEN;

      const request = mockGetRequest({
        "hub.mode": "subscribe",
        "hub.verify_token": "unknown-token",
        "hub.challenge": "challenge-xyz",
      });

      const response = await webhookModule.GET(request);
      expect(response.status).toBe(403);
    });
  });

  describe("POST - Incoming Messages", () => {
    const validPayload = {
      object: "whatsapp_business_account",
      entry: [
        {
          changes: [
            {
              field: "messages",
              value: {
                metadata: {
                  phone_number_id: "12345",
                  display_phone_number: "+1234567890",
                },
                contacts: [{ profile: { name: "Test Customer" } }],
                messages: [
                  {
                    id: "msg-1",
                    from: "+97412345678",
                    timestamp: String(Math.floor(Date.now() / 1000)),
                    type: "text",
                    text: { body: "Hello" },
                  },
                ],
              },
            },
          ],
        },
      ],
    };

    const mockPostRequest = (body: any, signature?: string) =>
      ({
        text: jest.fn().mockResolvedValue(JSON.stringify(body)),
        headers: {
          get: jest.fn((key: string) => {
            if (key === "x-hub-signature-256") return signature || null;
            if (key === "x-forwarded-for") return "1.2.3.4";
            return null;
          }),
        },
        ip: "1.2.3.4",
        url: "https://example.com/api/webhooks/whatsapp",
      }) as unknown as NextRequest;

    it("should return 400 for invalid JSON", async () => {
      const request = {
        text: jest.fn().mockResolvedValue("not-json{{{"),
        headers: {
          get: jest.fn(() => null),
        },
        ip: "1.2.3.4",
        url: "https://example.com/api/webhooks/whatsapp",
      } as unknown as NextRequest;

      const response = await webhookModule.POST(request);
      const body = await response.json();
      expect(response.status).toBe(400);
      expect(body.error).toBe("Invalid JSON");
    });

    it("should return ok for non-WhatsApp payload", async () => {
      const request = mockPostRequest({ object: "instagram" });
      const response = await webhookModule.POST(request);
      const body = await response.json();
      expect(body.status).toBe("ok");
    });

    it("should return ok for empty entry array", async () => {
      const request = mockPostRequest({
        object: "whatsapp_business_account",
        entry: [],
      });
      const response = await webhookModule.POST(request);
      const body = await response.json();
      expect(body.status).toBe("ok");
    });

    it("should return 400 if phone_number_id is missing", async () => {
      const request = mockPostRequest({
        object: "whatsapp_business_account",
        entry: [{ changes: [{ value: { metadata: {} } }] }],
      });
      const response = await webhookModule.POST(request);
      const body = await response.json();
      expect(response.status).toBe(400);
      expect(body.error).toBe("Missing phone_number_id");
    });

    it("should return 404 for unknown phone number with no legacy fallback", async () => {
      (
        orgWhatsAppKeysService.getOrgByPhoneNumberId as jest.Mock
      ).mockResolvedValue(null);
      delete process.env.WHATSAPP_APP_SECRET;

      const request = mockPostRequest(validPayload);
      const response = await webhookModule.POST(request);
      const body = await response.json();
      expect(response.status).toBe(404);
      expect(body.error).toBe("Unknown phone number");
    });

    it("should return 500 if credentials not found for org", async () => {
      (
        orgWhatsAppKeysService.getOrgByPhoneNumberId as jest.Mock
      ).mockResolvedValue({
        id: "org-1",
        ownerId: "user-1",
      });
      (
        orgWhatsAppKeysService.getDecryptedCredentials as jest.Mock
      ).mockResolvedValue(null);

      const request = mockPostRequest(validPayload);
      const response = await webhookModule.POST(request);
      const body = await response.json();
      expect(response.status).toBe(500);
      expect(body.error).toBe("Configuration error");
    });

    it("should return 401 if signature verification fails", async () => {
      (
        orgWhatsAppKeysService.getOrgByPhoneNumberId as jest.Mock
      ).mockResolvedValue({
        id: "org-1",
        ownerId: "user-1",
      });
      (
        orgWhatsAppKeysService.getDecryptedCredentials as jest.Mock
      ).mockResolvedValue({
        appSecret: "test-secret",
      });

      const request = mockPostRequest(validPayload, "sha256=invalid_signature");
      const response = await webhookModule.POST(request);
      const body = await response.json();
      expect(response.status).toBe(401);
      expect(body.error).toBe("Invalid signature");
    });

    it("should return 500 on unexpected errors", async () => {
      (
        orgWhatsAppKeysService.getOrgByPhoneNumberId as jest.Mock
      ).mockRejectedValue(new Error("DB error"));

      const request = mockPostRequest(validPayload);
      const response = await webhookModule.POST(request);
      const body = await response.json();
      expect(response.status).toBe(500);
      expect(body.error).toBe("Internal error");
    });
  });
});
