/**
 * 额度恢复集成测试
 * 测试额度恢复逻辑
 */

import { render, screen, waitFor, fireEvent } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { creditBalanceClient } from '@/api'
import { mockShowToast, mockShowModal } from '~/__mocks__/taroMock'

// 由于额度恢复主要在服务端处理，这里测试前端相关的恢复逻辑
// 包括额度查询、状态显示等

// @tarojs/taro 已经在 jest.config.js 中通过 moduleNameMapper 重定向到 mock 文件
// 不需要额外 mock

// Mock API客户端
jest.mock('@/api', () => ({
  creditBalanceClient: {
    me: {
      $get: jest.fn(),
    },
    checkout: {
      $post: jest.fn(),
    },
  },
}))

// 创建测试QueryClient
const createTestQueryClient = () => new QueryClient({
  defaultOptions: {
    queries: { retry: false },
    mutations: { retry: false },
  },
})

// 测试包装器
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
  <QueryClientProvider client={createTestQueryClient()}>
    {children}
  </QueryClientProvider>
)

// 测试数据工厂
const createTestCreditBalance = (overrides = {}) => ({
  totalLimit: 1000,
  usedAmount: 200,
  availableAmount: 800,
  isEnabled: true,
  ...overrides,
})

// 简单的测试组件，模拟额度显示和恢复操作
const TestCreditBalanceComponent = () => {
  const { data: creditBalance, isLoading, error, refetch } = useQuery({
    queryKey: ['credit-balance-test'],
    queryFn: async () => {
      const response = await creditBalanceClient.me.$get({})
      if (response.status === 200) {
        return response.json()
      }
      throw new Error('查询失败')
    },
  })

  const handleCheckout = async () => {
    try {
      const response = await creditBalanceClient.checkout.$post({
        json: {
          amount: 100,
          referenceId: 'ORD123456',
          remark: '结账恢复额度',
        },
      })

      if (response.status === 200) {
        mockShowToast({ title: '额度恢复成功', icon: 'success' })
        refetch()
      } else {
        mockShowToast({ title: '额度恢复失败', icon: 'none' })
      }
    } catch (error) {
      mockShowToast({ title: '额度恢复异常', icon: 'none' })
    }
  }

  if (isLoading) {
    return <div data-testid="loading">加载中...</div>
  }

  if (error) {
    return (
      <div data-testid="error">
        <div>加载失败</div>
        <button data-testid="retry-button" onClick={() => refetch()}>重试</button>
      </div>
    )
  }

  return (
    <div data-testid="credit-balance-component">
      <div data-testid="used-amount">已用额度: ¥{creditBalance?.usedAmount.toFixed(2)}</div>
      <div data-testid="available-amount">可用额度: ¥{creditBalance?.availableAmount.toFixed(2)}</div>
      <button data-testid="checkout-button" onClick={handleCheckout}>结账恢复额度</button>
      <button data-testid="refresh-button" onClick={() => refetch()}>刷新额度</button>
    </div>
  )
}

// Mock useQuery
import { useQuery } from '@tanstack/react-query'
jest.mock('@tanstack/react-query', () => ({
  ...jest.requireActual('@tanstack/react-query'),
  useQuery: jest.fn(),
}))

describe('额度恢复集成测试', () => {
  beforeEach(() => {
    jest.clearAllMocks()
  })

  test('额度查询和显示功能', async () => {
    // Mock 额度查询返回正常数据
    const mockCreditBalance = createTestCreditBalance({ usedAmount: 200, availableAmount: 800 })
    ;(creditBalanceClient.me.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: () => Promise.resolve(mockCreditBalance),
    })

    ;(useQuery as jest.Mock).mockReturnValue({
      data: mockCreditBalance,
      isLoading: false,
      error: null,
      refetch: jest.fn(),
    })

    render(
      <TestWrapper>
        <TestCreditBalanceComponent />
      </TestWrapper>
    )

    // 验证额度信息显示
    expect(screen.getByTestId('used-amount')).toHaveTextContent('已用额度: ¥200.00')
    expect(screen.getByTestId('available-amount')).toHaveTextContent('可用额度: ¥800.00')
    expect(screen.getByTestId('checkout-button')).toBeInTheDocument()
    expect(screen.getByTestId('refresh-button')).toBeInTheDocument()
  })

  test('结账恢复额度操作', async () => {
    // Mock 额度查询返回正常数据
    const initialBalance = createTestCreditBalance({ usedAmount: 200, availableAmount: 800 })
    ;(creditBalanceClient.me.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: () => Promise.resolve(initialBalance),
    })

    // Mock 结账恢复成功
    const updatedBalance = createTestCreditBalance({ usedAmount: 100, availableAmount: 900 })
    ;(creditBalanceClient.checkout.$post as jest.Mock).mockResolvedValue({
      status: 200,
      json: () => Promise.resolve(updatedBalance),
    })

    const mockRefetch = jest.fn()
    ;(useQuery as jest.Mock).mockReturnValue({
      data: initialBalance,
      isLoading: false,
      error: null,
      refetch: mockRefetch,
    })

    render(
      <TestWrapper>
        <TestCreditBalanceComponent />
      </TestWrapper>
    )

    // 点击结账恢复按钮
    const checkoutButton = screen.getByTestId('checkout-button')
    fireEvent.click(checkoutButton)

    // 验证调用了结账恢复API
    await waitFor(() => {
      expect(creditBalanceClient.checkout.$post).toHaveBeenCalledWith({
        json: {
          amount: 100,
          referenceId: 'ORD123456',
          remark: '结账恢复额度',
        },
      })
    })

    // 验证显示成功提示
    await waitFor(() => {
      expect(mockShowToast).toHaveBeenCalledWith({
        title: '额度恢复成功',
        icon: 'success',
      })
    })

    // 验证重新查询额度
    expect(mockRefetch).toHaveBeenCalled()
  })

  test('结账恢复额度失败处理', async () => {
    // Mock 额度查询返回正常数据
    const initialBalance = createTestCreditBalance({ usedAmount: 200, availableAmount: 800 })
    ;(creditBalanceClient.me.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: () => Promise.resolve(initialBalance),
    })

    // Mock 结账恢复失败
    ;(creditBalanceClient.checkout.$post as jest.Mock).mockResolvedValue({
      status: 400,
      json: () => Promise.resolve({ message: '恢复失败' }),
    })

    const mockRefetch = jest.fn()
    ;(useQuery as jest.Mock).mockReturnValue({
      data: initialBalance,
      isLoading: false,
      error: null,
      refetch: mockRefetch,
    })

    render(
      <TestWrapper>
        <TestCreditBalanceComponent />
      </TestWrapper>
    )

    // 点击结账恢复按钮
    const checkoutButton = screen.getByTestId('checkout-button')
    fireEvent.click(checkoutButton)

    // 验证显示失败提示
    await waitFor(() => {
      expect(mockShowToast).toHaveBeenCalledWith({
        title: '额度恢复失败',
        icon: 'none',
      })
    })

    // 验证没有重新查询额度
    expect(mockRefetch).not.toHaveBeenCalled()
  })

  test('额度查询失败后的重试功能', async () => {
    // Mock 额度查询第一次失败，第二次成功
    let queryCallCount = 0
    ;(creditBalanceClient.me.$get as jest.Mock).mockImplementation(() => {
      queryCallCount++
      if (queryCallCount === 1) {
        return Promise.reject(new Error('网络错误'))
      } else {
        return Promise.resolve({
          status: 200,
          json: () => Promise.resolve(createTestCreditBalance({ usedAmount: 200 })),
        })
      }
    })

    const mockRefetch = jest.fn(() => {
      // 模拟重试逻辑
      return Promise.resolve()
    })

    // 第一次渲染：错误状态
    ;(useQuery as jest.Mock).mockReturnValue({
      data: null,
      isLoading: false,
      error: new Error('网络错误'),
      refetch: mockRefetch,
    })

    const { rerender } = render(
      <TestWrapper>
        <TestCreditBalanceComponent />
      </TestWrapper>
    )

    // 验证错误状态显示
    expect(screen.getByTestId('error')).toBeInTheDocument()
    expect(screen.getByTestId('retry-button')).toBeInTheDocument()

    // 点击重试按钮
    const retryButton = screen.getByTestId('retry-button')
    fireEvent.click(retryButton)

    // 验证调用了重试函数
    expect(mockRefetch).toHaveBeenCalled()

    // 模拟重试成功后的状态
    ;(useQuery as jest.Mock).mockReturnValue({
      data: createTestCreditBalance({ usedAmount: 200, availableAmount: 800 }),
      isLoading: false,
      error: null,
      refetch: mockRefetch,
    })

    // 重新渲染
    rerender(
      <TestWrapper>
        <TestCreditBalanceComponent />
      </TestWrapper>
    )

    // 验证正常显示额度信息
    expect(screen.getByTestId('used-amount')).toHaveTextContent('已用额度: ¥200.00')
    expect(screen.getByTestId('available-amount')).toHaveTextContent('可用额度: ¥800.00')
  })

  test('额度恢复的幂等性验证（前端角度）', async () => {
    // Mock 额度查询返回正常数据
    const initialBalance = createTestCreditBalance({ usedAmount: 200, availableAmount: 800 })
    ;(creditBalanceClient.me.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: () => Promise.resolve(initialBalance),
    })

    // Mock 结账恢复API，多次调用返回相同结果
    const mockCheckoutResponse = {
      status: 200,
      json: () => Promise.resolve(createTestCreditBalance({ usedAmount: 100, availableAmount: 900 })),
    }
    ;(creditBalanceClient.checkout.$post as jest.Mock).mockResolvedValue(mockCheckoutResponse)

    const mockRefetch = jest.fn()
    ;(useQuery as jest.Mock).mockReturnValue({
      data: initialBalance,
      isLoading: false,
      error: null,
      refetch: mockRefetch,
    })

    render(
      <TestWrapper>
        <TestCreditBalanceComponent />
      </TestWrapper>
    )

    // 连续点击多次结账恢复按钮
    const checkoutButton = screen.getByTestId('checkout-button')
    fireEvent.click(checkoutButton)
    fireEvent.click(checkoutButton)
    fireEvent.click(checkoutButton)

    // 验证API被调用了3次
    await waitFor(() => {
      expect(creditBalanceClient.checkout.$post).toHaveBeenCalledTimes(3)
    })

    // 验证每次调用参数相同
    expect(creditBalanceClient.checkout.$post).toHaveBeenNthCalledWith(1, {
      json: {
        amount: 100,
        referenceId: 'ORD123456',
        remark: '结账恢复额度',
      },
    })
    expect(creditBalanceClient.checkout.$post).toHaveBeenNthCalledWith(2, {
      json: {
        amount: 100,
        referenceId: 'ORD123456',
        remark: '结账恢复额度',
      },
    })
    expect(creditBalanceClient.checkout.$post).toHaveBeenNthCalledWith(3, {
      json: {
        amount: 100,
        referenceId: 'ORD123456',
        remark: '结账恢复额度',
      },
    })

    // 验证重新查询额度被调用了3次
    expect(mockRefetch).toHaveBeenCalledTimes(3)
  })

  test('额度数据刷新功能', async () => {
    // Mock 额度查询
    const initialBalance = createTestCreditBalance({ usedAmount: 200, availableAmount: 800 })
    ;(creditBalanceClient.me.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: () => Promise.resolve(initialBalance),
    })

    const mockRefetch = jest.fn()
    ;(useQuery as jest.Mock).mockReturnValue({
      data: initialBalance,
      isLoading: false,
      error: null,
      refetch: mockRefetch,
    })

    render(
      <TestWrapper>
        <TestCreditBalanceComponent />
      </TestWrapper>
    )

    // 点击刷新按钮
    const refreshButton = screen.getByTestId('refresh-button')
    fireEvent.click(refreshButton)

    // 验证调用了刷新函数
    expect(mockRefetch).toHaveBeenCalled()
  })

  test('额度查询加载状态', () => {
    // Mock 加载状态
    ;(useQuery as jest.Mock).mockReturnValue({
      data: null,
      isLoading: true,
      error: null,
      refetch: jest.fn(),
    })

    render(
      <TestWrapper>
        <TestCreditBalanceComponent />
      </TestWrapper>
    )

    // 验证加载状态显示
    expect(screen.getByTestId('loading')).toHaveTextContent('加载中...')
  })

  test('额度恢复后的状态更新', async () => {
    // 模拟额度恢复前后的状态变化
    const beforeRestore = createTestCreditBalance({ usedAmount: 200, availableAmount: 800 })
    const afterRestore = createTestCreditBalance({ usedAmount: 100, availableAmount: 900 })

    let currentData = beforeRestore
    const mockRefetch = jest.fn(() => {
      currentData = afterRestore
      return Promise.resolve()
    })

    // 第一次渲染：恢复前状态
    ;(useQuery as jest.Mock).mockReturnValue({
      data: currentData,
      isLoading: false,
      error: null,
      refetch: mockRefetch,
    })

    const { rerender } = render(
      <TestWrapper>
        <TestCreditBalanceComponent />
      </TestWrapper>
    )

    // 验证恢复前状态
    expect(screen.getByTestId('used-amount')).toHaveTextContent('已用额度: ¥200.00')
    expect(screen.getByTestId('available-amount')).toHaveTextContent('可用额度: ¥800.00')

    // Mock 结账恢复API
    ;(creditBalanceClient.checkout.$post as jest.Mock).mockResolvedValue({
      status: 200,
      json: () => Promise.resolve(afterRestore),
    })

    // 点击结账恢复按钮
    const checkoutButton = screen.getByTestId('checkout-button')
    fireEvent.click(checkoutButton)

    // 模拟恢复后的查询状态
    ;(useQuery as jest.Mock).mockReturnValue({
      data: afterRestore,
      isLoading: false,
      error: null,
      refetch: mockRefetch,
    })

    // 重新渲染
    rerender(
      <TestWrapper>
        <TestCreditBalanceComponent />
      </TestWrapper>
    )

    // 验证恢复后状态
    expect(screen.getByTestId('used-amount')).toHaveTextContent('已用额度: ¥100.00')
    expect(screen.getByTestId('available-amount')).toHaveTextContent('可用额度: ¥900.00')
  })
})