import test from 'node:test';
import assert from 'node:assert/strict';

import { sanitizeEvidence } from '../src/release-observability.js';

const validEvidence = () => ({
  schemaVersion: 1,
  releaseTag: 'daily-2026-08-22.1',
  commit: 'ca31e6d38bec649407f63880504554c59f2878ae',
  generatedAtUtc: '2026-08-22T12:00:00Z',
  checks: [
    { id: 'app.healthz', boundary: 'app', status: 'pass', latencyMs: 12 },
    { id: 'api.analyze', boundary: 'api', status: 'pass', counters: { ok: 40, fail: 1 } },
    { id: 'queue.depth', boundary: 'queue', status: 'pass', counters: { ok: 30, fail: 0 } },
    { id: 'model.inference', boundary: 'model', status: 'pass', latencyMs: 900, counters: { ok: 25, fail: 0, abstain: 3 } },
  ],
});

test('sanitizer keeps bounded sanitized release evidence intact', () => {
  const result = sanitizeEvidence(validEvidence());
  assert.equal(result.ok, true);
  assert.deepEqual(result.evidence, {
    schemaVersion: 1,
    releaseTag: 'daily-2026-08-22.1',
    commit: 'ca31e6d38bec649407f63880504554c59f2878ae',
    generatedAtUtc: '2026-08-22T12:00:00Z',
    checks: [
      { id: 'app.healthz', boundary: 'app', status: 'pass', failureClass: null, latencyMs: 12, counters: {} },
      { id: 'api.analyze', boundary: 'api', status: 'pass', failureClass: null, latencyMs: null, counters: { ok: 40, fail: 1 } },
      { id: 'queue.depth', boundary: 'queue', status: 'pass', failureClass: null, latencyMs: null, counters: { ok: 30, fail: 0 } },
      { id: 'model.inference', boundary: 'model', status: 'pass', failureClass: null, latencyMs: 900, counters: { ok: 25, fail: 0, abstain: 3 } },
    ],
  });
});

test('sanitizer drops session identifiers, photo payloads, credentials, and free-text notes', () => {
  // Assembled at runtime so no contiguous private-key marker ever lands in Git history.
  const privateKeyFixture = `-----BEGIN ${'OPENSSH'} PRIVATE KEY${'-----'}`;
  const hostile = {
    ...validEvidence(),
    sessionToken: 'sess_live_abc123',
    adminCookie: 'timmy_agent=secret-cookie-value',
    environmentDump: { NODE_ENV: 'production', SECRET_TOKEN: 'must-not-leak' },
    operatorEmail: 'someone@example.com',
    credentialFile: privateKeyFixture,
    photos: [{ dataBase64: 'aGVsbG8gd29ybGQgaGVsbG8gd29ybGQ=' }],
    checks: [
      ...validEvidence().checks,
      {
        id: 'leak',
        boundary: 'app',
        status: 'pass',
        noteText: 'patient said blood at 3am, see photo hash e3b0c442',
        imageHash: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
        base64Payload: 'aGVsbG8=',
        authHeader: `Bearer ${'sk-'}live-abcdef1234567890`,
      },
    ],
  };

  const result = sanitizeEvidence(hostile);
  assert.equal(result.ok, true);
  const serialized = JSON.stringify(result.evidence);
  for (const forbidden of [
    'sess_live_abc123',
    'secret-cookie-value',
    'must-not-leak',
    'someone@example.com',
    'PRIVATE KEY',
    'aGVsbG8',
    'patient said blood',
    'e3b0c44298fc1c14',
    'sk-live-abcdef1234567890',
  ]) {
    assert.equal(serialized.includes(forbidden), false, `sanitized output leaked ${forbidden}`);
  }
  assert.equal(result.evidence.checks.some(check => check.id === 'leak'), false);
});

test('sanitizer fail-closes evidence carrying hostile failure-class payloads', () => {
  const hostileFailureClasses = [
    'worker.unavailable\nsecond line',
    'worker.unavailable\r\ncarriage',
    '\x1b[31mANSI-red',
    'worker.unavailable\x1b[0m',
    'bell\x07class',
    'tab\tseparated',
    'DROP TABLE users',
    'patient reported blood at 3am',
    'sess_live_abc123def456',
    '-----BEGIN OPENSSH PRIVATE KEY-----',
    'a@b.example.com',
    '.leading.dot',
    'trailing.dot.',
    'double..dot',
    '-leading-dash.d',
    'd.trailing-dash-',
    '-'.repeat(65),
    42,
    {},
    [],
  ];
  for (const failureClass of hostileFailureClasses) {
    const evidence = validEvidence();
    evidence.checks = [{ id: 'app.healthz', boundary: 'app', status: 'fail', failureClass }];
    const result = sanitizeEvidence(evidence);
    assert.equal(result.ok, false, `expected rejection for failureClass ${JSON.stringify(failureClass)}`);
  }
});

test('sanitizer accepts only the exact privacy-safe failure-class vocabulary in slug grammar', () => {
  const baseChecks = validEvidence().checks;
  for (const failureClass of ['worker.unavailable', 'vision.timeout', 'model.error']) {
    const evidence = validEvidence();
    evidence.checks = [
      ...baseChecks.filter(check => check.boundary !== 'api'),
      { id: 'api.analyze', boundary: 'api', status: 'degraded', failureClass },
    ];
    const result = sanitizeEvidence(evidence);
    assert.equal(result.ok, true, `vocabulary class ${failureClass} must survive sanitization`);
    assert.equal(result.evidence.checks.some(check => check.failureClass === failureClass), true);
  }
  for (const failureClass of ['made.up.class', 'worker.somethingelse', 'VISION.TIMEOUT', 'vision..timeout']) {
    const evidence = validEvidence();
    evidence.checks = [
      ...baseChecks.filter(check => check.boundary !== 'api'),
      { id: 'api.analyze', boundary: 'api', status: 'degraded', failureClass },
    ];
    const result = sanitizeEvidence(evidence);
    assert.equal(result.ok, false, `non-vocabulary class ${failureClass} must be rejected`);
  }
});
