import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
import { storeWebhookSecret, webhookSecretEndpoint } from './form-webhook-secret';

const CFG = { apiUrl: '/api/site-builder.php', csrfToken: 'tok-123', siteId: 42 };

beforeEach(() => {
  (window as any).WHP_CONFIG = { ...CFG };
});

afterEach(() => {
  delete (window as any).WHP_CONFIG;
  vi.unstubAllGlobals();
});

describe('webhookSecretEndpoint', () => {
  test('derives the sibling endpoint from the configured API url', () => {
    expect(webhookSecretEndpoint('/api/site-builder.php')).toBe('/api/form-webhook-secret.php');
    expect(webhookSecretEndpoint('https://panel.example.com/api/site-builder'))
      .toBe('https://panel.example.com/api/form-webhook-secret.php');
  });

  test('falls back to the absolute path when there is no configured url', () => {
    expect(webhookSecretEndpoint(undefined)).toBe('/api/form-webhook-secret.php');
    expect(webhookSecretEndpoint('')).toBe('/api/form-webhook-secret.php');
  });
});

describe('storeWebhookSecret', () => {
  test('POSTs the secret with the CSRF header and returns only the id', async () => {
    const fetchMock = vi.fn().mockResolvedValue({
      ok: true, status: 200,
      json: async () => ({ success: true, secret_id: 'whs_42_abcdef0123456789' }),
    });
    vi.stubGlobal('fetch', fetchMock);

    const result = await storeWebhookSecret('SUPERSECRET');

    expect(result).toEqual({ ok: true, secretId: 'whs_42_abcdef0123456789' });
    const [url, opts] = fetchMock.mock.calls[0];
    expect(url).toBe('/api/form-webhook-secret.php');
    // POST only -- the endpoint has no read route by design.
    expect(opts.method).toBe('POST');
    expect(opts.headers['X-CSRF-Token']).toBe('tok-123');
    expect(JSON.parse(opts.body)).toEqual({ site_id: 42, secret: 'SUPERSECRET' });
  });

  test('passes the 429 cap message through so a customer can act on it', async () => {
    vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
      ok: false, status: 429,
      json: async () => ({ success: false, error: 'Too many webhook secrets stored for this site — please contact support.' }),
    }));
    const result = await storeWebhookSecret('s');
    expect(result.ok).toBe(false);
    expect(result.error).toContain('Too many webhook secrets stored for this site');
  });

  test('a success:false body is a failure even with HTTP 200', async () => {
    vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
      ok: true, status: 200, json: async () => ({ success: false, error: 'Invalid CSRF token' }),
    }));
    expect(await storeWebhookSecret('s')).toEqual({ ok: false, error: 'Invalid CSRF token' });
  });

  test('a network failure resolves with an error rather than throwing', async () => {
    vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('offline')));
    const result = await storeWebhookSecret('s');
    expect(result.ok).toBe(false);
    expect(result.secretId).toBeUndefined();
  });

  test('standalone mode (no WHP_CONFIG) never posts anywhere', async () => {
    delete (window as any).WHP_CONFIG;
    const fetchMock = vi.fn();
    vi.stubGlobal('fetch', fetchMock);
    const result = await storeWebhookSecret('s');
    expect(result.ok).toBe(false);
    expect(fetchMock).not.toHaveBeenCalled();
  });
});
