import { render, fireEvent, waitFor } from '@testing-library/react'
import Taro from '@tarojs/taro'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import OrderDetailPage from '@/pages/order-detail/index'
import '~/__mocks__/taroMock'

// Mock API client
jest.mock('@/api', () => ({
  orderClient: {
    ':id': {
      $get: jest.fn()
    },
    'cancel-order': {
      $post: jest.fn()
    }
  }
}))

const mockOrder = {
  id: 1,
  orderNo: 'ORDER001',
  payState: 0, // 未支付
  state: 0, // 未发货
  amount: 100,
  payAmount: 100,
  freightAmount: 0,
  discountAmount: 0,
  orderGoods: [
    {
      id: 1,
      goodsId: 1,
      goodsName: '商品1',
      price: 50,
      num: 2,
      imageFile: {
        id: 1,
        fullUrl: 'https://minio.example.com/d8dai/uploads/goods/2024/product-image.jpg'
      }
    }
  ],
  recevierName: '张三',
  receiverMobile: '13800138000',
  address: '北京市朝阳区',
  createdAt: '2025-01-01T00:00:00Z'
}

const createTestQueryClient = () => new QueryClient({
  defaultOptions: {
    queries: { retry: false },
    mutations: { retry: false }
  }
})

const TestWrapper = ({ children }: { children: React.ReactNode }) => (
  <QueryClientProvider client={createTestQueryClient()}>
    {children}
  </QueryClientProvider>
)

describe('OrderDetailPage', () => {
  beforeEach(() => {
    jest.clearAllMocks()
    ;(Taro.getCurrentInstance as jest.Mock).mockReturnValue({
      router: { params: { id: '1' } }
    })
    ;(Taro.getNetworkType as jest.Mock).mockResolvedValue({ networkType: 'wifi' })
  })

  it('should render order details when data is loaded', async () => {
    const mockGetOrder = require('@/api').orderClient[':id'].$get as jest.Mock
    mockGetOrder.mockResolvedValue({
      status: 200,
      json: () => Promise.resolve(mockOrder)
    })

    const { findByText } = render(
      <TestWrapper>
        <OrderDetailPage />
      </TestWrapper>
    )

    expect(await findByText('订单详情')).toBeTruthy()
    expect(await findByText('待付款')).toBeTruthy()
    expect(await findByText('请尽快完成支付')).toBeTruthy()
    expect(await findByText('商品1')).toBeTruthy()
    expect(await findByText('ORDER001')).toBeTruthy()
  })

  it('should show loading state when fetching order', () => {
    const mockGetOrder = require('@/api').orderClient[':id'].$get as jest.Mock
    mockGetOrder.mockImplementation(() => new Promise(() => {})) // 永不resolve

    const { container } = render(
      <TestWrapper>
        <OrderDetailPage />
      </TestWrapper>
    )

    // 检查加载状态 - 应该显示加载指示器
    const loadingSpinner = container.querySelector('.i-heroicons-arrow-path-20-solid.animate-spin')
    expect(loadingSpinner).toBeTruthy()
  })

  it('should show error when order not found', async () => {
    const mockGetOrder = require('@/api').orderClient[':id'].$get as jest.Mock
    mockGetOrder.mockRejectedValue(new Error('订单不存在'))

    const { findByText } = render(
      <TestWrapper>
        <OrderDetailPage />
      </TestWrapper>
    )

    expect(await findByText('订单不存在')).toBeTruthy()
  })

  it('should show cancel dialog when cancel button is clicked', async () => {
    const mockGetOrder = require('@/api').orderClient[':id'].$get as jest.Mock
    mockGetOrder.mockResolvedValue({
      status: 200,
      json: () => Promise.resolve(mockOrder)
    })

    const { findByText, getByText, container } = render(
      <TestWrapper>
        <OrderDetailPage />
      </TestWrapper>
    )

    await findByText('订单详情')

    fireEvent.click(getByText('取消订单'))

    await waitFor(() => {
      // 检查对话框是否通过DOM查询器找到
      const dialog = container.querySelector('[role="dialog"]')
      expect(dialog).toBeTruthy()
    })
  })

  it('should call cancel API when cancel is confirmed', async () => {
    const mockGetOrder = require('@/api').orderClient[':id'].$get as jest.Mock
    const mockCancelOrder = require('@/api').orderClient['cancel-order'].$post as jest.Mock
    const mockShowModal = Taro.showModal as jest.Mock

    mockGetOrder.mockResolvedValue({
      status: 200,
      json: () => Promise.resolve(mockOrder)
    })

    mockCancelOrder.mockResolvedValue({
      status: 200,
      json: () => Promise.resolve({ success: true, message: '取消成功' })
    })

    mockShowModal.mockResolvedValue({ confirm: true })

    const { findByText, getByText } = render(
      <TestWrapper>
        <OrderDetailPage />
      </TestWrapper>
    )

    await findByText('订单详情')

    // 打开取消对话框
    fireEvent.click(getByText('取消订单'))

    // 选择取消原因并确认
    await waitFor(() => {
      const reasonOption = getByText('我不想买了')
      fireEvent.click(reasonOption)

      const confirmButton = getByText('确认取消')
      fireEvent.click(confirmButton)
    })

    await waitFor(() => {
      expect(mockShowModal).toHaveBeenCalledWith({
        title: '确认取消',
        content: expect.stringContaining('我不想买了'),
        success: expect.any(Function)
      })
    })
  })

  it('should show success message when cancel succeeds', async () => {
    const mockGetOrder = require('@/api').orderClient[':id'].$get as jest.Mock
    const mockCancelOrder = require('@/api').orderClient['cancel-order'].$post as jest.Mock
    const mockShowToast = Taro.showToast as jest.Mock
    const mockShowModal = Taro.showModal as jest.Mock

    mockGetOrder.mockResolvedValue({
      status: 200,
      json: () => Promise.resolve(mockOrder)
    })

    mockCancelOrder.mockResolvedValue({
      status: 200,
      json: () => Promise.resolve({ success: true, message: '取消成功' })
    })

    mockShowModal.mockResolvedValue({ confirm: true })

    const { findByText, getByText } = render(
      <TestWrapper>
        <OrderDetailPage />
      </TestWrapper>
    )

    await findByText('订单详情')

    fireEvent.click(getByText('取消订单'))

    await waitFor(() => {
      const reasonOption = getByText('我不想买了')
      fireEvent.click(reasonOption)

      const confirmButton = getByText('确认取消')
      fireEvent.click(confirmButton)
    })

    await waitFor(() => {
      expect(mockShowToast).toHaveBeenCalledWith({
        title: '订单取消成功',
        icon: 'success',
        duration: 2000
      })
    })
  })

  it('should show error message when cancel fails', async () => {
    const mockGetOrder = require('@/api').orderClient[':id'].$get as jest.Mock
    const mockCancelOrder = require('@/api').orderClient['cancel-order'].$post as jest.Mock
    const mockShowToast = Taro.showToast as jest.Mock
    const mockShowModal = Taro.showModal as jest.Mock

    mockGetOrder.mockResolvedValue({
      status: 200,
      json: () => Promise.resolve(mockOrder)
    })

    mockCancelOrder.mockRejectedValue(new Error('订单状态不允许取消'))
    mockShowModal.mockResolvedValue({ confirm: true })

    const { findByText, getByText } = render(
      <TestWrapper>
        <OrderDetailPage />
      </TestWrapper>
    )

    await findByText('订单详情')

    fireEvent.click(getByText('取消订单'))

    await waitFor(() => {
      const reasonOption = getByText('我不想买了')
      fireEvent.click(reasonOption)

      const confirmButton = getByText('确认取消')
      fireEvent.click(confirmButton)
    })

    await waitFor(() => {
      expect(mockShowToast).toHaveBeenCalledWith({
        title: '当前订单状态不允许取消',
        icon: 'error',
        duration: 3000
      })
    })
  })

  it('should copy order number when copy button is clicked', async () => {
    const mockGetOrder = require('@/api').orderClient[':id'].$get as jest.Mock
    const mockSetClipboardData = Taro.setClipboardData as jest.Mock
    const mockShowToast = Taro.showToast as jest.Mock

    mockGetOrder.mockResolvedValue({
      status: 200,
      json: () => Promise.resolve(mockOrder)
    })

    mockSetClipboardData.mockResolvedValue({ success: true })

    const { findByText, getByText } = render(
      <TestWrapper>
        <OrderDetailPage />
      </TestWrapper>
    )

    await findByText('订单详情')

    const copyButton = getByText('复制')
    fireEvent.click(copyButton)

    await waitFor(() => {
      expect(mockSetClipboardData).toHaveBeenCalledWith({
        data: 'ORDER001'
      })
      expect(mockShowToast).toHaveBeenCalledWith({
        title: '订单号已复制',
        icon: 'success'
      })
    })
  })

  it('should check network before showing cancel dialog', async () => {
    const mockGetOrder = require('@/api').orderClient[':id'].$get as jest.Mock
    const mockShowToast = Taro.showToast as jest.Mock

    mockGetOrder.mockResolvedValue({
      status: 200,
      json: () => Promise.resolve(mockOrder)
    })

    // 模拟无网络
    ;(Taro.getNetworkType as jest.Mock).mockResolvedValue({ networkType: 'none' })

    const { findByText, getByText } = render(
      <TestWrapper>
        <OrderDetailPage />
      </TestWrapper>
    )

    await findByText('订单详情')

    fireEvent.click(getByText('取消订单'))

    await waitFor(() => {
      expect(mockShowToast).toHaveBeenCalledWith({
        title: '网络连接失败，请检查网络后重试',
        icon: 'error',
        duration: 3000
      })
    })
  })
})