/**
 * Tests for Rule Metadata Registry (PRO-82)
 */

import { RULE_REGISTRY, getRuleMetadata, getAllCategories, hasMetadata, type RuleMetadata } from '../metadata'
import type { VulnerabilityCategory } from '../../types'

// All vulnerability categories that should have metadata
const ALL_CATEGORIES: VulnerabilityCategory[] = [
  'hardcoded_secret',
  'high_entropy_string',
  'sensitive_variable',
  'security_bypass',
  'dangerous_function',
  'sql_injection',
  'xss',
  'command_injection',
  'insecure_config',
  'missing_auth',
  'suspicious_package',
  'cors_misconfiguration',
  'root_container',
  'dangerous_file',
  'ai_pattern',
  'sensitive_url',
  'weak_crypto',
  'data_exposure',
  'ai_prompt_injection',
  'ai_unsafe_execution',
  'ai_overpermissive_tool',
  'ai_rag_exfiltration',
  'ai_endpoint_unprotected',
  'ai_schema_mismatch',
  // AI Detection Roadmap Phase 1
  'ai_package_hallucination',
  'ai_rag_corpus_poisoning',
  'ai_rag_pii_leakage',
  'ai_mcp_tool_poisoning',
  'ai_mcp_credential_issue',
  'ai_mcp_confused_deputy',
  // Phase 1 Enhancement Backlog
  'ai_mcp_description_injection',
  'ai_mcp_server_shadowing',
  'ai_mcp_config_secrets',
  'ai_mcp_config_permissions',
  'ai_rag_query_injection',
  'ai_rag_embedding_poisoning',
  'ai_rag_chunk_injection',
  'ai_package_typosquat',
  'ai_package_malicious',
  // AI Detection Roadmap Phase 2
  'ai_unsafe_model_load',
  'ai_unverified_model',
  'ai_unsafe_finetuning',
  'ai_excessive_agency',
  // Agent skill file security
  'ai_skill_injection',
  // OWASP Workstream 1
  'missing_security_headers',
  'ssrf',
  'log_injection',
  'xxe',
]

describe('Rule Metadata Registry', () => {
  describe('RULE_REGISTRY coverage', () => {
    it('should have metadata for all vulnerability categories', () => {
      for (const category of ALL_CATEGORIES) {
        expect(RULE_REGISTRY[category]).toBeDefined()
        expect(RULE_REGISTRY[category].name).toBeTruthy()
      }
    })

    it('should have all required fields for each category', () => {
      for (const category of ALL_CATEGORIES) {
        const metadata = RULE_REGISTRY[category]

        expect(metadata.name).toBeDefined()
        expect(typeof metadata.name).toBe('string')
        expect(metadata.name.length).toBeGreaterThan(0)

        expect(metadata.whyItMatters).toBeDefined()
        expect(typeof metadata.whyItMatters).toBe('string')
        expect(metadata.whyItMatters.length).toBeGreaterThan(20)

        expect(metadata.fixSteps).toBeDefined()
        expect(Array.isArray(metadata.fixSteps)).toBe(true)
        expect(metadata.fixSteps.length).toBeGreaterThan(0)

        expect(metadata.evidence).toBeDefined()
        expect(typeof metadata.evidence).toBe('string')
        expect(metadata.evidence.length).toBeGreaterThan(0)

        expect(metadata.references).toBeDefined()
        expect(Array.isArray(metadata.references)).toBe(true)
        expect(metadata.references.length).toBeGreaterThan(0)
      }
    })

    it('should have valid OWASP/CWE reference URLs', () => {
      for (const category of ALL_CATEGORIES) {
        const metadata = RULE_REGISTRY[category]

        for (const ref of metadata.references) {
          expect(ref).toMatch(/^https?:\/\//)
          // Should be OWASP, CWE, or other recognized security references
          expect(
            ref.includes('owasp.org') ||
            ref.includes('cwe.mitre.org') ||
            ref.includes('docker.com') ||
            ref.includes('genai.owasp.org') ||
            ref.includes('arxiv.org') ||  // Academic research
            ref.includes('modelcontextprotocol.io') ||  // MCP official docs
            ref.includes('invariantlabs.ai') ||  // Security research
            ref.includes('snyk.io') ||  // Dependency security
            ref.includes('osv.dev') ||  // Open Source Vulnerabilities
            ref.includes('socket.dev') ||  // npm malware research
            ref.includes('huggingface.co') ||  // ML model security docs
            ref.includes('atlas.mitre.org') ||  // MITRE ATLAS (AI threat matrix)
            ref.includes('docs.crewai.com')  // CrewAI security docs
          ).toBe(true)
        }
      }
    })
  })

  describe('getRuleMetadata', () => {
    it('should return metadata for valid category', () => {
      const metadata = getRuleMetadata('sql_injection')

      expect(metadata).toBeDefined()
      expect(metadata?.name).toBe('SQL Injection')
      expect(metadata?.whyItMatters).toContain('database')
      expect(metadata?.fixSteps.length).toBeGreaterThan(0)
    })

    it('should return undefined for invalid category', () => {
      // @ts-expect-error - testing invalid input
      const metadata = getRuleMetadata('invalid_category')
      expect(metadata).toBeUndefined()
    })
  })

  describe('getAllCategories', () => {
    it('should return all categories', () => {
      const categories = getAllCategories()

      expect(categories.length).toBe(ALL_CATEGORIES.length)
      for (const category of ALL_CATEGORIES) {
        expect(categories).toContain(category)
      }
    })
  })

  describe('hasMetadata', () => {
    it('should return true for valid categories', () => {
      expect(hasMetadata('hardcoded_secret')).toBe(true)
      expect(hasMetadata('ai_prompt_injection')).toBe(true)
    })

    it('should return false for invalid categories', () => {
      // @ts-expect-error - testing invalid input
      expect(hasMetadata('invalid_category')).toBe(false)
    })
  })

  describe('metadata quality', () => {
    it('whyItMatters should explain business impact', () => {
      // Check a few key categories have meaningful impact descriptions
      const hardcodedSecret = RULE_REGISTRY.hardcoded_secret
      expect(hardcodedSecret.whyItMatters).toMatch(/unauthorized|extract|access|credential/i)

      const sqlInjection = RULE_REGISTRY.sql_injection
      expect(sqlInjection.whyItMatters).toMatch(/read|modify|delete|database/i)

      const missingAuth = RULE_REGISTRY.missing_auth
      expect(missingAuth.whyItMatters).toMatch(/access|authentication|anyone/i)
    })

    it('fixSteps should be actionable', () => {
      // Check fix steps are actionable (start with verbs)
      for (const category of ALL_CATEGORIES) {
        const metadata = RULE_REGISTRY[category]

        for (const step of metadata.fixSteps) {
          // Each step should start with an action verb (capitalized)
          expect(step[0]).toBe(step[0].toUpperCase())
          expect(step.length).toBeGreaterThan(10)
        }
      }
    })

    it('AI-specific categories should have OWASP LLM Top 10 references', () => {
      const aiCategories: VulnerabilityCategory[] = [
        'ai_prompt_injection',
        'ai_unsafe_execution',
        'ai_overpermissive_tool',
        'ai_rag_exfiltration',
        'ai_endpoint_unprotected',
        'ai_schema_mismatch',
        // AI Detection Roadmap Phase 1
        'ai_rag_corpus_poisoning',
        'ai_rag_pii_leakage',
        'ai_mcp_tool_poisoning',
        'ai_mcp_credential_issue',
        'ai_mcp_confused_deputy',
        // AI Detection Roadmap Phase 2
        'ai_unsafe_model_load',
        'ai_unverified_model',
        'ai_unsafe_finetuning',
        'ai_excessive_agency',
      ]

      for (const category of aiCategories) {
        const metadata = RULE_REGISTRY[category]
        const hasLlmReference = metadata.references.some(
          ref => ref.includes('genai.owasp.org') ||
                 ref.includes('large-language-model') ||
                 ref.includes('modelcontextprotocol.io')  // MCP has its own docs
        )
        expect(hasLlmReference).toBe(true)
      }
    })
  })
})
