import { describe, it, expect } from 'vitest';
import {
  ALLOWED_KEX,
  ALLOWED_CIPHER,
  ALLOWED_HOST_KEY,
  ALLOWED_HMAC,
  ALLOWED_COMPRESSION,
  buildAlgorithmsOption,
  isAllowedAlgorithm,
  isAllowedHostKeyType,
} from './algorithms.js';

describe('ssh/algorithms', () => {
  it('excludes weak algorithms (CBC, SHA1, MD5, DSA, arcfour)', () => {
    const allLists = [
      ALLOWED_KEX,
      ALLOWED_CIPHER,
      ALLOWED_HOST_KEY,
      ALLOWED_HMAC,
      ALLOWED_COMPRESSION,
    ].flatMap((l) => [...l] as string[]);
    const forbidden = [
      'ssh-dss',
      'ssh-rsa', // SHA1 RSA (use rsa-sha2-*)
      'hmac-sha1',
      'hmac-md5',
      'aes256-cbc',
      'aes192-cbc',
      'aes128-cbc',
      'blowfish-cbc',
      '3des-cbc',
      'arcfour',
      'arcfour128',
      'arcfour256',
      'cast128-cbc',
      'diffie-hellman-group1-sha1',
      'diffie-hellman-group14-sha1',
      'zlib',
      'zlib@openssh.com',
    ];
    for (const f of forbidden) {
      expect(allLists, `forbidden: ${f}`).not.toContain(f);
    }
  });

  it('includes a modern strong baseline (Ed25519, curve25519, chacha20, gcm, etm)', () => {
    expect(ALLOWED_HOST_KEY).toContain('ssh-ed25519');
    expect(ALLOWED_KEX).toContain('curve25519-sha256');
    expect(ALLOWED_CIPHER).toContain('chacha20-poly1305@openssh.com');
    expect(ALLOWED_CIPHER).toContain('aes256-gcm@openssh.com');
    expect(ALLOWED_HMAC).toContain('hmac-sha2-256-etm@openssh.com');
  });

  it('ranks the strongest options first (preference order)', () => {
    // Most-favored first per ssh2 docs.
    expect(ALLOWED_KEX[0]).toBe('curve25519-sha256');
    expect(ALLOWED_CIPHER[0]).toBe('chacha20-poly1305@openssh.com');
    expect(ALLOWED_HOST_KEY[0]).toBe('ssh-ed25519');
    expect(ALLOWED_HMAC[0]).toBe('hmac-sha2-256-etm@openssh.com');
  });

  it('keeps compression off', () => {
    expect(ALLOWED_COMPRESSION).toEqual(['none']);
  });

  it('buildAlgorithmsOption returns mutable copies (not the readonly originals)', () => {
    const opts = buildAlgorithmsOption();
    expect(opts.kex).toEqual(ALLOWED_KEX);
    expect(opts.cipher).toEqual(ALLOWED_CIPHER);
    expect(opts.serverHostKey).toEqual(ALLOWED_HOST_KEY);
    expect(opts.hmac).toEqual(ALLOWED_HMAC);
    expect(opts.compress).toEqual(ALLOWED_COMPRESSION);
    // mutating the returned arrays should not affect the readonly originals
    (opts.kex as unknown as string[]).push('mutated');
    expect(ALLOWED_KEX).not.toContain('mutated');
  });

  it('isAllowedAlgorithm: accepts allowed, rejects forbidden', () => {
    expect(isAllowedAlgorithm('curve25519-sha256')).toBe(true);
    expect(isAllowedAlgorithm('aes256-gcm@openssh.com')).toBe(true);
    expect(isAllowedAlgorithm('ssh-ed25519')).toBe(true);
    expect(isAllowedAlgorithm('hmac-sha1')).toBe(false);
    expect(isAllowedAlgorithm('ssh-dss')).toBe(false);
    expect(isAllowedAlgorithm('aes256-cbc')).toBe(false);
    expect(isAllowedAlgorithm('')).toBe(false);
    expect(isAllowedAlgorithm('totally-fake-algo')).toBe(false);
  });

  it('isAllowedHostKeyType: only matches host key category', () => {
    expect(isAllowedHostKeyType('ssh-ed25519')).toBe(true);
    expect(isAllowedHostKeyType('rsa-sha2-256')).toBe(true);
    // KEX, cipher, hmac are not host-key types even if otherwise allowed
    expect(isAllowedHostKeyType('curve25519-sha256')).toBe(false);
    expect(isAllowedHostKeyType('aes256-gcm@openssh.com')).toBe(false);
    expect(isAllowedHostKeyType('hmac-sha2-256-etm@openssh.com')).toBe(false);
    expect(isAllowedHostKeyType('ssh-rsa')).toBe(false);
    expect(isAllowedHostKeyType('ssh-dss')).toBe(false);
  });
});
