import { describe, it, expect } from 'vitest';
import { assertSafeProviderUrl, isBlockedAddress } from '@/lib/ai/safeUrl';

// SSRF guard for user-supplied provider base URLs (EVALUATION.md P1).
// Self-hosted intent: private-LAN + loopback are ALLOWED (Ollama, gateways,
// dev); only link-local/cloud-metadata + unspecified + non-http(s) are blocked.

describe('isBlockedAddress', () => {
  it('blocks link-local / cloud-metadata and unspecified', () => {
    expect(isBlockedAddress('169.254.169.254')).toBe(true); // cloud metadata
    expect(isBlockedAddress('169.254.0.1')).toBe(true);
    expect(isBlockedAddress('0.0.0.0')).toBe(true);
    expect(isBlockedAddress('::')).toBe(true);
    expect(isBlockedAddress('fe80::1')).toBe(true); // IPv6 link-local
    expect(isBlockedAddress('::ffff:169.254.169.254')).toBe(true); // mapped
  });

  it('allows private LAN, loopback, and public (the legitimate targets)', () => {
    expect(isBlockedAddress('192.168.1.10')).toBe(false);
    expect(isBlockedAddress('10.0.0.5')).toBe(false);
    expect(isBlockedAddress('172.16.0.9')).toBe(false);
    expect(isBlockedAddress('127.0.0.1')).toBe(false);
    expect(isBlockedAddress('1.1.1.1')).toBe(false);
  });
});

describe('assertSafeProviderUrl', () => {
  it('rejects non-http(s) schemes', async () => {
    await expect(assertSafeProviderUrl('ftp://example.com')).rejects.toThrow();
    await expect(assertSafeProviderUrl('file:///etc/passwd')).rejects.toThrow();
  });

  it('rejects an unparseable URL', async () => {
    await expect(assertSafeProviderUrl('not a url')).rejects.toThrow();
  });

  it('rejects cloud-metadata / link-local IP literals', async () => {
    await expect(
      assertSafeProviderUrl('http://169.254.169.254/latest/meta-data/'),
    ).rejects.toThrow();
    await expect(assertSafeProviderUrl('http://0.0.0.0:11434')).rejects.toThrow();
    await expect(assertSafeProviderUrl('http://[fe80::1]:11434')).rejects.toThrow();
  });

  it('allows private-LAN and loopback Ollama targets', async () => {
    await expect(
      assertSafeProviderUrl('http://192.168.1.50:11434/api/chat'),
    ).resolves.toBeUndefined();
    await expect(
      assertSafeProviderUrl('http://127.0.0.1:11434/api/tags'),
    ).resolves.toBeUndefined();
    await expect(
      assertSafeProviderUrl('http://10.0.0.2:8000/v1/chat/completions'),
    ).resolves.toBeUndefined();
  });

  // IPv6 literals carry brackets in URL.hostname; the guard must strip BOTH
  // and classify by the real address (catches a missing-/g bracket-strip bug).
  it('handles bracketed IPv6 literals correctly', async () => {
    await expect(
      assertSafeProviderUrl('http://[::1]:11434/api/tags'),
    ).resolves.toBeUndefined(); // loopback allowed
    await expect(
      assertSafeProviderUrl('http://[fe80::1]:11434'),
    ).rejects.toThrow(/link-local/); // blocked by isBlockedAddress, not a DNS miss
  });
});
