import { describe, it, expect, beforeEach, afterEach } from '@jest/globals'
import { DeltaNeutralController } from '../../src/controllers/DeltaNeutralController.js'
import { AccountManager } from '../../src/modules/account/AccountManager.js'
import { HedgeExecutionService } from '../../src/services/HedgeExecutionService.js'
import { RiskMonitoringService } from '../../src/services/RiskMonitoringService.js'

/**
 * DeltaNeutralController 单元测试
 * 测试Delta中性控制器的核心功能
 */
describe('DeltaNeutralController Unit Tests', () => {
  let controller: DeltaNeutralController
  let mockAccountManager: jest.Mocked<AccountManager>
  let mockHedgeService: jest.Mocked<HedgeExecutionService>
  let mockRiskService: jest.Mocked<RiskMonitoringService>

  beforeEach(() => {
    // 创建模拟依赖
    mockAccountManager = {
      getAccounts: jest.fn().mockReturnValue(new Map([
        ['account-1', { netPosition: 0.5, unrealizedPnl: 100 }],
        ['account-2', { netPosition: -0.3, unrealizedPnl: -50 }]
      ])),
      getAccountState: jest.fn(),
      updateAccountState: jest.fn(),
    } as any

    mockHedgeService = {
      executeHedge: jest.fn().mockResolvedValue({
        success: true,
        executedOrders: [
          { accountId: 'account-1', symbol: 'BTC-USD', amount: 0.2, side: 'sell' }
        ],
        totalCost: 1000,
        executionTime: 150,
        netDeltaChange: -0.2
      }),
      getStatus: jest.fn().mockReturnValue('running'),
    } as any

    mockRiskService = {
      getRiskMetrics: jest.fn().mockResolvedValue({
        accountId: 'test-account',
        deltaDeviation: 0.5,
        utilizationRate: 0.7,
        leverageRatio: 2,
        unrealizedPnl: 100,
        maxDrawdown: 200,
        timestamp: Date.now()
      }),
      updateAccountRisk: jest.fn(),
      getActiveAlerts: jest.fn().mockReturnValue([]),
      getStatus: jest.fn().mockReturnValue('running'),
    } as any

    controller = new DeltaNeutralController(
      mockAccountManager,
      mockHedgeService,
      mockRiskService
    )
  })

  afterEach(async () => {
    await controller.stop()
  })

  describe('Service Lifecycle', () => {
    it('should initialize with stopped status', () => {
      expect(controller.getStatus()).toBe('stopped')
    })

    it('should start successfully', async () => {
      await controller.start()
      expect(controller.getStatus()).toBe('running')
    })

    it('should stop successfully', async () => {
      await controller.start()
      await controller.stop()
      expect(controller.getStatus()).toBe('stopped')
    })

    it('should handle multiple start calls gracefully', async () => {
      await controller.start()
      await controller.start() // 第二次调用应该被忽略
      expect(controller.getStatus()).toBe('running')
    })
  })

  describe('Delta Control Execution', () => {
    beforeEach(async () => {
      await controller.start()
    })

    it('should execute delta control with valid request', async () => {
      const request = {
        targetDelta: 0.0,
        accounts: ['account-1', 'account-2'],
        maxDeviation: 0.1,
        symbol: 'BTC-USD'
      }

      const response = await controller.executeDeltaControl(request)

      expect(response.success).toBe(true)
      expect(response.accountsProcessed).toEqual(['account-1', 'account-2'])
      expect(response.totalAdjustments).toBeGreaterThan(0)
      expect(response.finalDelta).toBeDefined()
      expect(mockHedgeService.executeHedge).toHaveBeenCalled()
    })

    it('should calculate net delta correctly', async () => {
      const request = {
        targetDelta: 0.0,
        accounts: ['account-1', 'account-2'],
        maxDeviation: 0.1,
        symbol: 'BTC-USD'
      }

      const response = await controller.executeDeltaControl(request)

      // 净Delta = 0.5 + (-0.3) = 0.2
      expect(response.initialDelta).toBe(0.2)
      expect(response.deltaDeviation).toBe(0.2) // 偏离目标0.0
    })

    it('should skip adjustment when delta is within tolerance', async () => {
      // 设置小的净仓位偏差
      mockAccountManager.getAccounts.mockReturnValue(new Map([
        ['account-1', { netPosition: 0.05, unrealizedPnl: 10 }],
        ['account-2', { netPosition: -0.03, unrealizedPnl: -5 }]
      ]))

      const request = {
        targetDelta: 0.0,
        accounts: ['account-1', 'account-2'],
        maxDeviation: 0.1, // 10% 容差
        symbol: 'BTC-USD'
      }

      const response = await controller.executeDeltaControl(request)

      expect(response.success).toBe(true)
      expect(response.adjustmentNeeded).toBe(false)
      expect(response.reason).toContain('在容差范围内')
      expect(mockHedgeService.executeHedge).not.toHaveBeenCalled()
    })

    it('should handle hedge execution failure gracefully', async () => {
      mockHedgeService.executeHedge.mockResolvedValue({
        success: false,
        error: 'Insufficient balance',
        executedOrders: [],
        totalCost: 0,
        executionTime: 0,
        netDeltaChange: 0
      })

      const request = {
        targetDelta: 0.0,
        accounts: ['account-1', 'account-2'],
        maxDeviation: 0.1,
        symbol: 'BTC-USD'
      }

      const response = await controller.executeDeltaControl(request)

      expect(response.success).toBe(false)
      expect(response.error).toContain('对冲执行失败')
    })
  })

  describe('Utilization Rebalancing', () => {
    beforeEach(async () => {
      await controller.start()
    })

    it('should execute utilization rebalancing successfully', async () => {
      const request = {
        targetUtilization: 0.75, // 75% 目标利用率
        accounts: ['account-1', 'account-2'],
        symbol: 'BTC-USD'
      }

      const response = await controller.executeUtilizationRebalance(request)

      expect(response.success).toBe(true)
      expect(response.accountsProcessed).toEqual(['account-1', 'account-2'])
      expect(response.rebalanceActions).toBeDefined()
    })

    it('should calculate utilization rates correctly', async () => {
      // 模拟风险服务返回特定利用率
      mockRiskService.getRiskMetrics
        .mockResolvedValueOnce({
          accountId: 'account-1',
          deltaDeviation: 0.5,
          utilizationRate: 0.85, // 85% 高利用率
          leverageRatio: 2,
          unrealizedPnl: 100,
          maxDrawdown: 200,
          timestamp: Date.now()
        })
        .mockResolvedValueOnce({
          accountId: 'account-2',
          deltaDeviation: 0.3,
          utilizationRate: 0.65, // 65% 低利用率
          leverageRatio: 1,
          unrealizedPnl: -50,
          maxDrawdown: 100,
          timestamp: Date.now()
        })

      const request = {
        targetUtilization: 0.75,
        accounts: ['account-1', 'account-2'],
        symbol: 'BTC-USD'
      }

      const response = await controller.executeUtilizationRebalance(request)

      expect(response.utilizationMetrics).toHaveLength(2)
      expect(response.utilizationMetrics[0].currentUtilization).toBe(0.85)
      expect(response.utilizationMetrics[1].currentUtilization).toBe(0.65)
      expect(response.averageUtilization).toBe(0.75) // (0.85 + 0.65) / 2
    })

    it('should identify accounts needing rebalancing', async () => {
      mockRiskService.getRiskMetrics
        .mockResolvedValueOnce({
          accountId: 'account-1',
          deltaDeviation: 0.5,
          utilizationRate: 0.95, // 需要降低
          leverageRatio: 3,
          unrealizedPnl: 200,
          maxDrawdown: 300,
          timestamp: Date.now()
        })
        .mockResolvedValueOnce({
          accountId: 'account-2',
          deltaDeviation: 0.2,
          utilizationRate: 0.45, // 需要提高
          leverageRatio: 1,
          unrealizedPnl: 50,
          maxDrawdown: 50,
          timestamp: Date.now()
        })

      const request = {
        targetUtilization: 0.75,
        accounts: ['account-1', 'account-2'],
        symbol: 'BTC-USD',
        tolerancePercent: 0.1 // 10% 容差
      }

      const response = await controller.executeUtilizationRebalance(request)

      expect(response.rebalanceActions).toHaveLength(2)

      const account1Action = response.rebalanceActions.find(a => a.accountId === 'account-1')
      const account2Action = response.rebalanceActions.find(a => a.accountId === 'account-2')

      expect(account1Action?.action).toBe('reduce') // 降低利用率
      expect(account2Action?.action).toBe('increase') // 提高利用率
    })

    it('should skip rebalancing when utilization is optimal', async () => {
      mockRiskService.getRiskMetrics.mockResolvedValue({
        accountId: 'test-account',
        deltaDeviation: 0.1,
        utilizationRate: 0.75, // 正好是目标利用率
        leverageRatio: 2,
        unrealizedPnl: 100,
        maxDrawdown: 150,
        timestamp: Date.now()
      })

      const request = {
        targetUtilization: 0.75,
        accounts: ['account-1', 'account-2'],
        symbol: 'BTC-USD',
        tolerancePercent: 0.05 // 5% 容差
      }

      const response = await controller.executeUtilizationRebalance(request)

      expect(response.success).toBe(true)
      expect(response.rebalanceNeeded).toBe(false)
      expect(response.reason).toContain('在目标范围内')
    })
  })

  describe('Risk Integration', () => {
    beforeEach(async () => {
      await controller.start()
    })

    it('should check risk metrics before executing operations', async () => {
      const request = {
        targetDelta: 0.0,
        accounts: ['account-1'],
        maxDeviation: 0.1,
        symbol: 'BTC-USD'
      }

      await controller.executeDeltaControl(request)

      expect(mockRiskService.getRiskMetrics).toHaveBeenCalledWith('account-1')
    })

    it('should abort operations when risk alerts are present', async () => {
      // 模拟活跃的风险警报
      mockRiskService.getActiveAlerts.mockReturnValue([
        {
          id: 'alert-1',
          type: 'delta-alert',
          severity: 'CRITICAL',
          accountId: 'account-1',
          message: '严重Delta偏差',
          timestamp: Date.now(),
          data: { deltaDeviation: 5.0 }
        }
      ])

      const request = {
        targetDelta: 0.0,
        accounts: ['account-1'],
        maxDeviation: 0.1,
        symbol: 'BTC-USD'
      }

      const response = await controller.executeDeltaControl(request)

      expect(response.success).toBe(false)
      expect(response.error).toContain('存在CRITICAL级别风险警报')
      expect(mockHedgeService.executeHedge).not.toHaveBeenCalled()
    })

    it('should update risk monitoring after successful operations', async () => {
      const request = {
        targetDelta: 0.0,
        accounts: ['account-1', 'account-2'],
        maxDeviation: 0.1,
        symbol: 'BTC-USD'
      }

      await controller.executeDeltaControl(request)

      expect(mockRiskService.updateAccountRisk).toHaveBeenCalled()
    })
  })

  describe('Error Handling and Edge Cases', () => {
    beforeEach(async () => {
      await controller.start()
    })

    it('should handle empty account list', async () => {
      const request = {
        targetDelta: 0.0,
        accounts: [],
        maxDeviation: 0.1,
        symbol: 'BTC-USD'
      }

      const response = await controller.executeDeltaControl(request)

      expect(response.success).toBe(false)
      expect(response.error).toContain('账户列表为空')
    })

    it('should handle invalid target delta values', async () => {
      const request = {
        targetDelta: NaN,
        accounts: ['account-1'],
        maxDeviation: 0.1,
        symbol: 'BTC-USD'
      }

      const response = await controller.executeDeltaControl(request)

      expect(response.success).toBe(false)
      expect(response.error).toContain('无效的目标Delta值')
    })

    it('should handle account manager failures', async () => {
      mockAccountManager.getAccounts.mockImplementation(() => {
        throw new Error('AccountManager connection failed')
      })

      const request = {
        targetDelta: 0.0,
        accounts: ['account-1'],
        maxDeviation: 0.1,
        symbol: 'BTC-USD'
      }

      const response = await controller.executeDeltaControl(request)

      expect(response.success).toBe(false)
      expect(response.error).toContain('获取账户状态失败')
    })

    it('should handle risk service unavailability', async () => {
      mockRiskService.getRiskMetrics.mockRejectedValue(new Error('Risk service unavailable'))

      const request = {
        targetUtilization: 0.75,
        accounts: ['account-1'],
        symbol: 'BTC-USD'
      }

      const response = await controller.executeUtilizationRebalance(request)

      expect(response.success).toBe(false)
      expect(response.error).toContain('风险评估失败')
    })
  })

  describe('Performance and Monitoring', () => {
    beforeEach(async () => {
      await controller.start()
    })

    it('should track operation execution time', async () => {
      const request = {
        targetDelta: 0.0,
        accounts: ['account-1', 'account-2'],
        maxDeviation: 0.1,
        symbol: 'BTC-USD'
      }

      const response = await controller.executeDeltaControl(request)

      expect(response.executionTime).toBeGreaterThan(0)
      expect(response.executionTime).toBeLessThan(10000) // 应该在10秒内完成
    })

    it('should provide detailed operation metrics', async () => {
      const request = {
        targetDelta: 0.0,
        accounts: ['account-1', 'account-2'],
        maxDeviation: 0.1,
        symbol: 'BTC-USD'
      }

      const response = await controller.executeDeltaControl(request)

      expect(response.operationId).toBeDefined()
      expect(response.timestamp).toBeDefined()
      expect(response.accountsProcessed).toBeDefined()
      expect(response.totalAdjustments).toBeDefined()
    })

    it('should handle concurrent operation requests', async () => {
      const request1 = {
        targetDelta: 0.0,
        accounts: ['account-1'],
        maxDeviation: 0.1,
        symbol: 'BTC-USD'
      }

      const request2 = {
        targetUtilization: 0.75,
        accounts: ['account-2'],
        symbol: 'BTC-USD'
      }

      const [response1, response2] = await Promise.all([
        controller.executeDeltaControl(request1),
        controller.executeUtilizationRebalance(request2)
      ])

      expect(response1.success).toBe(true)
      expect(response2.success).toBe(true)
      expect(response1.operationId).not.toBe(response2.operationId)
    })
  })
})