/**
 * 支付工具函数单元测试
 */

import {
  validatePaymentParams,
  validatePaymentSecurity,
  formatPaymentAmount,
  checkPaymentEnvironment,
  PaymentStateManager,
  PaymentStatus,
  retryPayment,
  PaymentRateLimiter,
  validateAmountConsistency,
  generatePaymentParamsHash,
  verifyPaymentParamsIntegrity
} from '@/utils/payment'

// Mock Taro.requestPayment
const mockRequestPayment = jest.fn()
jest.mock('@tarojs/taro', () => ({
  default: {
    requestPayment: mockRequestPayment
  }
}))

describe('Payment Utils', () => {
  beforeEach(() => {
    mockRequestPayment.mockClear()
    // 清除状态管理器实例
    const stateManager = PaymentStateManager.getInstance()
    const allStates = stateManager.getAllPaymentStates()
    allStates.forEach((_, orderId) => {
      stateManager.clearPaymentState(orderId)
    })
  })

  describe('validatePaymentParams', () => {
    it('should validate correct payment parameters', () => {
      const validParams = {
        timeStamp: '1234567890',
        nonceStr: 'abcdefghijklmnopqrstuvwxyz',
        package: 'prepay_id=wx1234567890',
        signType: 'RSA',
        paySign: 'abcdefghijklmnopqrstuvwxyz1234567890'
      }

      const result = validatePaymentParams(validParams)
      expect(result.valid).toBe(true)
      expect(result.errors).toHaveLength(0)
    })

    it('should detect missing parameters', () => {
      const invalidParams = {
        timeStamp: '',
        nonceStr: '',
        package: '',
        signType: '',
        paySign: ''
      }

      const result = validatePaymentParams(invalidParams)
      expect(result.valid).toBe(false)
      expect(result.errors).toHaveLength(5)
    })
  })

  describe('validatePaymentSecurity', () => {
    it('should validate secure payment parameters', () => {
      const validParams = {
        timeStamp: Math.floor(Date.now() / 1000).toString(),
        nonceStr: 'abcdefghijklmnopqrstuvwxyz',
        package: 'prepay_id=wx1234567890',
        signType: 'RSA',
        paySign: 'abcdefghijklmnopqrstuvwxyz1234567890'
      }

      const result = validatePaymentSecurity(123, 100, validParams)
      expect(result.valid).toBe(true)
    })

    it('should detect expired timestamp', () => {
      const expiredParams = {
        timeStamp: '1000000000', // 很旧的时间戳
        nonceStr: 'abcdefghijklmnopqrstuvwxyz',
        package: 'prepay_id=wx1234567890',
        signType: 'RSA',
        paySign: 'abcdefghijklmnopqrstuvwxyz1234567890'
      }

      const result = validatePaymentSecurity(123, 100, expiredParams)
      expect(result.valid).toBe(false)
      expect(result.reason).toContain('支付参数已过期')
    })

    it('should detect invalid sign type', () => {
      const invalidParams = {
        timeStamp: Math.floor(Date.now() / 1000).toString(),
        nonceStr: 'abcdefghijklmnopqrstuvwxyz',
        package: 'prepay_id=wx1234567890',
        signType: 'INVALID',
        paySign: 'abcdefghijklmnopqrstuvwxyz1234567890'
      }

      const result = validatePaymentSecurity(123, 100, invalidParams)
      expect(result.valid).toBe(false)
      expect(result.reason).toContain('签名类型不支持')
    })
  })

  describe('formatPaymentAmount', () => {
    it('should convert yuan to fen correctly', () => {
      expect(formatPaymentAmount(100)).toBe(10000)
      expect(formatPaymentAmount(50.5)).toBe(5050)
      expect(formatPaymentAmount(0.01)).toBe(1)
    })

    it('should round fractional amounts', () => {
      expect(formatPaymentAmount(100.499)).toBe(10050) // 100.499 * 100 = 10049.9，四舍五入为10050
      expect(formatPaymentAmount(100.501)).toBe(10050)
    })
  })

  describe('PaymentStateManager', () => {
    it('should manage payment states correctly', () => {
      const stateManager = PaymentStateManager.getInstance()

      stateManager.setPaymentState(123, PaymentStatus.PROCESSING)
      expect(stateManager.getPaymentState(123)).toBe(PaymentStatus.PROCESSING)

      stateManager.setPaymentState(123, PaymentStatus.SUCCESS)
      expect(stateManager.getPaymentState(123)).toBe(PaymentStatus.SUCCESS)
    })

    it('should detect duplicate payments', () => {
      const stateManager = PaymentStateManager.getInstance()

      stateManager.setPaymentState(123, PaymentStatus.PROCESSING)
      expect(stateManager.isDuplicatePayment(123)).toBe(true)

      stateManager.setPaymentState(123, PaymentStatus.SUCCESS)
      expect(stateManager.isDuplicatePayment(123)).toBe(true)

      stateManager.setPaymentState(123, PaymentStatus.FAILED)
      expect(stateManager.isDuplicatePayment(123)).toBe(false)
    })

    it('should clear payment states', () => {
      const stateManager = PaymentStateManager.getInstance()

      stateManager.setPaymentState(123, PaymentStatus.PROCESSING)
      stateManager.clearPaymentState(123)
      expect(stateManager.getPaymentState(123)).toBeUndefined()
    })
  })

  describe('retryPayment', () => {
    it('should succeed on first attempt', async () => {
      // 直接模拟 requestWechatPayment 函数返回成功结果
      const mockRequestWechatPayment = jest.fn()
      mockRequestWechatPayment.mockResolvedValueOnce({
        success: true,
        type: 'success',
        result: {}
      })

      const paymentFn = async () => {
        return await mockRequestWechatPayment()
      }

      const result = await retryPayment(paymentFn)
      expect(result.success).toBe(true)
      expect(result.type).toBe('success')
      expect(mockRequestWechatPayment).toHaveBeenCalledTimes(1)
    })

    it('should retry on failure', async () => {
      // 直接模拟 requestWechatPayment 函数，第一次失败，第二次成功
      const mockRequestWechatPayment = jest.fn()
      mockRequestWechatPayment
        .mockResolvedValueOnce({
          success: false,
          type: 'error',
          message: '网络错误'
        })
        .mockResolvedValueOnce({
          success: true,
          type: 'success',
          result: {}
        })

      const paymentFn = async () => {
        return await mockRequestWechatPayment()
      }

      const result = await retryPayment(paymentFn, 3, 10)
      expect(result.success).toBe(true)
      expect(result.type).toBe('success')
      expect(mockRequestWechatPayment).toHaveBeenCalledTimes(2)
    })

    it('should not retry on user cancellation', async () => {
      // 直接模拟 requestWechatPayment 函数返回用户取消结果
      const mockRequestWechatPayment = jest.fn()
      mockRequestWechatPayment.mockResolvedValueOnce({
        success: false,
        type: 'cancel',
        message: '用户取消支付'
      })

      const paymentFn = async () => {
        return await mockRequestWechatPayment()
      }

      const result = await retryPayment(paymentFn)
      expect(result.success).toBe(false)
      expect(result.type).toBe('cancel')
      expect(result.message).toBe('用户取消支付')
      expect(mockRequestWechatPayment).toHaveBeenCalledTimes(1)
    })
  })

  describe('PaymentRateLimiter', () => {
    it('should allow payments within rate limit', () => {
      const rateLimiter = PaymentRateLimiter.getInstance()

      for (let i = 0; i < 4; i++) {
        rateLimiter.recordAttempt(123)
      }

      const result = rateLimiter.isRateLimited(123)
      expect(result.limited).toBe(false)
    })

    it('should block payments exceeding rate limit', () => {
      const rateLimiter = PaymentRateLimiter.getInstance()

      for (let i = 0; i < 5; i++) {
        rateLimiter.recordAttempt(123)
      }

      const result = rateLimiter.isRateLimited(123)
      expect(result.limited).toBe(true)
      expect(result.remainingTime).toBeGreaterThan(0)
    })

    it('should clear attempts', () => {
      const rateLimiter = PaymentRateLimiter.getInstance()

      rateLimiter.recordAttempt(123)
      rateLimiter.clearAttempts(123)

      const result = rateLimiter.isRateLimited(123)
      expect(result.limited).toBe(false)
    })
  })

  describe('validateAmountConsistency', () => {
    it('should validate consistent amounts', () => {
      const result = validateAmountConsistency(100, 10000)
      expect(result.valid).toBe(true)
    })

    it('should detect inconsistent amounts', () => {
      const result = validateAmountConsistency(100, 9999)
      expect(result.valid).toBe(false)
      expect(result.reason).toContain('金额不一致')
    })
  })

  describe('generatePaymentParamsHash', () => {
    it('should generate consistent hash for same parameters', () => {
      const params = {
        timeStamp: '1234567890',
        nonceStr: 'abcdefghijklmnopqrstuvwxyz',
        package: 'prepay_id=wx1234567890',
        signType: 'RSA',
        paySign: 'signature'
      }

      const hash1 = generatePaymentParamsHash(params)
      const hash2 = generatePaymentParamsHash(params)
      expect(hash1).toBe(hash2)
    })

    it('should generate different hash for different parameters', () => {
      const params1 = {
        timeStamp: '1234567890',
        nonceStr: 'abcdefghijklmnopqrstuvwxyz',
        package: 'prepay_id=wx1234567890',
        signType: 'RSA',
        paySign: 'signature'
      }

      const params2 = {
        timeStamp: '1234567891', // 不同的时间戳
        nonceStr: 'abcdefghijklmnopqrstuvwxyz',
        package: 'prepay_id=wx1234567890',
        signType: 'RSA',
        paySign: 'signature'
      }

      const hash1 = generatePaymentParamsHash(params1)
      const hash2 = generatePaymentParamsHash(params2)
      expect(hash1).not.toBe(hash2)
    })
  })

  describe('verifyPaymentParamsIntegrity', () => {
    it('should verify identical parameters', () => {
      const originalParams = {
        timeStamp: '1234567890',
        nonceStr: 'abcdefghijklmnopqrstuvwxyz',
        package: 'prepay_id=wx1234567890',
        signType: 'RSA',
        paySign: 'signature'
      }

      const receivedParams = { ...originalParams }

      const result = verifyPaymentParamsIntegrity(originalParams, receivedParams)
      expect(result.valid).toBe(true)
    })

    it('should detect tampered parameters', () => {
      const originalParams = {
        timeStamp: '1234567890',
        nonceStr: 'abcdefghijklmnopqrstuvwxyz',
        package: 'prepay_id=wx1234567890',
        signType: 'RSA',
        paySign: 'signature'
      }

      const tamperedParams = {
        ...originalParams,
        package: 'prepay_id=wx9876543210' // 被篡改的预支付ID
      }

      const result = verifyPaymentParamsIntegrity(originalParams, tamperedParams)
      expect(result.valid).toBe(false)
      expect(result.reason).toContain('支付参数被篡改')
    })
  })
})