import { describe, it, expect } from 'vitest'

import { CROWDSEC_PRESETS, findCrowdsecPreset, type CrowdsecPreset } from '../crowdsecPresets'

describe('crowdsecPresets', () => {
  describe('CROWDSEC_PRESETS', () => {
    it('should contain all expected presets', () => {
      expect(CROWDSEC_PRESETS).toHaveLength(3)
      expect(CROWDSEC_PRESETS.map((p) => p.slug)).toEqual([
        'bot-mitigation-essentials',
        'honeypot-friendly-defaults',
        'geolocation-aware',
      ])
    })

    it('should have valid YAML content for each preset', () => {
      for (const preset of CROWDSEC_PRESETS) {
        expect(preset.content).toContain('configs:')
        expect(preset.content).toMatch(/collections:|parsers:|scenarios:|postoverflows:/)
      }
    })

    it('should have required metadata fields', () => {
      for (const preset of CROWDSEC_PRESETS) {
        expect(preset).toHaveProperty('slug')
        expect(preset).toHaveProperty('title')
        expect(preset).toHaveProperty('description')
        expect(preset).toHaveProperty('content')
        expect(preset.slug).toMatch(/^[a-z0-9-]+$/) // Slug format validation
      }
    })

    it('should have descriptive titles and descriptions', () => {
      for (const preset of CROWDSEC_PRESETS) {
        expect(preset.title.length).toBeGreaterThan(5)
        expect(preset.description.length).toBeGreaterThan(10)
      }
    })

    it('should have tags for each preset', () => {
      for (const preset of CROWDSEC_PRESETS) {
        expect(preset.tags).toBeDefined()
        expect(Array.isArray(preset.tags)).toBe(true)
        expect(preset.tags!.length).toBeGreaterThan(0)
      }
    })

    it('should have warnings for production-critical presets', () => {
      const botMitigation = CROWDSEC_PRESETS.find((p) => p.slug === 'bot-mitigation-essentials')
      expect(botMitigation?.warning).toBeDefined()
      expect(botMitigation?.warning).toContain('allowlist')

      const honeypot = CROWDSEC_PRESETS.find((p) => p.slug === 'honeypot-friendly-defaults')
      expect(honeypot?.warning).toBeDefined()
      expect(honeypot?.warning).toContain('honeypot')

      const geo = CROWDSEC_PRESETS.find((p) => p.slug === 'geolocation-aware')
      expect(geo?.warning).toBeDefined()
      expect(geo?.warning).toContain('GeoIP')
    })
  })

  describe('preset content integrity', () => {
    it('should have valid CrowdSec YAML structure', () => {
      for (const preset of CROWDSEC_PRESETS) {
        const lines = preset.content.split('\n')
        expect(lines[0]).toMatch(/^configs:/)
      }
    })

    it('should reference valid CrowdSec hub items', () => {
      for (const preset of CROWDSEC_PRESETS) {
        // Extract collection references
        const collections = preset.content.match(/- crowdsecurity\/[\w-]+/g) || []
        for (const item of collections) {
          // Hub items can contain underscores (e.g., http-crawl-non_statics)
          expect(item).toMatch(/^- crowdsecurity\/[a-z0-9-_]+$/)
        }
      }
    })

    it('should have proper YAML indentation', () => {
      for (const preset of CROWDSEC_PRESETS) {
        const lines = preset.content.split('\n')
        // Check that collection/parser/scenario items are indented with spaces
        const itemLines = lines.filter((line) => line.trim().startsWith('- crowdsecurity/'))
        for (const line of itemLines) {
          expect(line).toMatch(/^\s{4,}- crowdsecurity\//)
        }
      }
    })

    it('should reference known CrowdSec collections', () => {
      const botMitigation = CROWDSEC_PRESETS.find((p) => p.slug === 'bot-mitigation-essentials')
      expect(botMitigation?.content).toContain('crowdsecurity/base-http-scenarios')
      expect(botMitigation?.content).toContain('crowdsecurity/http-cve')
    })

    it('should reference known CrowdSec parsers', () => {
      const botMitigation = CROWDSEC_PRESETS.find((p) => p.slug === 'bot-mitigation-essentials')
      expect(botMitigation?.content).toContain('crowdsecurity/http-logs')
      expect(botMitigation?.content).toContain('crowdsecurity/nginx-logs')
    })

    it('should reference known CrowdSec scenarios', () => {
      const botMitigation = CROWDSEC_PRESETS.find((p) => p.slug === 'bot-mitigation-essentials')
      expect(botMitigation?.content).toContain('crowdsecurity/http-bf')
      expect(botMitigation?.content).toContain('crowdsecurity/http-probing')
    })

    it('should have whitelists postoverflow for production presets', () => {
      const botMitigation = CROWDSEC_PRESETS.find((p) => p.slug === 'bot-mitigation-essentials')
      expect(botMitigation?.content).toContain('postoverflows:')
      expect(botMitigation?.content).toContain('crowdsecurity/whitelists')
    })
  })

  describe('findCrowdsecPreset', () => {
    it('should find preset by slug', () => {
      const preset = findCrowdsecPreset('bot-mitigation-essentials')
      expect(preset).toBeDefined()
      expect(preset?.slug).toBe('bot-mitigation-essentials')
      expect(preset?.title).toBe('Bot Mitigation Essentials')
    })

    it('should find honeypot preset', () => {
      const preset = findCrowdsecPreset('honeypot-friendly-defaults')
      expect(preset).toBeDefined()
      expect(preset?.slug).toBe('honeypot-friendly-defaults')
      expect(preset?.tags).toContain('low-noise')
    })

    it('should find geolocation preset', () => {
      const preset = findCrowdsecPreset('geolocation-aware')
      expect(preset).toBeDefined()
      expect(preset?.slug).toBe('geolocation-aware')
      expect(preset?.tags).toContain('geo')
    })

    it('should return undefined for non-existent slug', () => {
      const preset = findCrowdsecPreset('non-existent-preset')
      expect(preset).toBeUndefined()
    })

    it('should be case-sensitive', () => {
      const preset = findCrowdsecPreset('BOT-MITIGATION-ESSENTIALS')
      expect(preset).toBeUndefined()
    })

    it('should not match partial slugs', () => {
      const preset = findCrowdsecPreset('bot-mitigation')
      expect(preset).toBeUndefined()
    })

    it('should handle empty string', () => {
      const preset = findCrowdsecPreset('')
      expect(preset).toBeUndefined()
    })

    it('should handle slugs with special characters', () => {
      const preset = findCrowdsecPreset('bot/mitigation')
      expect(preset).toBeUndefined()
    })
  })

  describe('preset-specific validations', () => {
    it('bot-mitigation-essentials should target web threats', () => {
      const preset = findCrowdsecPreset('bot-mitigation-essentials')
      expect(preset?.tags).toContain('bots')
      expect(preset?.tags).toContain('web')
      expect(preset?.tags).toContain('auth')
      expect(preset?.description).toContain('credential stuffing')
      expect(preset?.description).toContain('scanners')
    })

    it('honeypot-friendly-defaults should be low-noise', () => {
      const preset = findCrowdsecPreset('honeypot-friendly-defaults')
      expect(preset?.tags).toContain('low-noise')
      expect(preset?.tags).toContain('ssh')
      expect(preset?.description).toContain('honeypot')
      expect(preset?.content).toContain('crowdsecurity/sshd')
    })

    it('geolocation-aware should require GeoIP', () => {
      const preset = findCrowdsecPreset('geolocation-aware')
      expect(preset?.tags).toContain('geo')
      expect(preset?.tags).toContain('access-control')
      expect(preset?.warning).toContain('GeoIP database')
      expect(preset?.content).toContain('geoip-enricher')
    })
  })

  describe('preset tag consistency', () => {
    it('should have consistent tag naming (lowercase, hyphenated)', () => {
      for (const preset of CROWDSEC_PRESETS) {
        for (const tag of preset.tags ?? []) {
          expect(tag).toMatch(/^[a-z0-9-]+$/)
        }
      }
    })

    it('should have descriptive tags', () => {
      const allTags = CROWDSEC_PRESETS.flatMap((p) => p.tags || [])
      expect(allTags.length).toBeGreaterThan(5)
      expect(new Set(allTags).size).toBeGreaterThan(3) // Multiple unique tags
    })
  })

  describe('slug format validation', () => {
    it('should use lowercase slugs', () => {
      for (const preset of CROWDSEC_PRESETS) {
        expect(preset.slug).toBe(preset.slug.toLowerCase())
      }
    })

    it('should use hyphens as separators', () => {
      for (const preset of CROWDSEC_PRESETS) {
        expect(preset.slug).not.toContain('_')
        expect(preset.slug).not.toContain(' ')
      }
    })

    it('should not have leading or trailing hyphens', () => {
      for (const preset of CROWDSEC_PRESETS) {
        expect(preset.slug).not.toMatch(/^-/)
        expect(preset.slug).not.toMatch(/-$/)
      }
    })

    it('should not have consecutive hyphens', () => {
      for (const preset of CROWDSEC_PRESETS) {
        expect(preset.slug).not.toContain('--')
      }
    })
  })

  describe('content safety', () => {
    it('should not contain executable code', () => {
      for (const preset of CROWDSEC_PRESETS) {
        expect(preset.content).not.toContain('<script')
        expect(preset.content).not.toContain('eval(')
        expect(preset.content).not.toContain('exec(')
      }
    })

    it('should not contain SQL injection attempts', () => {
      for (const preset of CROWDSEC_PRESETS) {
        expect(preset.content).not.toContain('DROP TABLE')
        expect(preset.content).not.toContain('DELETE FROM')
      }
    })

    it('should not contain path traversal attempts', () => {
      for (const preset of CROWDSEC_PRESETS) {
        expect(preset.content).not.toContain('../')
        expect(preset.content).not.toContain('..\\')
      }
    })
  })

  describe('TypeScript type safety', () => {
    it('should satisfy CrowdsecPreset interface', () => {
      for (const preset of CROWDSEC_PRESETS) {
        const typedPreset: CrowdsecPreset = preset
        expect(typedPreset.slug).toBeTruthy()
        expect(typedPreset.title).toBeTruthy()
        expect(typedPreset.description).toBeTruthy()
        expect(typedPreset.content).toBeTruthy()
      }
    })

    it('should have optional tags and warning properties', () => {
      for (const preset of CROWDSEC_PRESETS) {
        expect(preset.tags === undefined || Array.isArray(preset.tags)).toBe(true)
        expect(preset.warning === undefined || typeof preset.warning === 'string').toBe(true)
      }
    })
  })

  describe('usability validations', () => {
    it('should have human-readable titles', () => {
      for (const preset of CROWDSEC_PRESETS) {
        expect(preset.title).not.toMatch(/^[a-z-]+$/) // Not just slug format
        expect(preset.title).toMatch(/^[A-Z]/) // Starts with capital
      }
    })

    it('should have actionable descriptions', () => {
      for (const preset of CROWDSEC_PRESETS) {
        expect(preset.description.split(' ').length).toBeGreaterThan(5)
      }
    })

    it('should have clear warnings when present', () => {
      for (const preset of CROWDSEC_PRESETS) {
        for (const warning of preset.warning ? [preset.warning] : []) {
          expect(warning.length).toBeGreaterThan(10)
          expect(warning).toMatch(/[.!]$/) // Ends with punctuation
        }
      }
    })
  })
})
