import { describe, test, expect, mock, beforeEach, afterEach } from 'bun:test';
import { WebhookManager } from '../../../src/services/webhook-manager';

describe('WebhookManager', () => {
  const envBackup = process.env;

  beforeEach(() => {
    process.env = {
      ...envBackup,
      EVOLUTION_API_URL: 'https://test-api',
      EVOLUTION_API_KEY: 'test-key',
      EVOLUTION_API_INSTANCE: 'test-instance',
      WEBHOOK_URL: 'https://test-webhook'
    };
  });

  afterEach(() => {
    process.env = envBackup;
  });

  test('should warn about missing port in internal URLs', () => {
    process.env.WEBHOOK_URL = 'http://srv-captain--taskbot/webhook';
    const consoleSpy = mock(() => {});
    console.warn = consoleSpy;
    
    WebhookManager['validateConfig']();
    
    expect(consoleSpy).toHaveBeenCalledWith(
      expect.stringContaining('missing port number')
    );
  });

  test('should accept internal URLs with ports', () => {
    process.env.WEBHOOK_URL = 'http://srv-captain--taskbot:3007/webhook';
    const consoleSpy = mock(() => {});
    console.warn = consoleSpy;
    
    WebhookManager['validateConfig']();
    
    expect(consoleSpy).not.toHaveBeenCalled();
  });

  test('should create correct config structure', () => {
    const config = WebhookManager['getConfig']();
    expect(config).toEqual({
      webhook: {
        url: 'https://test-webhook',
        enabled: true,
        webhook_by_events: true,
        webhook_base64: true,
        events: expect.any(Array)
      }
    });
  });

  test('should validate successful response', () => {
    const validResponse = {
      id: 'test-id',
      url: 'https://test-webhook',
      enabled: true,
      events: ['APPLICATION_STARTUP']
    };
    // Mock the private validateResponse method
    const mockValidate = mock(() => {});
    WebhookManager['validateResponse'] = mockValidate;
    WebhookManager['validateResponse'](validResponse);
    expect(mockValidate).toHaveBeenCalled();
  });

  test('should reject disabled webhook response', () => {
    const invalidResponse = {
      id: 'test-id',
      url: 'https://test-webhook',
      enabled: false,
      events: ['APPLICATION_STARTUP']
    };
    expect(() => {
      if (!invalidResponse.enabled) {
        throw new Error('Webhook not enabled');
      }
    }).toThrow();
  });

  test('should retry registration in background until success', async () => {
    // Acelerar reintentos
    process.env.WEBHOOK_RETRY_INITIAL_DELAY_MS = '10';
    process.env.WEBHOOK_RETRY_MAX_DELAY_MS = '20';
    process.env.WEBHOOK_RETRY_DEGRADED_INTERVAL_MS = '50';

    const originalFetch: any = globalThis.fetch;
    let setCalls = 0;

    const fetchSpy = mock(async (input: any, init?: any) => {
      const url = typeof input === 'string' ? input : input.url;

      // Self-test of our own webhook endpoint
      if (url === process.env.WEBHOOK_URL) {
        return new Response('ok', { status: 200, headers: { 'Content-Type': 'application/json' } });
      }

      // Register webhook endpoint (first fails, then succeeds)
      if (typeof url === 'string' && url.includes('/webhook/set/')) {
        setCalls++;
        if (setCalls < 2) {
          return new Response('unavailable', { status: 503, statusText: 'Service Unavailable' });
        }
        const body = JSON.stringify({
          id: 'test-id',
          url: process.env.WEBHOOK_URL,
          enabled: true,
          events: ['APPLICATION_STARTUP']
        });
        return new Response(body, { status: 200, headers: { 'Content-Type': 'application/json' } });
      }

      // Verify webhook endpoint (returns enabled)
      if (typeof url === 'string' && url.includes('/webhook/find/')) {
        const body = JSON.stringify({
          enabled: true,
          url: process.env.WEBHOOK_URL,
          events: ['APPLICATION_STARTUP']
        });
        return new Response(body, { status: 200, headers: { 'Content-Type': 'application/json' } });
      }

      return new Response('not found', { status: 404 });
    });

    // @ts-ignore
    globalThis.fetch = fetchSpy;

    try {
      // Asegurar que no haya un lazo previo corriendo
      try { (WebhookManager as any).stopAutoEnsure(); } catch {}

      WebhookManager['startAutoEnsure']();

      // Esperar a que el lazo logre activar el webhook (o agote timeout)
      const deadline = Date.now() + 2000;
      while (WebhookManager['autoEnsureActive'] && Date.now() < deadline) {
        await new Promise(res => setTimeout(res, 20));
      }

      expect(WebhookManager['autoEnsureActive']).toBe(false);
      expect(fetchSpy).toHaveBeenCalled();
      expect(setCalls).toBeGreaterThan(1);
    } finally {
      try { WebhookManager['stopAutoEnsure'](); } catch {}
      // @ts-ignore
      globalThis.fetch = originalFetch;
    }
  });
});
