import { describe, it, expect } from 'vitest';
import { SendGridProviderAdapter } from '@zyphon/provider-email-sendgrid';
import { TwilioSmsProviderAdapter } from '@zyphon/provider-sms-twilio';
import { MetaWhatsAppProviderAdapter } from '@zyphon/provider-whatsapp-meta';
import { TwilioWhatsAppProviderAdapter } from '@zyphon/provider-whatsapp-twilio';
import { SesProviderAdapter } from '@zyphon/provider-email-ses';
import { ZohoEmailProviderAdapter } from '@zyphon/provider-email-zoho';
import { sampleNormalizedMessage } from '@zyphon/test-utils';

describe('Real Provider Adapters & Webhooks', () => {
  describe('SendGrid Adapter', () => {
    it('dispatches email and parses webhook event', async () => {
      let capturedUrl = '';
      let capturedBody: any = null;
      let capturedAuth = '';

      const fakeFetch: typeof fetch = async (url, init) => {
        capturedUrl = String(url);
        capturedAuth = String(init?.headers && (init.headers as any)['Authorization']);
        capturedBody = JSON.parse(String(init?.body));
        return new Response(JSON.stringify({}), {
          status: 202,
          headers: { 'x-message-id': 'sg_msg_12345' },
        });
      };

      const adapter = new SendGridProviderAdapter({ fetchFn: fakeFetch });
      const signal = new AbortController().signal;

      const result = await adapter.send(
        sampleNormalizedMessage,
        { api_key: 'SG.test_key', from: 'alerts@zyphon.local' },
        signal
      );

      expect(result.success).toBe(true);
      expect(result.provider_reference).toBe('sg_msg_12345');
      expect(capturedUrl).toBe('https://api.sendgrid.com/v3/mail/send');
      expect(capturedAuth).toBe('Bearer SG.test_key');
      expect(capturedBody.personalizations[0].to[0].email).toBe('user@example.com');

      // Test Webhook parsing
      const webhookEvent = adapter.parseWebhook({
        headers: {},
        body: [
          {
            event: 'delivered',
            sg_message_id: 'sg_msg_12345',
            timestamp: 1700000000,
          },
        ],
      });

      expect(webhookEvent).toBeDefined();
      expect(webhookEvent?.status).toBe('delivered');
      expect(webhookEvent?.message_id).toBe('sg_msg_12345');
    });

    it('handles 401 unauthorized as CREDENTIAL_ERROR', async () => {
      const fakeFetch: typeof fetch = async () => {
        return new Response(JSON.stringify({ errors: [{ message: 'Bad API key' }] }), {
          status: 401,
        });
      };

      const adapter = new SendGridProviderAdapter({ fetchFn: fakeFetch });
      const signal = new AbortController().signal;

      const result = await adapter.send(
        sampleNormalizedMessage,
        { api_key: 'SG.invalid' },
        signal
      );

      expect(result.success).toBe(false);
      expect(result.error?.code).toBe('CREDENTIAL_ERROR');
    });
  });

  describe('Twilio SMS Adapter', () => {
    it('dispatches SMS and parses delivery status webhook', async () => {
      let capturedBody: any = null;
      const fakeFetch: typeof fetch = async (_url, init) => {
        capturedBody = new URLSearchParams(String(init?.body));
        return new Response(JSON.stringify({ sid: 'SM_twilio_123', status: 'queued' }), {
          status: 201,
        });
      };

      const adapter = new TwilioSmsProviderAdapter({ fetchFn: fakeFetch });
      const signal = new AbortController().signal;

      const smsMsg = { ...sampleNormalizedMessage, channel: 'sms' as const, recipient: '+14155552671' };
      const result = await adapter.send(
        smsMsg,
        { account_sid: 'AC_test', auth_token: 'auth_test', from: '+15550001111' },
        signal
      );

      expect(result.success).toBe(true);
      expect(result.provider_reference).toBe('SM_twilio_123');
      expect(capturedBody.get('To')).toBe('+14155552671');
      expect(capturedBody.get('From')).toBe('+15550001111');

      // Test webhook callback
      const webhookEvent = adapter.parseWebhook({
        headers: {},
        body: {
          MessageSid: 'SM_twilio_123',
          MessageStatus: 'delivered',
        },
      });

      expect(webhookEvent?.status).toBe('delivered');
      expect(webhookEvent?.message_id).toBe('SM_twilio_123');
    });
  });

  describe('Meta WhatsApp Adapter', () => {
    it('dispatches WhatsApp template or text and parses webhook', async () => {
      let capturedBody: any = null;
      const fakeFetch: typeof fetch = async (_url, init) => {
        capturedBody = JSON.parse(String(init?.body));
        return new Response(JSON.stringify({ messages: [{ id: 'wamid.HBgM' }] }), {
          status: 200,
        });
      };

      const adapter = new MetaWhatsAppProviderAdapter({ fetchFn: fakeFetch });
      const signal = new AbortController().signal;

      const waMsg = { ...sampleNormalizedMessage, channel: 'whatsapp' as const, recipient: '+14155552671' };
      const result = await adapter.send(
        waMsg,
        { phone_number_id: '10987654321', access_token: 'EAAB_token' },
        signal
      );

      expect(result.success).toBe(true);
      expect(result.provider_reference).toBe('wamid.HBgM');
      // Strips leading '+' for Meta API
      expect(capturedBody.to).toBe('14155552671');

      // Test webhook parsing
      const webhookEvent = adapter.parseWebhook({
        headers: {},
        body: {
          entry: [
            {
              changes: [
                {
                  value: {
                    statuses: [{ id: 'wamid.HBgM', status: 'delivered', timestamp: '1700000000' }],
                  },
                },
              ],
            },
          ],
        },
      });

      expect(webhookEvent?.status).toBe('delivered');
      expect(webhookEvent?.message_id).toBe('wamid.HBgM');
    });
  });

  describe('Twilio WhatsApp Adapter', () => {
    it('prefixes recipient and sender with whatsapp:', async () => {
      let capturedBody: any = null;
      const fakeFetch: typeof fetch = async (_url, init) => {
        capturedBody = new URLSearchParams(String(init?.body));
        return new Response(JSON.stringify({ sid: 'WA_twilio_456' }), {
          status: 201,
        });
      };

      const adapter = new TwilioWhatsAppProviderAdapter({ fetchFn: fakeFetch });
      const signal = new AbortController().signal;

      const waMsg = { ...sampleNormalizedMessage, channel: 'whatsapp' as const, recipient: '+14155552671' };
      const result = await adapter.send(
        waMsg,
        { account_sid: 'AC_test', auth_token: 'auth_test', from: '+15559998888' },
        signal
      );

      expect(result.success).toBe(true);
      expect(capturedBody.get('To')).toBe('whatsapp:+14155552671');
      expect(capturedBody.get('From')).toBe('whatsapp:+15559998888');
    });
  });

  describe('AWS SES Adapter', () => {
    it('signs request with SigV4 and dispatches email', async () => {
      let capturedHeaders: any = null;
      const fakeFetch: typeof fetch = async (_url, init) => {
        capturedHeaders = init?.headers;
        return new Response(JSON.stringify({ MessageId: 'ses_msg_9999' }), {
          status: 200,
        });
      };

      const adapter = new SesProviderAdapter({ fetchFn: fakeFetch });
      const signal = new AbortController().signal;

      const result = await adapter.send(
        sampleNormalizedMessage,
        {
          region: 'us-east-1',
          access_key_id: 'AKIA_TEST_KEY',
          secret_access_key: 'secret_test_key',
          from: 'notifications@zyphon.local',
        },
        signal
      );

      expect(result.success).toBe(true);
      expect(result.provider_reference).toBe('ses_msg_9999');
      expect(capturedHeaders['authorization']).toContain('AWS4-HMAC-SHA256');
      expect(capturedHeaders['authorization']).toContain('AKIA_TEST_KEY');
    });
  });

  describe('Zoho Email Adapter', () => {
    it('dispatches email and checks health', async () => {
      const fakeFetch: typeof fetch = async (url) => {
        if (String(url).includes('/messages')) {
          return new Response(JSON.stringify({ status: { code: 200 }, data: { messageId: 'zoho_777' } }), {
            status: 200,
          });
        }
        return new Response(JSON.stringify({ status: { code: 200 } }), { status: 200 });
      };

      const adapter = new ZohoEmailProviderAdapter({ fetchFn: fakeFetch });
      const signal = new AbortController().signal;

      const result = await adapter.send(
        sampleNormalizedMessage,
        { auth_token: 'zoho_oauth_token', from: 'admin@zoho.local' },
        signal
      );

      expect(result.success).toBe(true);
      expect(result.provider_reference).toBe('zoho_777');

      const health = await adapter.healthCheck({ auth_token: 'zoho_oauth_token' });
      expect(health.status).toBe('healthy');
    });
  });
});

