import { describe, it, expect, beforeEach } from 'vitest'
import { ATLASMapper } from '../../../src/compliance/ATLASMapper.js'

describe('ATLASMapper', () => {
  let mapper: ATLASMapper

  beforeEach(() => {
    mapper = new ATLASMapper()
  })

  describe('mapRule()', () => {
    it('should map prompt-injection rule to AML.T0051', () => {
      const mapping = mapper.mapRule('rule:prompt-injection')
      expect(mapping).toBeDefined()
      expect(mapping!.techniqueId).toBe('AML.T0051')
      expect(mapping!.techniqueName).toContain('Prompt Injection')
      expect(mapping!.tacticName).toBe('Initial Access')
    })

    it('should map indirect prompt injection to AML.T0051.001', () => {
      const mapping = mapper.mapRule('rule:prompt-injection-indirect')
      expect(mapping).toBeDefined()
      expect(mapping!.techniqueId).toBe('AML.T0051.001')
    })

    it('should map jailbreak rule to AML.T0054', () => {
      const mapping = mapper.mapRule('rule:jailbreak')
      expect(mapping).toBeDefined()
      expect(mapping!.techniqueId).toBe('AML.T0054')
      expect(mapping!.relatedKillChainPhase).toBe('privilege_escalation')
    })

    it('should map model-extraction rule to AML.T0024', () => {
      const mapping = mapper.mapRule('rule:model-extraction')
      expect(mapping).toBeDefined()
      expect(mapping!.techniqueId).toBe('AML.T0024')
    })

    it('should map system-prompt-extraction rule', () => {
      const mapping = mapper.mapRule('rule:system-prompt-extraction')
      expect(mapping).toBeDefined()
      expect(mapping!.techniqueId).toBe('AML.T0051.002')
      expect(mapping!.relatedKillChainPhase).toBe('reconnaissance')
    })

    it('should map data-poisoning rule to AML.T0020', () => {
      const mapping = mapper.mapRule('rule:data-poisoning')
      expect(mapping).toBeDefined()
      expect(mapping!.techniqueId).toBe('AML.T0020')
      expect(mapping!.relatedKillChainPhase).toBe('persistence')
    })

    it('should map supply-chain rule to AML.T0010', () => {
      const mapping = mapper.mapRule('rule:supply-chain')
      expect(mapping).toBeDefined()
      expect(mapping!.techniqueId).toBe('AML.T0010')
    })

    it('should map encoding-evasion rule to AML.T0015', () => {
      const mapping = mapper.mapRule('rule:encoding-evasion')
      expect(mapping).toBeDefined()
      expect(mapping!.techniqueId).toBe('AML.T0015')
    })

    it('should map tool-abuse rule to AML.T0040', () => {
      const mapping = mapper.mapRule('rule:tool-abuse')
      expect(mapping).toBeDefined()
      expect(mapping!.techniqueId).toBe('AML.T0040')
      expect(mapping!.relatedKillChainPhase).toBe('command_and_control')
    })

    it('should map data-exfiltration rule to AML.T0025', () => {
      const mapping = mapper.mapRule('rule:data-exfiltration')
      expect(mapping).toBeDefined()
      expect(mapping!.techniqueId).toBe('AML.T0025')
      expect(mapping!.relatedKillChainPhase).toBe('actions_on_objective')
    })

    it('should return undefined for unknown rule', () => {
      const mapping = mapper.mapRule('rule:nonexistent')
      expect(mapping).toBeUndefined()
    })

    it('should include mitigation IDs in mappings', () => {
      const mapping = mapper.mapRule('rule:prompt-injection')!
      expect(mapping.mitigationIds.length).toBeGreaterThan(0)
      expect(mapping.mitigationIds[0]).toMatch(/^AML\.M\d+/)
    })
  })

  describe('getMappedRules()', () => {
    it('should return all mapped rule IDs', () => {
      const rules = mapper.getMappedRules()
      expect(rules.length).toBeGreaterThan(0)
      expect(rules).toContain('rule:prompt-injection')
      expect(rules).toContain('rule:jailbreak')
      expect(rules).toContain('rule:data-exfiltration')
    })

    it('should return frozen array', () => {
      const rules = mapper.getMappedRules()
      expect(Object.isFrozen(rules)).toBe(true)
    })
  })

  describe('getCoverage()', () => {
    it('should return coverage statistics', () => {
      const coverage = mapper.getCoverage()
      expect(coverage.covered).toBeGreaterThan(0)
      expect(coverage.total).toBe(29)
      expect(coverage.covered).toBeLessThanOrEqual(coverage.total)
    })

    it('should identify coverage gaps', () => {
      const coverage = mapper.getCoverage()
      expect(coverage.gaps.length).toBeGreaterThan(0)
      // Gaps should be technique IDs not covered
      for (const gap of coverage.gaps) {
        expect(gap).toMatch(/^AML\.T(A)?\d+/)
      }
    })

    it('should have covered + gaps = total known techniques', () => {
      const coverage = mapper.getCoverage()
      // All techniques are either covered or in gaps
      const allKnownCount = coverage.covered + coverage.gaps.length
      // May not equal total exactly since some rules map to same technique
      expect(allKnownCount).toBeLessThanOrEqual(coverage.total)
    })

    it('should return frozen result', () => {
      const coverage = mapper.getCoverage()
      expect(Object.isFrozen(coverage)).toBe(true)
      expect(Object.isFrozen(coverage.gaps)).toBe(true)
    })
  })

  describe('getAllMappings()', () => {
    it('should return all ATLAS mappings', () => {
      const mappings = mapper.getAllMappings()
      expect(mappings.length).toBeGreaterThan(0)
    })

    it('should include required fields in each mapping', () => {
      const mappings = mapper.getAllMappings()
      for (const mapping of mappings) {
        expect(mapping.techniqueId).toBeTruthy()
        expect(mapping.tacticId).toBeTruthy()
        expect(mapping.techniqueName).toBeTruthy()
        expect(mapping.tacticName).toBeTruthy()
        expect(mapping.description).toBeTruthy()
        expect(mapping.relatedKillChainPhase).toBeTruthy()
        expect(Array.isArray(mapping.mitigationIds)).toBe(true)
        expect(Array.isArray(mapping.caseStudyIds)).toBe(true)
      }
    })

    it('should return frozen array', () => {
      const mappings = mapper.getAllMappings()
      expect(Object.isFrozen(mappings)).toBe(true)
    })
  })
})
