/**
 * Unit Tests for AccountStatus
 *
 * Tests the AccountStatus monitoring implementation in isolation.
 */

import { AccountStatus, AccountState, AccountHealthMetrics, AccountBalanceInfo } from '@/core/credential-manager/AccountStatus'
import { Platform } from '@/types/credential'

describe('AccountStatus Unit Tests', () => {
  let accountStatus: AccountStatus

  beforeEach(() => {
    accountStatus = new AccountStatus('test-account', Platform.PACIFICA, {
      alias: 'Test Account',
      description: 'Account for unit testing'
    })
  })

  describe('Initialization', () => {
    test('should initialize with correct properties', () => {
      expect(accountStatus.accountId).toBe('test-account')
      expect(accountStatus.platform).toBe(Platform.PACIFICA)
      expect(accountStatus.createdAt).toBeInstanceOf(Date)
    })

    test('should start in LOADING state', () => {
      expect(accountStatus.getState()).toBe(AccountState.LOADING)
    })

    test('should initialize with healthy metrics', () => {
      const metrics = accountStatus.getHealthMetrics()

      expect(metrics.successCount).toBe(0)
      expect(metrics.failureCount).toBe(0)
      expect(metrics.totalOperations).toBe(0)
      expect(metrics.healthScore).toBe(1.0)
      expect(metrics.consecutiveFailures).toBe(0)
    })

    test('should store metadata correctly', () => {
      const metadata = accountStatus.getMetadata()

      expect(metadata.alias).toBe('Test Account')
      expect(metadata.description).toBe('Account for unit testing')
    })

    test('should record initial state change', () => {
      const stateHistory = accountStatus.getStateHistory()

      expect(stateHistory).toHaveLength(1)
      expect(stateHistory[0].state).toBe(AccountState.LOADING)
      expect(stateHistory[0].reason).toBe('Account status initialized')
    })
  })

  describe('State Management', () => {
    test('should change state correctly', () => {
      accountStatus.setState(AccountState.ACTIVE, 'Account is ready')

      expect(accountStatus.getState()).toBe(AccountState.ACTIVE)

      const stateHistory = accountStatus.getStateHistory()
      expect(stateHistory).toHaveLength(2)
      expect(stateHistory[1].state).toBe(AccountState.ACTIVE)
      expect(stateHistory[1].reason).toBe('Account is ready')
    })

    test('should not duplicate state changes', () => {
      accountStatus.setState(AccountState.ACTIVE)
      accountStatus.setState(AccountState.ACTIVE) // Same state again

      const stateHistory = accountStatus.getStateHistory()
      expect(stateHistory).toHaveLength(2) // Should not add duplicate
    })

    test('should check if account is active', () => {
      expect(accountStatus.isActive()).toBe(false) // LOADING state

      accountStatus.setState(AccountState.ACTIVE)
      expect(accountStatus.isActive()).toBe(true)

      accountStatus.setState(AccountState.ERROR)
      expect(accountStatus.isActive()).toBe(false)
    })

    test('should check if account is available', () => {
      expect(accountStatus.isAvailable()).toBe(false) // LOADING state

      accountStatus.setState(AccountState.ACTIVE)
      expect(accountStatus.isAvailable()).toBe(true)

      accountStatus.setState(AccountState.WARNING)
      expect(accountStatus.isAvailable()).toBe(true)

      accountStatus.setState(AccountState.ERROR)
      expect(accountStatus.isAvailable()).toBe(false)
    })
  })

  describe('Health Monitoring', () => {
    test('should record successful operations', () => {
      accountStatus.recordSuccess(50) // 50ms response time

      const metrics = accountStatus.getHealthMetrics()

      expect(metrics.successCount).toBe(1)
      expect(metrics.totalOperations).toBe(1)
      expect(metrics.consecutiveFailures).toBe(0)
      expect(metrics.lastSuccess).toBeInstanceOf(Date)
      expect(metrics.averageResponseTime).toBe(50)
      expect(metrics.healthScore).toBe(1.0)
    })

    test('should record failed operations', () => {
      const error = new Error('Test failure')
      accountStatus.recordFailure(error, 100)

      const metrics = accountStatus.getHealthMetrics()

      expect(metrics.failureCount).toBe(1)
      expect(metrics.totalOperations).toBe(1)
      expect(metrics.consecutiveFailures).toBe(1)
      expect(metrics.lastFailure).toBeInstanceOf(Date)
      expect(metrics.averageResponseTime).toBe(100)
      expect(metrics.healthScore).toBeLessThan(1.0)
    })

    test('should calculate success rate correctly', () => {
      accountStatus.recordSuccess()
      accountStatus.recordSuccess()
      accountStatus.recordFailure(new Error('Test'))

      expect(accountStatus.getSuccessRate()).toBeCloseTo(2/3, 2)
      expect(accountStatus.getFailureRate()).toBeCloseTo(1/3, 2)
    })

    test('should reset consecutive failures on success', () => {
      // Record some failures
      accountStatus.recordFailure(new Error('Failure 1'))
      accountStatus.recordFailure(new Error('Failure 2'))

      expect(accountStatus.getHealthMetrics().consecutiveFailures).toBe(2)

      // Record success - should reset consecutive failures
      accountStatus.recordSuccess()

      expect(accountStatus.getHealthMetrics().consecutiveFailures).toBe(0)
    })

    test('should auto-disable after consecutive failures', () => {
      // Record 5 consecutive failures (threshold)
      for (let i = 0; i < 5; i++) {
        accountStatus.recordFailure(new Error(`Failure ${i + 1}`))
      }

      expect(accountStatus.getState()).toBe(AccountState.ERROR)
      expect(accountStatus.getHealthMetrics().consecutiveFailures).toBe(5)
    })

    test('should update average response time correctly', () => {
      accountStatus.recordSuccess(100)
      accountStatus.recordSuccess(200)

      const metrics = accountStatus.getHealthMetrics()

      // With weighted average (80% old, 20% new), expected: 100 * 0.8 + 200 * 0.2 = 120
      expect(metrics.averageResponseTime).toBeCloseTo(120, 1)
    })

    test('should update health score based on performance', () => {
      // Record mostly successful operations
      for (let i = 0; i < 9; i++) {
        accountStatus.recordSuccess()
      }
      accountStatus.recordFailure(new Error('Single failure'))

      const healthScore = accountStatus.getHealthMetrics().healthScore
      expect(healthScore).toBeGreaterThan(0.8) // Should be high with 90% success rate
    })

    test('should penalize health score for slow response times', () => {
      // Record operations with slow response time
      accountStatus.recordSuccess(2000) // 2 seconds - slow

      const healthScore = accountStatus.getHealthMetrics().healthScore
      expect(healthScore).toBeLessThan(1.0) // Should be penalized for slow response
    })

    test('should automatically update state based on health', () => {
      // Start with active state
      accountStatus.setState(AccountState.ACTIVE)

      // Record many failures to degrade health
      for (let i = 0; i < 10; i++) {
        accountStatus.recordFailure(new Error(`Failure ${i + 1}`))
      }

      // Should automatically transition to ERROR state
      expect(accountStatus.getState()).toBe(AccountState.ERROR)
    })
  })

  describe('Balance Information', () => {
    test('should update balance information', () => {
      const balanceInfo: AccountBalanceInfo = {
        totalValue: 10000,
        positions: { BTC: 0.5, ETH: 2.0 },
        lastUpdated: new Date(),
        currency: 'USD'
      }

      accountStatus.updateBalanceInfo(balanceInfo)

      const retrievedBalance = accountStatus.getBalanceInfo()
      expect(retrievedBalance).toEqual(expect.objectContaining({
        totalValue: 10000,
        positions: { BTC: 0.5, ETH: 2.0 },
        currency: 'USD'
      }))
      expect(retrievedBalance?.lastUpdated).toBeInstanceOf(Date)
    })

    test('should detect stale balance information', () => {
      const oldBalance: AccountBalanceInfo = {
        totalValue: 5000,
        positions: {},
        lastUpdated: new Date(Date.now() - 10 * 60 * 1000), // 10 minutes ago
        currency: 'USD'
      }

      accountStatus.updateBalanceInfo(oldBalance)

      expect(accountStatus.isBalanceStale(5 * 60 * 1000)).toBe(true) // 5 minute threshold
      expect(accountStatus.isBalanceStale(15 * 60 * 1000)).toBe(false) // 15 minute threshold
    })

    test('should return undefined for missing balance info', () => {
      expect(accountStatus.getBalanceInfo()).toBeUndefined()
      expect(accountStatus.isBalanceStale()).toBe(true)
    })
  })

  describe('Activity Tracking', () => {
    test('should track last activity timestamp', () => {
      const beforeActivity = new Date()
      accountStatus.recordActivity()
      const afterActivity = new Date()

      const lastActivity = accountStatus.getLastActivity()

      expect(lastActivity.getTime()).toBeGreaterThanOrEqual(beforeActivity.getTime())
      expect(lastActivity.getTime()).toBeLessThanOrEqual(afterActivity.getTime())
    })

    test('should detect idle accounts', () => {
      // Account should not be idle initially
      expect(accountStatus.isIdle(1000)).toBe(false)

      // Mock an old last activity time
      const oldTime = new Date(Date.now() - 15 * 60 * 1000) // 15 minutes ago
      accountStatus['lastActivity'] = oldTime

      expect(accountStatus.isIdle(10 * 60 * 1000)).toBe(true) // 10 minute threshold
      expect(accountStatus.isIdle(20 * 60 * 1000)).toBe(false) // 20 minute threshold
    })

    test('should update activity on success/failure records', () => {
      const beforeTime = Date.now()

      accountStatus.recordSuccess()

      const lastActivity = accountStatus.getLastActivity()
      expect(lastActivity.getTime()).toBeGreaterThan(beforeTime)
    })
  })

  describe('State History and Diagnostics', () => {
    test('should maintain state history with timestamps', () => {
      accountStatus.setState(AccountState.ACTIVE, 'Initialized')
      accountStatus.setState(AccountState.WARNING, 'High latency')
      accountStatus.setState(AccountState.ERROR, 'Connection failed')

      const history = accountStatus.getStateHistory()

      expect(history).toHaveLength(4) // Including initial LOADING state
      expect(history[1].state).toBe(AccountState.ACTIVE)
      expect(history[2].state).toBe(AccountState.WARNING)
      expect(history[3].state).toBe(AccountState.ERROR)

      history.forEach(entry => {
        expect(entry.timestamp).toBeInstanceOf(Date)
      })
    })

    test('should limit state history size', () => {
      // Add more than 50 state changes
      for (let i = 0; i < 55; i++) {
        accountStatus.setState(
          i % 2 === 0 ? AccountState.ACTIVE : AccountState.WARNING,
          `Change ${i}`
        )
      }

      const history = accountStatus.getStateHistory()
      expect(history.length).toBeLessThanOrEqual(50)
    })

    test('should provide comprehensive diagnostics', () => {
      accountStatus.setState(AccountState.ACTIVE)
      accountStatus.recordSuccess(50)
      accountStatus.recordFailure(new Error('Test failure'), 100)

      const diagnostics = accountStatus.getDiagnostics()

      expect(diagnostics.accountId).toBe('test-account')
      expect(diagnostics.platform).toBe(Platform.PACIFICA)
      expect(diagnostics.currentState).toBe(AccountState.ACTIVE)
      expect(diagnostics.totalOperations).toBe(2)
      expect(diagnostics.successRate).toBe(0.5)
      expect(diagnostics.failureRate).toBe(0.5)
      expect(diagnostics.uptime).toBeGreaterThan(0)
      expect(diagnostics.timeSinceLastActivity).toBeGreaterThanOrEqual(0)
      expect(diagnostics.isActive).toBe(true)
      expect(diagnostics.isAvailable).toBe(true)
      expect(diagnostics.balanceStale).toBe(true)
      expect(diagnostics.recentStateChanges).toHaveLength(5) // Last 5 changes
    })
  })

  describe('Metadata Management', () => {
    test('should update metadata', () => {
      accountStatus.updateMetadata({
        tags: ['production', 'primary'],
        priority: 1
      })

      const metadata = accountStatus.getMetadata()

      expect(metadata.alias).toBe('Test Account') // Original
      expect(metadata.description).toBe('Account for unit testing') // Original
      expect(metadata.tags).toEqual(['production', 'primary']) // New
      expect(metadata.priority).toBe(1) // New
    })

    test('should handle maintenance window metadata', () => {
      accountStatus.updateMetadata({
        maintenanceWindow: {
          start: '02:00',
          end: '04:00',
          timezone: 'UTC'
        }
      })

      const metadata = accountStatus.getMetadata()
      expect(metadata.maintenanceWindow).toEqual({
        start: '02:00',
        end: '04:00',
        timezone: 'UTC'
      })
    })
  })

  describe('Health Check Integration', () => {
    test('should update health from external checks', () => {
      accountStatus.setState(AccountState.ACTIVE)

      // External health check fails
      accountStatus.updateHealth(false, { lastChecked: new Date() })

      expect(accountStatus.getState()).toBe(AccountState.WARNING)

      // External health check passes
      accountStatus.updateHealth(true, { lastChecked: new Date() })

      expect(accountStatus.getState()).toBe(AccountState.ACTIVE)
    })

    test('should include health check details in metadata', () => {
      const healthDetails = {
        lastChecked: new Date(),
        apiResponse: 'OK',
        latency: 45
      }

      accountStatus.updateHealth(true, healthDetails)

      const metadata = accountStatus.getMetadata()
      expect(metadata).toEqual(expect.objectContaining(healthDetails))
    })
  })

  describe('Edge Cases and Error Handling', () => {
    test('should handle rapid state changes', () => {
      const states = [
        AccountState.ACTIVE,
        AccountState.WARNING,
        AccountState.ERROR,
        AccountState.ACTIVE,
        AccountState.WARNING
      ]

      states.forEach((state, index) => {
        accountStatus.setState(state, `Rapid change ${index}`)
      })

      expect(accountStatus.getState()).toBe(AccountState.WARNING)
      expect(accountStatus.getStateHistory().length).toBeGreaterThan(states.length)
    })

    test('should handle very large numbers of operations', () => {
      // Simulate many operations
      for (let i = 0; i < 10000; i++) {
        if (i % 3 === 0) {
          accountStatus.recordFailure(new Error('Test'), Math.random() * 100)
        } else {
          accountStatus.recordSuccess(Math.random() * 50)
        }
      }

      const metrics = accountStatus.getHealthMetrics()

      expect(metrics.totalOperations).toBe(10000)
      expect(metrics.successCount).toBeGreaterThan(6000)
      expect(metrics.failureCount).toBeGreaterThan(3000)
      expect(metrics.healthScore).toBeGreaterThan(0)
      expect(metrics.averageResponseTime).toBeGreaterThan(0)
    })

    test('should handle zero operations gracefully', () => {
      expect(accountStatus.getSuccessRate()).toBe(1) // No operations = 100% success
      expect(accountStatus.getFailureRate()).toBe(0)

      const metrics = accountStatus.getHealthMetrics()
      expect(metrics.healthScore).toBe(1.0)
    })

    test('should handle extreme response times', () => {
      // Very fast response
      accountStatus.recordSuccess(0.1)

      // Very slow response
      accountStatus.recordSuccess(60000) // 1 minute

      const metrics = accountStatus.getHealthMetrics()

      expect(metrics.averageResponseTime).toBeGreaterThan(0)
      expect(metrics.healthScore).toBeLessThan(1.0) // Should be penalized for slow response
    })

    test('should maintain consistency during concurrent updates', () => {
      // Simulate concurrent updates
      const promises = []

      for (let i = 0; i < 100; i++) {
        promises.push(Promise.resolve().then(() => {
          if (i % 2 === 0) {
            accountStatus.recordSuccess(Math.random() * 100)
          } else {
            accountStatus.recordFailure(new Error(`Concurrent error ${i}`))
          }
        }))
      }

      return Promise.all(promises).then(() => {
        const metrics = accountStatus.getHealthMetrics()
        expect(metrics.totalOperations).toBe(100)
        expect(metrics.successCount + metrics.failureCount).toBe(100)
      })
    })
  })
})