import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import { categoryOf, getRuleLabel, getRuleSeverityClass } from './rule-labels.js';

// ── categoryOf ────────────────────────────────────────────────────────────────

describe('categoryOf', () => {
  it('classifies u-* rules as universal', () => {
    assert.equal(categoryOf('u-normalise-line-endings'), 'universal');
    assert.equal(categoryOf('u-dedupe-imports'), 'universal');
    assert.equal(categoryOf('u-prop-no-untracked-todo'), 'universal');
    assert.equal(categoryOf('u-prop-no-placeholder-secret'), 'universal');
  });

  it('classifies tg-* rules as target-language', () => {
    assert.equal(categoryOf('tg-java-no-system-exit'), 'target-language');
    assert.equal(categoryOf('tg-ts-no-console-log-in-service'), 'target-language');
    assert.equal(categoryOf('tg-py-no-bare-except'), 'target-language');
    assert.equal(categoryOf('tg-cs-no-environment-exit'), 'target-language');
    assert.equal(categoryOf('tg-rs-no-bare-unwrap'), 'target-language');
    assert.equal(categoryOf('tg-go-no-panic-in-service'), 'target-language');
    assert.equal(categoryOf('tg-swift-no-fatal-error'), 'target-language');
  });

  it('classifies *-prop-kg-* rules as kg', () => {
    assert.equal(categoryOf('cj-prop-kg-comp3-mapping'), 'kg');
    assert.equal(categoryOf('pj-prop-kg-number-mapping'), 'kg');
    assert.equal(categoryOf('vc-prop-kg-integer-width'), 'kg');
    assert.equal(categoryOf('jk-prop-kg-nullability-preservation'), 'kg');
    assert.equal(categoryOf('cr-prop-kg-pointer-safety'), 'kg');
  });

  it('classifies two-letter-prefix prop rules as pair (not kg)', () => {
    // The regex /^[a-z]{2}-prop-/ requires exactly 2 lowercase letters before -prop-
    assert.equal(categoryOf('cj-prop-no-float-for-money'), 'pair');
    assert.equal(categoryOf('vc-prop-no-msgbox'), 'pair');
    assert.equal(categoryOf('jt-prop-no-var'), 'pair');
    assert.equal(categoryOf('pp-prop-no-perl-die'), 'pair');
    assert.equal(categoryOf('jk-prop-no-java-accessors'), 'pair');
  });

  it('classifies three-letter-prefix prop rules (e.g. php-prop-*) as other', () => {
    // "php" has 3 chars so /^[a-z]{2}-prop-/ does not match → 'other'
    assert.equal(categoryOf('php-prop-no-legacy-mysql'), 'other');
    assert.equal(categoryOf('php-prop-no-each'), 'other');
  });

  it('classifies unknown patterns as other', () => {
    assert.equal(categoryOf('custom-rule-id'), 'other');
    assert.equal(categoryOf(''), 'other');
    assert.equal(categoryOf('random'), 'other');
  });

  it('kg takes priority over pair for two-letter prefix rules', () => {
    // cj-prop-kg-* should be kg, not pair
    assert.equal(categoryOf('cj-prop-kg-88level-enum'), 'kg');
  });
});

// ── getRuleLabel ──────────────────────────────────────────────────────────────

describe('getRuleLabel', () => {
  it('returns label and tooltip for a known rule', () => {
    const r = getRuleLabel('u-normalise-line-endings');
    assert.ok(typeof r.label === 'string' && r.label.length > 0);
    assert.ok(typeof r.tooltip === 'string' && r.tooltip.length > 0);
  });

  it('returns the rule id (hyphen → space) as label for unknown rules', () => {
    const r = getRuleLabel('my-custom-rule');
    assert.equal(r.label, 'my custom rule');
  });

  it('tooltip for unknown rule contains the rule id', () => {
    const r = getRuleLabel('some-unknown-check');
    assert.ok(r.tooltip.includes('some-unknown-check'));
  });

  it('returns a severity for rules that declare one', () => {
    const error = getRuleLabel('u-prop-no-real-secret-pattern');
    assert.equal(error.severity, 'error');

    const warn = getRuleLabel('u-prop-no-untracked-todo');
    assert.equal(warn.severity, 'warning');

    const info = getRuleLabel('u-prop-no-long-line');
    assert.equal(info.severity, 'info');
  });

  it('severity is undefined for rules that do not declare one', () => {
    const r = getRuleLabel('u-normalise-line-endings');
    assert.equal(r.severity, undefined);
  });

  it('returns labels for key pair-specific rules', () => {
    const rules = [
      'java-display-as-log',
      'vb6-csharp-drop-set',
      'php-array-short-syntax',
      'ts-var-to-let',
    ];
    for (const id of rules) {
      const r = getRuleLabel(id);
      assert.ok(r.label.length > 0, `empty label for ${id}`);
      assert.ok(r.tooltip.length > 0, `empty tooltip for ${id}`);
    }
  });

  it('returns labels for tg-* target rules', () => {
    const rules = [
      'tg-java-no-system-exit',
      'tg-ts-no-as-any',
      'tg-py-no-bare-except',
      'tg-cs-no-environment-exit',
      'tg-rs-no-bare-unwrap',
      'tg-go-no-panic-in-service',
    ];
    for (const id of rules) {
      const r = getRuleLabel(id);
      assert.ok(r.label.length > 0, `empty label for ${id}`);
    }
  });
});

// ── getRuleSeverityClass ──────────────────────────────────────────────────────

describe('getRuleSeverityClass', () => {
  it('returns red class for error severity', () => {
    const cls = getRuleSeverityClass('u-prop-no-real-secret-pattern');
    assert.ok(cls.includes('red'), `expected red class, got: ${cls}`);
  });

  it('returns amber class for warning severity', () => {
    const cls = getRuleSeverityClass('u-prop-no-untracked-todo');
    assert.ok(cls.includes('amber'), `expected amber class, got: ${cls}`);
  });

  it('returns blue class for info severity', () => {
    const cls = getRuleSeverityClass('u-prop-no-long-line');
    assert.ok(cls.includes('blue'), `expected blue class, got: ${cls}`);
  });

  it('returns muted class for rules without severity', () => {
    const cls = getRuleSeverityClass('u-normalise-line-endings');
    assert.ok(cls.includes('muted'), `expected muted class, got: ${cls}`);
  });

  it('returns muted class for unknown rule ids', () => {
    const cls = getRuleSeverityClass('no-such-rule');
    assert.ok(cls.includes('muted'), `expected muted class for unknown rule, got: ${cls}`);
  });

  it('error rules across multiple pairs all return a red class', () => {
    const errorRules = [
      'cj-prop-no-float-for-money',
      'vc-prop-no-on-error-goto',
      'jk-prop-no-lateinit-nullable',
      'php-prop-no-legacy-mysql',
      'cr-prop-no-mem-transmute',
    ];
    for (const id of errorRules) {
      const cls = getRuleSeverityClass(id);
      assert.ok(cls.includes('red'), `expected red for ${id}, got: ${cls}`);
    }
  });
});
