import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import {
  normalizeTier,
  getPlanCapabilities,
  getLanguageComplexityMultiplier,
  isLegacyLanguage,
} from './plan-access.js';

describe('normalizeTier', () => {
  it('returns each valid tier as-is', () => {
    assert.equal(normalizeTier('starter'), 'starter');
    assert.equal(normalizeTier('professional'), 'professional');
    assert.equal(normalizeTier('enterprise'), 'enterprise');
  });

  it('is case-insensitive', () => {
    assert.equal(normalizeTier('Professional'), 'professional');
    assert.equal(normalizeTier('ENTERPRISE'), 'enterprise');
    assert.equal(normalizeTier('Starter'), 'starter');
    assert.equal(normalizeTier('PROFESSIONAL'), 'professional');
  });

  it('falls back to starter for unknown values', () => {
    assert.equal(normalizeTier('unknown'), 'starter');
    assert.equal(normalizeTier('free'), 'starter');
    assert.equal(normalizeTier('pro'), 'starter');
  });

  it('falls back to starter for nullish input', () => {
    assert.equal(normalizeTier(undefined), 'starter');
    assert.equal(normalizeTier(null), 'starter');
    assert.equal(normalizeTier(''), 'starter');
  });
});

describe('getLanguageComplexityMultiplier', () => {
  it('returns 1 for modern languages', () => {
    assert.equal(getLanguageComplexityMultiplier('java'), 1);
    assert.equal(getLanguageComplexityMultiplier('python'), 1);
    assert.equal(getLanguageComplexityMultiplier('typescript'), 1);
    assert.equal(getLanguageComplexityMultiplier('go'), 1);
    assert.equal(getLanguageComplexityMultiplier('rust'), 1);
    assert.equal(getLanguageComplexityMultiplier('kotlin'), 1);
    assert.equal(getLanguageComplexityMultiplier('csharp'), 1);
  });

  it('is case-insensitive for modern languages', () => {
    assert.equal(getLanguageComplexityMultiplier('JAVA'), 1);
    assert.equal(getLanguageComplexityMultiplier('Python'), 1);
    assert.equal(getLanguageComplexityMultiplier('TypeScript'), 1);
  });

  it('returns 3 for mainframe/JCL languages', () => {
    assert.equal(getLanguageComplexityMultiplier('jcl'), 3);
    assert.equal(getLanguageComplexityMultiplier('assembler'), 3);
    assert.equal(getLanguageComplexityMultiplier('assembly'), 3);
    assert.equal(getLanguageComplexityMultiplier('cics'), 3);
    assert.equal(getLanguageComplexityMultiplier('mumps'), 3);
    assert.equal(getLanguageComplexityMultiplier('assembler-zos'), 3);
  });

  it('is case-insensitive for mainframe languages', () => {
    assert.equal(getLanguageComplexityMultiplier('JCL'), 3);
    assert.equal(getLanguageComplexityMultiplier('ASSEMBLER'), 3);
    assert.equal(getLanguageComplexityMultiplier('CICS'), 3);
  });

  it('returns 2.5 for other legacy languages (fallback)', () => {
    assert.equal(getLanguageComplexityMultiplier('cobol'), 2.5);
    assert.equal(getLanguageComplexityMultiplier('fortran'), 2.5);
    assert.equal(getLanguageComplexityMultiplier('rpg'), 2.5);
    assert.equal(getLanguageComplexityMultiplier('natural'), 2.5);
    assert.equal(getLanguageComplexityMultiplier('unknown-lang'), 2.5);
  });

  it('returns 1 for nullish or empty input', () => {
    assert.equal(getLanguageComplexityMultiplier(null), 1);
    assert.equal(getLanguageComplexityMultiplier(undefined), 1);
    assert.equal(getLanguageComplexityMultiplier(''), 1);
  });

  it('normalizes whitespace in multi-word language names', () => {
    assert.equal(getLanguageComplexityMultiplier('assembler zos'), 3);
    assert.equal(getLanguageComplexityMultiplier('  java  '), 1);
  });
});

describe('isLegacyLanguage', () => {
  it('returns false for modern languages', () => {
    assert.equal(isLegacyLanguage('java'), false);
    assert.equal(isLegacyLanguage('python'), false);
    assert.equal(isLegacyLanguage('typescript'), false);
    assert.equal(isLegacyLanguage('go'), false);
    assert.equal(isLegacyLanguage('rust'), false);
    assert.equal(isLegacyLanguage('kotlin'), false);
  });

  it('returns true for known legacy languages', () => {
    assert.equal(isLegacyLanguage('cobol'), true);
    assert.equal(isLegacyLanguage('jcl'), true);
    assert.equal(isLegacyLanguage('assembler'), true);
    assert.equal(isLegacyLanguage('fortran'), true);
    assert.equal(isLegacyLanguage('rpg'), true);
  });

  it('is case-insensitive', () => {
    assert.equal(isLegacyLanguage('COBOL'), true);
    assert.equal(isLegacyLanguage('Java'), false);
    assert.equal(isLegacyLanguage('JCL'), true);
    assert.equal(isLegacyLanguage('PYTHON'), false);
  });

  it('handles whitespace around the language name', () => {
    assert.equal(isLegacyLanguage('  cobol  '), true);
    assert.equal(isLegacyLanguage('  java  '), false);
  });

  it('treats unknown languages as legacy', () => {
    assert.equal(isLegacyLanguage('made-up-lang'), true);
    assert.equal(isLegacyLanguage('xpl'), true);
  });

  it('returns false for nullish input', () => {
    assert.equal(isLegacyLanguage(null), false);
    assert.equal(isLegacyLanguage(undefined), false);
    assert.equal(isLegacyLanguage(''), false);
  });
});

describe('getPlanCapabilities', () => {
  it('returns starter capabilities for unknown or nullish tier', () => {
    for (const input of [undefined, null, '', 'unknown', 'free'] as const) {
      const caps = getPlanCapabilities(input);
      assert.equal(caps.tier, 'starter', `expected starter for input ${JSON.stringify(input)}`);
    }
  });

  it('starter tier has correct capabilities', () => {
    const caps = getPlanCapabilities('starter');
    assert.equal(caps.tier, 'starter');
    assert.equal(caps.supportSla, '48h');
    assert.equal(caps.includedCreditsTokens, 5_000_000);
    assert.equal(caps.extraConversionPriceEur, 5_000);
    assert.equal(caps.hasTokenDashboard, true);
    assert.equal(caps.hasQualityReport, false);
    assert.equal(caps.hasSso, false);
    assert.equal(caps.hasSoc2Iso27001, false);
    assert.equal(caps.hasEuAiAct, false);
    assert.equal(caps.hasGranularRbac, false);
    assert.equal(caps.hasOnPrem, false);
    assert.equal(caps.hasDedicatedChannel, false);
    assert.equal(caps.hasAccountManager, false);
    assert.deepEqual(caps.complianceOptions, ['gdpr']);
  });

  it('professional tier has correct capabilities', () => {
    const caps = getPlanCapabilities('professional');
    assert.equal(caps.tier, 'professional');
    assert.equal(caps.supportSla, '24h');
    assert.equal(caps.includedCreditsTokens, 10_000_000);
    assert.equal(caps.extraConversionPriceEur, 4_000);
    assert.equal(caps.hasTokenDashboard, true);
    assert.equal(caps.hasQualityReport, true);
    assert.equal(caps.hasSso, true);
    assert.equal(caps.hasSoc2Iso27001, true);
    assert.equal(caps.hasEuAiAct, true);
    assert.equal(caps.hasGranularRbac, false);
    assert.equal(caps.hasOnPrem, false);
    assert.equal(caps.hasDedicatedChannel, false);
    assert.equal(caps.hasAccountManager, false);
    assert.ok(caps.complianceOptions.includes('hipaa'));
    assert.ok(caps.complianceOptions.includes('gdpr'));
  });

  it('enterprise tier has correct capabilities', () => {
    const caps = getPlanCapabilities('enterprise');
    assert.equal(caps.tier, 'enterprise');
    assert.equal(caps.supportSla, '8h');
    assert.equal(caps.includedCreditsTokens, 15_000_000);
    assert.equal(caps.extraConversionPriceEur, 2_500);
    assert.equal(caps.hasTokenDashboard, true);
    assert.equal(caps.hasQualityReport, true);
    assert.equal(caps.hasSso, true);
    assert.equal(caps.hasSoc2Iso27001, true);
    assert.equal(caps.hasEuAiAct, true);
    assert.equal(caps.hasGranularRbac, true);
    assert.equal(caps.hasOnPrem, true);
    assert.equal(caps.hasDedicatedChannel, true);
    assert.equal(caps.hasAccountManager, true);
  });

  it('is case-insensitive (delegates to normalizeTier)', () => {
    assert.equal(getPlanCapabilities('Professional').tier, 'professional');
    assert.equal(getPlanCapabilities('ENTERPRISE').tier, 'enterprise');
  });

  it('included tokens increase with tier', () => {
    const starter = getPlanCapabilities('starter');
    const pro = getPlanCapabilities('professional');
    const ent = getPlanCapabilities('enterprise');
    assert.ok(pro.includedCreditsTokens > starter.includedCreditsTokens);
    assert.ok(ent.includedCreditsTokens > pro.includedCreditsTokens);
  });

  it('extra conversion price decreases with tier', () => {
    const starter = getPlanCapabilities('starter');
    const pro = getPlanCapabilities('professional');
    const ent = getPlanCapabilities('enterprise');
    assert.ok(pro.extraConversionPriceEur < starter.extraConversionPriceEur);
    assert.ok(ent.extraConversionPriceEur < pro.extraConversionPriceEur);
  });

  it('returns the same object reference for repeated calls (no mutation risk)', () => {
    const a = getPlanCapabilities('enterprise');
    const b = getPlanCapabilities('enterprise');
    assert.strictEqual(a, b);
  });

  it('professional complianceOptions includes all expected standards', () => {
    const caps = getPlanCapabilities('professional');
    for (const std of ['gdpr', 'sox', 'pci', 'hipaa', 'iso27001']) {
      assert.ok(caps.complianceOptions.includes(std), `missing ${std} in professional`);
    }
  });

  it('enterprise complianceOptions includes all expected standards', () => {
    const caps = getPlanCapabilities('enterprise');
    for (const std of ['gdpr', 'sox', 'pci', 'hipaa', 'iso27001']) {
      assert.ok(caps.complianceOptions.includes(std), `missing ${std} in enterprise`);
    }
  });

  it('starter only has gdpr in complianceOptions', () => {
    const caps = getPlanCapabilities('starter');
    assert.deepEqual(caps.complianceOptions, ['gdpr']);
  });

  it('complianceOptions is an array for every tier', () => {
    for (const tier of ['starter', 'professional', 'enterprise'] as const) {
      assert.ok(Array.isArray(getPlanCapabilities(tier).complianceOptions));
    }
  });
});

// ── getLanguageComplexityMultiplier — extended coverage ───────────────────────

describe('getLanguageComplexityMultiplier — extended languages', () => {
  it('returns 1 for c# (alias for csharp)', () => {
    assert.equal(getLanguageComplexityMultiplier('c#'), 1);
  });

  it('returns 1 for c++ and cpp', () => {
    assert.equal(getLanguageComplexityMultiplier('c++'), 1);
    assert.equal(getLanguageComplexityMultiplier('cpp'), 1);
  });

  it('returns 1 for swift, dart, scala, elixir, r, lua, perl, matlab, ruby, vbnet', () => {
    const modern = ['swift', 'dart', 'scala', 'elixir', 'r', 'lua', 'perl', 'matlab', 'ruby', 'vbnet'];
    for (const lang of modern) {
      assert.equal(getLanguageComplexityMultiplier(lang), 1, `expected 1 for ${lang}`);
    }
  });

  it('returns 3 for bms, cl400, and m (mainframe)', () => {
    for (const lang of ['bms', 'cl400', 'm']) {
      assert.equal(getLanguageComplexityMultiplier(lang), 3, `expected 3 for ${lang}`);
    }
  });

  it('returns 2.5 for pli, natural, ada (legacy non-mainframe)', () => {
    for (const lang of ['pli', 'natural', 'ada']) {
      assert.equal(getLanguageComplexityMultiplier(lang), 2.5, `expected 2.5 for ${lang}`);
    }
  });
});

// ── isLegacyLanguage — extended coverage ─────────────────────────────────────

describe('isLegacyLanguage — extended cases', () => {
  it('returns false for dart, swift, scala, r, perl, ruby, lua, elixir, cpp, c++', () => {
    const modern = ['dart', 'swift', 'scala', 'r', 'perl', 'ruby', 'lua', 'elixir', 'cpp', 'c++'];
    for (const lang of modern) {
      assert.equal(isLegacyLanguage(lang), false, `expected false for ${lang}`);
    }
  });

  it('returns true for pli, natural, ada (legacy not in MODERN set)', () => {
    for (const lang of ['pli', 'natural', 'ada']) {
      assert.equal(isLegacyLanguage(lang), true, `expected true for ${lang}`);
    }
  });
});
