/**
 * Performance Test for Signing Operations
 *
 * Validates that signing operations meet the <50ms performance requirement
 * across all platforms and under various load conditions.
 */

import { CredentialManager } from '@/core/credential-manager/CredentialManager'
import { UnifiedSigner } from '@/core/credential-manager/UnifiedSigner'
import { Platform } from '@/types/credential'

describe('Signing Performance Test', () => {
  let credentialManager: CredentialManager
  let unifiedSigner: UnifiedSigner

  beforeEach(async () => {
    const config = {
      accounts: [
        {
          id: 'perf-pacifica',
          name: 'Performance Test Pacifica',
          platform: Platform.PACIFICA,
          enabled: true,
          credentials: {
            type: 'ed25519' as const,
            privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
          }
        },
        {
          id: 'perf-aster',
          name: 'Performance Test Aster',
          platform: Platform.ASTER,
          enabled: true,
          credentials: {
            type: 'secp256k1' as const,
            privateKey: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
          }
        },
        {
          id: 'perf-binance',
          name: 'Performance Test Binance',
          platform: Platform.BINANCE,
          enabled: true,
          credentials: {
            type: 'hmac' as const,
            apiKey: 'test-api-key',
            secretKey: 'test-secret-key'
          }
        }
      ]
    }

    credentialManager = new CredentialManager()
    unifiedSigner = new UnifiedSigner()

    await credentialManager.loadConfiguration(config)
    unifiedSigner.setAccountGetter(async (accountId: string) => {
      return credentialManager.getAccount(accountId)
    })
  })

  afterEach(async () => {
    await credentialManager.shutdown()
  })

  describe('Single Operation Performance', () => {
    test('Pacifica signing should complete within 50ms', async () => {
      const message = new TextEncoder().encode('performance test message')

      const startTime = performance.now()
      const result = await credentialManager.sign('perf-pacifica', message)
      const duration = performance.now() - startTime

      expect(result.success).toBe(true)
      expect(duration).toBeLessThan(50)
      expect(result.metadata?.duration).toBeLessThan(50)
    })

    test('Aster signing should complete within 50ms', async () => {
      const message = new TextEncoder().encode('performance test message')

      const startTime = performance.now()
      const result = await credentialManager.sign('perf-aster', message)
      const duration = performance.now() - startTime

      expect(result.success).toBe(true)
      expect(duration).toBeLessThan(50)
      expect(result.metadata?.duration).toBeLessThan(50)
    })

    test('Binance signing should complete within 50ms', async () => {
      const message = new TextEncoder().encode('performance test message')

      const startTime = performance.now()
      const result = await credentialManager.sign('perf-binance', message)
      const duration = performance.now() - startTime

      expect(result.success).toBe(true)
      expect(duration).toBeLessThan(50)
      expect(result.metadata?.duration).toBeLessThan(50)
    })

    test('UnifiedSigner should meet performance requirements', async () => {
      const message = new TextEncoder().encode('unified signer test')
      const platforms = [
        { accountId: 'perf-pacifica', platform: Platform.PACIFICA },
        { accountId: 'perf-aster', platform: Platform.ASTER },
        { accountId: 'perf-binance', platform: Platform.BINANCE }
      ]

      for (const { accountId, platform } of platforms) {
        const startTime = performance.now()
        const result = await unifiedSigner.sign(accountId, message, {
          forcePlatform: platform,
          collectMetrics: true
        })
        const duration = performance.now() - startTime

        expect(result.success).toBe(true)
        expect(duration).toBeLessThan(50)
        expect(result.metadata?.duration).toBeLessThan(50)
      }
    })
  })

  describe('Batch Operation Performance', () => {
    test('should handle 10 concurrent signing operations within performance limits', async () => {
      const message = new TextEncoder().encode('concurrent test message')
      const operations = Array.from({ length: 10 }, (_, i) => ({
        accountId: ['perf-pacifica', 'perf-aster', 'perf-binance'][i % 3],
        message
      }))

      const startTime = performance.now()
      const results = await Promise.all(
        operations.map(op => credentialManager.sign(op.accountId, op.message))
      )
      const totalDuration = performance.now() - startTime

      expect(results).toHaveLength(10)
      expect(results.every(r => r.success)).toBe(true)

      // Total time for 10 concurrent operations should be reasonable
      expect(totalDuration).toBeLessThan(200)

      // Average time per operation
      const averageTime = totalDuration / 10
      expect(averageTime).toBeLessThan(20)

      // Each individual operation should meet requirement
      results.forEach(result => {
        expect(result.metadata?.duration).toBeLessThan(50)
      })
    })

    test('should handle batch signing with UnifiedSigner', async () => {
      const message = new TextEncoder().encode('batch test message')
      const batchRequests = [
        { accountId: 'perf-pacifica', message },
        { accountId: 'perf-aster', message },
        { accountId: 'perf-binance', message },
        { accountId: 'perf-pacifica', message },
        { accountId: 'perf-aster', message }
      ]

      const startTime = performance.now()
      const results = await unifiedSigner.signBatch(batchRequests)
      const totalDuration = performance.now() - startTime

      expect(results).toHaveLength(5)
      expect(results.every(r => r.success)).toBe(true)

      // Batch should complete efficiently
      expect(totalDuration).toBeLessThan(150)

      // Results should be in original order
      expect(results[0].accountId).toBe('perf-pacifica')
      expect(results[1].accountId).toBe('perf-aster')
      expect(results[2].accountId).toBe('perf-binance')
    })
  })

  describe('High-Load Performance', () => {
    test('should maintain performance under high load', async () => {
      const message = new TextEncoder().encode('high load test')
      const operationCount = 50

      const operations = Array.from({ length: operationCount }, (_, i) => ({
        accountId: ['perf-pacifica', 'perf-aster', 'perf-binance'][i % 3],
        message
      }))

      const startTime = performance.now()
      const results = await Promise.all(
        operations.map(op => credentialManager.sign(op.accountId, op.message))
      )
      const totalDuration = performance.now() - startTime

      expect(results).toHaveLength(operationCount)
      expect(results.every(r => r.success)).toBe(true)

      // Should handle 50 operations efficiently
      expect(totalDuration).toBeLessThan(1000) // 1 second for 50 operations

      // Calculate percentiles
      const durations = results
        .map(r => r.metadata?.duration || 0)
        .sort((a, b) => a - b)

      const p50 = durations[Math.floor(durations.length * 0.5)]
      const p95 = durations[Math.floor(durations.length * 0.95)]
      const p99 = durations[Math.floor(durations.length * 0.99)]

      expect(p50).toBeLessThan(30) // 50th percentile < 30ms
      expect(p95).toBeLessThan(50) // 95th percentile < 50ms (requirement)
      expect(p99).toBeLessThan(75) // 99th percentile < 75ms (allows for outliers)
    })

    test('should handle sustained load without performance degradation', async () => {
      const message = new TextEncoder().encode('sustained load test')
      const batchSize = 10
      const batchCount = 5
      const batchResults: number[] = []

      for (let batch = 0; batch < batchCount; batch++) {
        const operations = Array.from({ length: batchSize }, (_, i) => ({
          accountId: ['perf-pacifica', 'perf-aster', 'perf-binance'][i % 3],
          message
        }))

        const batchStartTime = performance.now()
        const results = await Promise.all(
          operations.map(op => credentialManager.sign(op.accountId, op.message))
        )
        const batchDuration = performance.now() - batchStartTime

        expect(results.every(r => r.success)).toBe(true)
        batchResults.push(batchDuration)

        // Small delay between batches
        await new Promise(resolve => setTimeout(resolve, 10))
      }

      // Performance should not degrade significantly across batches
      const firstBatch = batchResults[0]
      const lastBatch = batchResults[batchResults.length - 1]

      // Last batch should not be more than 50% slower than first batch
      expect(lastBatch).toBeLessThan(firstBatch * 1.5)

      // All batches should complete within reasonable time
      batchResults.forEach(duration => {
        expect(duration).toBeLessThan(200) // 10 operations in 200ms
      })
    })
  })

  describe('Memory and Resource Performance', () => {
    test('should not leak memory during repeated operations', async () => {
      const message = new TextEncoder().encode('memory test')
      const initialMemory = process.memoryUsage()

      // Perform many signing operations
      for (let i = 0; i < 100; i++) {
        const accountId = ['perf-pacifica', 'perf-aster', 'perf-binance'][i % 3]
        const result = await credentialManager.sign(accountId, message)
        expect(result.success).toBe(true)
      }

      // Force garbage collection if available
      if (global.gc) {
        global.gc()
        await new Promise(resolve => setTimeout(resolve, 100))
      }

      const finalMemory = process.memoryUsage()

      // Memory increase should be reasonable (less than 10MB)
      const heapIncrease = finalMemory.heapUsed - initialMemory.heapUsed
      expect(heapIncrease).toBeLessThan(10 * 1024 * 1024) // 10MB
    })

    test('should maintain consistent performance across different message sizes', async () => {
      const messageSizes = [100, 1000, 10000, 100000] // bytes
      const performanceResults: Record<number, number[]> = {}

      for (const size of messageSizes) {
        const message = new TextEncoder().encode('x'.repeat(size))
        const durations: number[] = []

        // Test each size multiple times
        for (let i = 0; i < 10; i++) {
          const startTime = performance.now()
          const result = await credentialManager.sign('perf-pacifica', message)
          const duration = performance.now() - startTime

          expect(result.success).toBe(true)
          durations.push(duration)
        }

        performanceResults[size] = durations
      }

      // All message sizes should meet performance requirements
      Object.entries(performanceResults).forEach(([size, durations]) => {
        const averageDuration = durations.reduce((a, b) => a + b, 0) / durations.length
        const maxDuration = Math.max(...durations)

        expect(averageDuration).toBeLessThan(50) // Average < 50ms
        expect(maxDuration).toBeLessThan(100) // Max < 100ms (allowing for larger messages)
      })

      // Performance should scale reasonably with message size
      const avg100 = performanceResults[100].reduce((a, b) => a + b, 0) / 10
      const avg100k = performanceResults[100000].reduce((a, b) => a + b, 0) / 10

      // 1000x larger message should not be more than 10x slower
      expect(avg100k).toBeLessThan(avg100 * 10)
    })
  })

  describe('Platform-Specific Performance', () => {
    test('Ed25519 (Pacifica) should be fastest', async () => {
      const message = new TextEncoder().encode('ed25519 performance test')
      const iterations = 20
      const durations: number[] = []

      for (let i = 0; i < iterations; i++) {
        const startTime = performance.now()
        const result = await credentialManager.sign('perf-pacifica', message)
        const duration = performance.now() - startTime

        expect(result.success).toBe(true)
        durations.push(duration)
      }

      const averageDuration = durations.reduce((a, b) => a + b, 0) / iterations
      const maxDuration = Math.max(...durations)

      // Ed25519 should be very fast
      expect(averageDuration).toBeLessThan(20)
      expect(maxDuration).toBeLessThan(50)
    })

    test('ECDSA (Aster) should meet performance requirements', async () => {
      const message = new TextEncoder().encode('ecdsa performance test')
      const iterations = 20
      const durations: number[] = []

      for (let i = 0; i < iterations; i++) {
        const startTime = performance.now()
        const result = await credentialManager.sign('perf-aster', message)
        const duration = performance.now() - startTime

        expect(result.success).toBe(true)
        durations.push(duration)
      }

      const averageDuration = durations.reduce((a, b) => a + b, 0) / iterations
      const maxDuration = Math.max(...durations)

      // ECDSA should meet requirements
      expect(averageDuration).toBeLessThan(40)
      expect(maxDuration).toBeLessThan(50)
    })

    test('HMAC (Binance) should be very fast', async () => {
      const message = new TextEncoder().encode('hmac performance test')
      const iterations = 20
      const durations: number[] = []

      for (let i = 0; i < iterations; i++) {
        const startTime = performance.now()
        const result = await credentialManager.sign('perf-binance', message)
        const duration = performance.now() - startTime

        expect(result.success).toBe(true)
        durations.push(duration)
      }

      const averageDuration = durations.reduce((a, b) => a + b, 0) / iterations
      const maxDuration = Math.max(...durations)

      // HMAC should be fastest
      expect(averageDuration).toBeLessThan(10)
      expect(maxDuration).toBeLessThan(25)
    })
  })

  describe('Performance Metrics Collection', () => {
    test('should collect accurate performance metrics', async () => {
      const message = new TextEncoder().encode('metrics test')

      // Reset metrics
      unifiedSigner.resetMetrics()

      // Perform signing operations
      await unifiedSigner.sign('perf-pacifica', message, { collectMetrics: true })
      await unifiedSigner.sign('perf-aster', message, { collectMetrics: true })
      await unifiedSigner.sign('perf-binance', message, { collectMetrics: true })

      const metrics = unifiedSigner.getMetrics()

      expect(metrics.totalOperations).toBe(3)
      expect(metrics.successfulOperations).toBe(3)
      expect(metrics.failedOperations).toBe(0)
      expect(metrics.averageSigningTime).toBeGreaterThan(0)
      expect(metrics.averageSigningTime).toBeLessThan(50)

      // Platform-specific metrics
      expect(metrics.platformBreakdown[Platform.PACIFICA].operations).toBe(1)
      expect(metrics.platformBreakdown[Platform.ASTER].operations).toBe(1)
      expect(metrics.platformBreakdown[Platform.BINANCE].operations).toBe(1)

      expect(metrics.platformBreakdown[Platform.PACIFICA].successRate).toBe(1)
      expect(metrics.platformBreakdown[Platform.ASTER].successRate).toBe(1)
      expect(metrics.platformBreakdown[Platform.BINANCE].successRate).toBe(1)
    })

    test('should track performance degradation', async () => {
      const message = new TextEncoder().encode('degradation test')

      unifiedSigner.resetMetrics()

      // Perform operations and check if performance remains consistent
      const operationCount = 50
      const checkpoints = [10, 25, 50]
      const performanceAtCheckpoints: number[] = []

      for (let i = 1; i <= operationCount; i++) {
        const accountId = ['perf-pacifica', 'perf-aster', 'perf-binance'][i % 3]
        await unifiedSigner.sign(accountId, message, { collectMetrics: true })

        if (checkpoints.includes(i)) {
          const metrics = unifiedSigner.getMetrics()
          performanceAtCheckpoints.push(metrics.averageSigningTime)
        }
      }

      // Performance should not degrade significantly
      const [perf10, perf25, perf50] = performanceAtCheckpoints

      expect(perf10).toBeLessThan(50)
      expect(perf25).toBeLessThan(50)
      expect(perf50).toBeLessThan(50)

      // Performance at 50 operations should not be more than 50% worse than at 10
      expect(perf50).toBeLessThan(perf10 * 1.5)
    })
  })
})