import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { SEVERITY, WEIGHTS, riskScore, verdict, riskBand, gradeFromPassRate, QUALITY_CATEGORIES } from '../../scanners/lib/severity.mjs';

describe('SEVERITY constants', () => {
  it('has all 5 levels', () => {
    assert.deepStrictEqual(Object.keys(SEVERITY).sort(), ['critical', 'high', 'info', 'low', 'medium']);
  });

  it('is frozen', () => {
    assert.throws(() => { SEVERITY.critical = 'x'; }, TypeError);
  });
});

describe('WEIGHTS named export (v5 F3 prep)', () => {
  it('exposes critical=25', () => {
    assert.strictEqual(WEIGHTS.critical, 25);
  });

  it('exposes high=10, medium=4, low=1, info=0', () => {
    assert.strictEqual(WEIGHTS.high, 10);
    assert.strictEqual(WEIGHTS.medium, 4);
    assert.strictEqual(WEIGHTS.low, 1);
    assert.strictEqual(WEIGHTS.info, 0);
  });
});

describe('riskScore', () => {
  it('returns 0 for empty counts', () => {
    assert.strictEqual(riskScore({}), 0);
  });

  it('returns 0 for info-only findings', () => {
    assert.strictEqual(riskScore({ info: 10 }), 0);
  });

  it('scores low findings at 1 point each', () => {
    assert.strictEqual(riskScore({ low: 5 }), 5);
  });

  it('scores medium findings at 4 points each', () => {
    assert.strictEqual(riskScore({ medium: 3 }), 12);
  });

  it('scores high findings at 10 points each', () => {
    assert.strictEqual(riskScore({ high: 2 }), 20);
  });

  it('scores critical findings at 25 points each', () => {
    assert.strictEqual(riskScore({ critical: 1 }), 25);
  });

  it('caps at 100', () => {
    assert.strictEqual(riskScore({ critical: 10 }), 100);
  });

  it('combines all severities', () => {
    assert.strictEqual(riskScore({ critical: 1, high: 1, medium: 1, low: 1, info: 1 }), 40);
  });
});

describe('verdict', () => {
  it('returns PASS for no findings', () => {
    assert.strictEqual(verdict({}), 'PASS');
  });

  it('returns PASS for low findings only', () => {
    assert.strictEqual(verdict({ low: 5, info: 10 }), 'PASS');
  });

  it('returns WARNING for any high finding', () => {
    assert.strictEqual(verdict({ high: 1 }), 'WARNING');
  });

  it('returns FAIL for any critical finding', () => {
    assert.strictEqual(verdict({ critical: 1 }), 'FAIL');
  });

  it('returns FAIL for score >= 61', () => {
    assert.strictEqual(verdict({ high: 6, medium: 1 }), 'FAIL');
  });

  it('returns WARNING for score >= 21', () => {
    assert.strictEqual(verdict({ medium: 6 }), 'WARNING');
  });
});

describe('riskBand', () => {
  it('returns Low for score 0', () => {
    assert.strictEqual(riskBand(0), 'Low');
  });

  it('returns Low for score 10', () => {
    assert.strictEqual(riskBand(10), 'Low');
  });

  it('returns Medium for score 11-30', () => {
    assert.strictEqual(riskBand(20), 'Medium');
  });

  it('returns High for score 31-60', () => {
    assert.strictEqual(riskBand(50), 'High');
  });

  it('returns Critical for score 61-80', () => {
    assert.strictEqual(riskBand(70), 'Critical');
  });

  it('returns Extreme for score > 80', () => {
    assert.strictEqual(riskBand(90), 'Extreme');
  });
});

describe('gradeFromPassRate', () => {
  it('returns A for 90+', () => {
    assert.strictEqual(gradeFromPassRate(95), 'A');
  });

  it('returns B for 75-89', () => {
    assert.strictEqual(gradeFromPassRate(80), 'B');
  });

  it('returns C for 60-74', () => {
    assert.strictEqual(gradeFromPassRate(65), 'C');
  });

  it('returns D for 40-59', () => {
    assert.strictEqual(gradeFromPassRate(50), 'D');
  });

  it('returns F for below 40', () => {
    assert.strictEqual(gradeFromPassRate(20), 'F');
  });
});

describe('QUALITY_CATEGORIES', () => {
  it('has expected categories', () => {
    assert.ok(QUALITY_CATEGORIES.STRUCTURE);
    assert.ok(QUALITY_CATEGORIES.FEATURES);
    assert.ok(QUALITY_CATEGORIES.SECURITY);
  });

  it('is frozen', () => {
    assert.throws(() => { QUALITY_CATEGORIES.NEW = 'x'; }, TypeError);
  });
});
