import { describe, it, expect, vi, beforeEach } from 'vitest'
import * as security from '../security'
import client from '../client'

vi.mock('../client')

describe('security API', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

  describe('getSecurityStatus', () => {
    it('should call GET /security/status', async () => {
      const mockData: security.SecurityStatus = {
        cerberus: { enabled: true },
        crowdsec: { mode: 'local', api_url: 'http://localhost:8080', enabled: true },
        waf: { mode: 'enabled', enabled: true },
        rate_limit: { mode: 'enabled', enabled: true },
        acl: { enabled: true }
      }
      vi.mocked(client.get).mockResolvedValue({ data: mockData })

      const result = await security.getSecurityStatus()

      expect(client.get).toHaveBeenCalledWith('/security/status')
      expect(result).toEqual(mockData)
    })
  })

  describe('getSecurityConfig', () => {
    it('should call GET /security/config', async () => {
      const mockData = { config: { admin_whitelist: '10.0.0.0/8' } }
      vi.mocked(client.get).mockResolvedValue({ data: mockData })

      const result = await security.getSecurityConfig()

      expect(client.get).toHaveBeenCalledWith('/security/config')
      expect(result).toEqual(mockData)
    })
  })

  describe('updateSecurityConfig', () => {
    it('should call POST /security/config with payload', async () => {
      const payload: security.SecurityConfigPayload = {
        name: 'test',
        enabled: true,
        admin_whitelist: '10.0.0.0/8'
      }
      const mockData = { success: true }
      vi.mocked(client.post).mockResolvedValue({ data: mockData })

      const result = await security.updateSecurityConfig(payload)

      expect(client.post).toHaveBeenCalledWith('/security/config', payload)
      expect(result).toEqual(mockData)
    })

    it('should handle all payload fields', async () => {
      const payload: security.SecurityConfigPayload = {
        name: 'test',
        enabled: true,
        admin_whitelist: '10.0.0.0/8',
        crowdsec_mode: 'local',
        crowdsec_api_url: 'http://localhost:8080',
        waf_mode: 'enabled',
        waf_rules_source: 'coreruleset',
        waf_learning: true,
        rate_limit_enable: true,
        rate_limit_burst: 10,
        rate_limit_requests: 100,
        rate_limit_window_sec: 60
      }
      const mockData = { success: true }
      vi.mocked(client.post).mockResolvedValue({ data: mockData })

      const result = await security.updateSecurityConfig(payload)

      expect(client.post).toHaveBeenCalledWith('/security/config', payload)
      expect(result).toEqual(mockData)
    })
  })

  describe('generateBreakGlassToken', () => {
    it('should call POST /security/breakglass/generate', async () => {
      const mockData = { token: 'abc123' }
      vi.mocked(client.post).mockResolvedValue({ data: mockData })

      const result = await security.generateBreakGlassToken()

      expect(client.post).toHaveBeenCalledWith('/security/breakglass/generate')
      expect(result).toEqual(mockData)
    })
  })

  describe('enableCerberus', () => {
    it('should call POST /security/enable with payload', async () => {
      const payload = { mode: 'full' }
      const mockData = { success: true }
      vi.mocked(client.post).mockResolvedValue({ data: mockData })

      const result = await security.enableCerberus(payload)

      expect(client.post).toHaveBeenCalledWith('/security/enable', payload)
      expect(result).toEqual(mockData)
    })

    it('should call POST /security/enable with empty object when no payload', async () => {
      const mockData = { success: true }
      vi.mocked(client.post).mockResolvedValue({ data: mockData })

      const result = await security.enableCerberus()

      expect(client.post).toHaveBeenCalledWith('/security/enable', {})
      expect(result).toEqual(mockData)
    })
  })

  describe('disableCerberus', () => {
    it('should call POST /security/disable with payload', async () => {
      const payload = { reason: 'maintenance' }
      const mockData = { success: true }
      vi.mocked(client.post).mockResolvedValue({ data: mockData })

      const result = await security.disableCerberus(payload)

      expect(client.post).toHaveBeenCalledWith('/security/disable', payload)
      expect(result).toEqual(mockData)
    })

    it('should call POST /security/disable with empty object when no payload', async () => {
      const mockData = { success: true }
      vi.mocked(client.post).mockResolvedValue({ data: mockData })

      const result = await security.disableCerberus()

      expect(client.post).toHaveBeenCalledWith('/security/disable', {})
      expect(result).toEqual(mockData)
    })
  })

  describe('getDecisions', () => {
    it('should call GET /security/decisions with default limit', async () => {
      const mockData = { decisions: [] }
      vi.mocked(client.get).mockResolvedValue({ data: mockData })

      const result = await security.getDecisions()

      expect(client.get).toHaveBeenCalledWith('/security/decisions?limit=50')
      expect(result).toEqual(mockData)
    })

    it('should call GET /security/decisions with custom limit', async () => {
      const mockData = { decisions: [] }
      vi.mocked(client.get).mockResolvedValue({ data: mockData })

      const result = await security.getDecisions(100)

      expect(client.get).toHaveBeenCalledWith('/security/decisions?limit=100')
      expect(result).toEqual(mockData)
    })
  })

  describe('createDecision', () => {
    it('should call POST /security/decisions with payload', async () => {
      const payload = { value: '1.2.3.4', duration: '4h', type: 'ban' }
      const mockData = { success: true }
      vi.mocked(client.post).mockResolvedValue({ data: mockData })

      const result = await security.createDecision(payload)

      expect(client.post).toHaveBeenCalledWith('/security/decisions', payload)
      expect(result).toEqual(mockData)
    })
  })

  describe('getRuleSets', () => {
    it('should call GET /security/rulesets', async () => {
      const mockData: security.RuleSetsResponse = {
        rulesets: [
          {
            id: 1,
            uuid: 'abc-123',
            name: 'OWASP CRS',
            source_url: 'https://example.com/rules',
            mode: 'blocking',
            last_updated: '2025-12-04T00:00:00Z',
            content: 'rule content'
          }
        ]
      }
      vi.mocked(client.get).mockResolvedValue({ data: mockData })

      const result = await security.getRuleSets()

      expect(client.get).toHaveBeenCalledWith('/security/rulesets')
      expect(result).toEqual(mockData)
    })
  })

  describe('upsertRuleSet', () => {
    it('should call POST /security/rulesets with create payload', async () => {
      const payload: security.UpsertRuleSetPayload = {
        name: 'Custom Rules',
        content: 'rule content',
        mode: 'blocking'
      }
      const mockData = { success: true }
      vi.mocked(client.post).mockResolvedValue({ data: mockData })

      const result = await security.upsertRuleSet(payload)

      expect(client.post).toHaveBeenCalledWith('/security/rulesets', payload)
      expect(result).toEqual(mockData)
    })

    it('should call POST /security/rulesets with update payload', async () => {
      const payload: security.UpsertRuleSetPayload = {
        id: 1,
        name: 'Updated Rules',
        source_url: 'https://example.com/rules',
        mode: 'detection'
      }
      const mockData = { success: true }
      vi.mocked(client.post).mockResolvedValue({ data: mockData })

      const result = await security.upsertRuleSet(payload)

      expect(client.post).toHaveBeenCalledWith('/security/rulesets', payload)
      expect(result).toEqual(mockData)
    })
  })

  describe('deleteRuleSet', () => {
    it('should call DELETE /security/rulesets/:id', async () => {
      const mockData = { success: true }
      vi.mocked(client.delete).mockResolvedValue({ data: mockData })

      const result = await security.deleteRuleSet(1)

      expect(client.delete).toHaveBeenCalledWith('/security/rulesets/1')
      expect(result).toEqual(mockData)
    })
  })
})
