import React from 'react'
import { render, fireEvent, waitFor } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import CartPage from '@/pages/cart/index'
import { mockShowToast, mockShowModal, mockNavigateTo, mockSetStorageSync, mockRemoveStorageSync, mockGetStorageSync, mockRequest } from '~/__mocks__/taroMock'

// Mock Taro API
jest.mock('@tarojs/taro', () => jest.requireActual('~/__mocks__/taroMock'))

// 使用真实CartContext，通过mock存储控制初始状态
import { CartProvider } from '@/contexts/CartContext'

// 购物车测试数据
const mockCartItems = [
  {
    id: 1,
    parentGoodsId: 100, // 父商品ID
    name: '红色/M', // 子商品规格名称
    price: 29.9,
    image: 'test-image1.jpg',
    stock: 10,
    quantity: 2,
  },
  {
    id: 2,
    parentGoodsId: 200, // 父商品ID
    name: '蓝色/L', // 子商品规格名称
    price: 49.9,
    image: 'test-image2.jpg',
    stock: 2, // 改为2，触发库存不足提示（<=3）
    quantity: 1,
  },
]


// mock数据
const mockGoodsData = {
  1: {
    id: 1,
    name: '红色/M', // 子商品规格名称
    price: 29.9,
    imageFile: { fullUrl: 'test-image1.jpg' },
    stock: 10,
    parent: {  // 父商品信息
      id: 100,
      name: '测试商品1', // 父商品名称（不含规格）
      price: 29.9,
      costPrice: 20,
      stock: 50,
      imageFileId: 1,
      goodsType: 'normal',
      spuId: 0
    }
  },
  2: {
    id: 2,
    name: '蓝色/L', // 子商品规格名称
    price: 49.9,
    imageFile: { fullUrl: 'test-image2.jpg' },
    stock: 2,
    parent: {  // 父商品信息
      id: 200,
      name: '测试商品2', // 父商品名称（不含规格）
      price: 49.9,
      costPrice: 35,
      stock: 30,
      imageFileId: 2,
      goodsType: 'normal',
      spuId: 0
    }
  },
  300: {
    id: 300,
    name: '单规格商品',
    price: 99.9,
    imageFile: { fullUrl: 'single.jpg' },
    stock: 10
    // 无parent字段，因为不是子商品
  }
}

// 使用getter延迟创建mockGoodsClient
let mockGoodsClient

jest.mock('@/api', () => {
  return {
    get goodsClient() {
      if (!mockGoodsClient) {
        // 第一次访问时创建mock
        mockGoodsClient = {
          ':id': {
            $get: jest.fn(({ param }: any) => {
              const goodsId = param?.id
              const idNum = Number(goodsId)
              const goodsData = mockGoodsData[idNum] || mockGoodsData[1]
              return Promise.resolve({
                status: 200,
                json: () => Promise.resolve(goodsData)
              })
            }),
            children: {
              $get: jest.fn()
            }
          }
        }
      }
      return mockGoodsClient
    }
  }
})

// Mock布局组件
jest.mock('@/layouts/tab-bar-layout', () => ({
  TabBarLayout: ({ children }: any) => <div>{children}</div>,
}))

// Mock导航栏组件
jest.mock('@/components/ui/navbar', () => ({
  Navbar: ({ title, onClickRight }: any) => (
    <div>
      <div>{title}</div>
      <button onClick={onClickRight}>清空购物车</button>
    </div>
  ),
}))

// Mock按钮组件
jest.mock('@/components/ui/button', () => ({
  Button: ({ children, onClick, disabled, className }: any) => (
    <button onClick={onClick} className={className}>
      {children}
    </button>
  ),
}))

// Mock图片组件
jest.mock('@/components/ui/image', () => ({
  Image: ({ src, className, mode }: any) => (
    <img src={src} className={className} alt="商品图片" />
  ),
}))


// 移除对useQueries的mock，使用真实hook

// 创建测试用的QueryClient
const createTestQueryClient = () => new QueryClient({
  defaultOptions: {
    queries: {
      retry: false,
      staleTime: 0, // 立即过期，强制重新获取
      gcTime: 0, // 禁用垃圾回收
      enabled: true // 确保查询启用
    },
    mutations: { retry: false }
  }
})

// 包装组件提供QueryClientProvider和CartProvider
const renderWithProviders = (ui: React.ReactElement) => {
  const testQueryClient = createTestQueryClient()
  return render(
    <QueryClientProvider client={testQueryClient}>
      <CartProvider>
        {ui}
      </CartProvider>
    </QueryClientProvider>
  )
}

// 导入api模块以触发mock初始化
import * as api from '@/api'

describe('购物车页面', () => {
  beforeEach(() => {
    jest.clearAllMocks()
    // 设置默认购物车数据（包含2个商品）
    mockGetStorageSync.mockImplementation((key) => {
        if (key === 'mini_cart') {
        return { items: mockCartItems }
      }
      return null
    })
    mockShowModal.mockImplementation(() => Promise.resolve({ confirm: true }))
    // 触发goodsClient getter以确保mock被创建
    // 访问api.goodsClient会触发getter，创建mockGoodsClient
    if (api.goodsClient) {
      // mock已经被创建，jest.clearAllMocks()已经清除了调用记录
    }
    mockRequest.mockClear()
  })

  it('应该正确渲染购物车页面标题', () => {
    const { getByText } = renderWithProviders(<CartPage />)
    expect(getByText('购物车')).toBeDefined()
  })

  it('应该显示购物车中的商品列表', async () => {
    const { findByText } = renderWithProviders(<CartPage />)

    // 等待商品API被调用
    await waitFor(() => {
      expect(api.goodsClient[':id'].$get).toHaveBeenCalled()
    })

    // 等待查询完成，商品名称应该显示父商品名称
    expect(await findByText('测试商品1')).toBeDefined()
    expect(await findByText('测试商品2')).toBeDefined()
    expect(await findByText('¥29.90')).toBeDefined()
    expect(await findByText('¥49.90')).toBeDefined()
  })

  it('应该显示商品规格信息', async () => {
    const { findByText } = renderWithProviders(<CartPage />)
    expect(await findByText('红色/M')).toBeDefined()
    expect(await findByText('蓝色/L')).toBeDefined()
  })

  it('应该显示商品数量选择器', () => {
    const { getByDisplayValue } = renderWithProviders(<CartPage />)
    expect(getByDisplayValue('2')).toBeDefined() // 商品1的数量
    expect(getByDisplayValue('1')).toBeDefined() // 商品2的数量
  })

  it('应该显示底部结算栏', () => {
    const { getByText } = renderWithProviders(<CartPage />)
    expect(getByText('全选')).toBeDefined()
    expect(getByText('总计')).toBeDefined()
    expect(getByText('去结算(0)')).toBeDefined()
  })

  it('应该支持全选功能', () => {
    const { getByText } = renderWithProviders(<CartPage />)
    const selectAllButton = getByText('全选')

    fireEvent.click(selectAllButton)

    // 检查结算按钮文本变化
    expect(getByText('去结算(2)')).toBeDefined()
  })

  it('应该支持单个商品选择', () => {
    const { getByText } = renderWithProviders(<CartPage />)
    const selectAllButton = getByText('全选')

    fireEvent.click(selectAllButton)

    // 再次点击取消全选
    fireEvent.click(selectAllButton)
    expect(getByText('去结算(0)')).toBeDefined()
  })

  it('应该显示清空购物车按钮', () => {
    const { getByText } = renderWithProviders(<CartPage />)
    const clearButton = getByText('清空购物车')

    fireEvent.click(clearButton)

    expect(mockShowModal).toHaveBeenCalledWith({
      title: '清空购物车',
      content: '确定要清空购物车吗？',
      success: expect.any(Function),
    })
  })

  it('应该显示删除按钮', () => {
    const { getAllByText } = renderWithProviders(<CartPage />)
    const deleteButtons = getAllByText('删除')

    expect(deleteButtons).toHaveLength(2)

    fireEvent.click(deleteButtons[0])

    expect(mockShowModal).toHaveBeenCalledWith({
      title: '删除商品',
      content: '确定要删除这个商品吗？',
      success: expect.any(Function),
    })
  })

  it('应该显示库存不足提示', async () => {
    // 修复：库存提示显示逻辑
    // 商品2的购物车stock改为2，应该显示"仅剩2件"
    // 即使useQueries不返回数据，使用item.stock也会触发提示
    const { findByText } = renderWithProviders(<CartPage />)

    // 等待商品2加载完成
    await findByText('测试商品2')

    // 商品2的stock是2，应该显示"仅剩2件"
    // 使用findByText等待元素出现
    expect(await findByText('仅剩2件')).toBeDefined()
  })

  it('应该显示库存不足提示（API查询成功）', async () => {
    // 获取mock的goodsClient
    const api = require('@/api')

    // 使用spyOn确保我们监视正确的方法
    const goodsClientSpy = jest.spyOn(api.goodsClient[':id'], '$get')
    goodsClientSpy.mockReset()

    // 设置购物车数据，商品2的本地库存为5（不触发提示），API返回1（触发提示）
    const testCartItems = [
      {
        id: 1,
        parentGoodsId: 100,
        name: '测试商品1',
        price: 29.9,
        image: 'test-image1.jpg',
        stock: 10,
        quantity: 2,
      },
      {
        id: 2,
        parentGoodsId: 200,
        name: '测试商品2',
        price: 49.9,
        image: 'test-image2.jpg',
        stock: 5,  // 本地库存5，不触发提示（>3）
        quantity: 1,
      },
    ]
    mockGetStorageSync.mockReturnValue({ items: testCartItems })

    // 设置mock返回正确的数据
    goodsClientSpy.mockImplementation(({ param }: any) => {
      const goodsId = param?.id

      // 根据商品ID返回不同的库存数据
      if (goodsId === 1) {
        return Promise.resolve({
          status: 200,
          json: () => Promise.resolve({
            id: 1,
            name: '测试商品1',
            price: 29.9,
            imageFile: { fullUrl: 'test-image1.jpg' },
            stock: 10
          })
        })
      } else if (goodsId === 2) {
        return Promise.resolve({
          status: 200,
          json: () => Promise.resolve({
            id: 2,
            name: '测试商品2',
            price: 49.9,
            imageFile: { fullUrl: 'test-image2.jpg' },
            stock: 1  // 低库存，触发库存提示
          })
        })
      }

      // 默认返回商品1的数据
      return Promise.resolve({
        status: 200,
        json: () => Promise.resolve({
          id: 1,
          name: '测试商品1',
          price: 29.9,
          imageFile: { fullUrl: 'test-image1.jpg' },
          stock: 10
        })
      })
    })

    const { findByText } = renderWithProviders(<CartPage />)

    // 等待商品2加载完成
    await findByText('测试商品2')

    // 等待查询完成
    await new Promise(resolve => setTimeout(resolve, 200))

    // 商品2的API库存是1，应该显示"仅剩1件"
    expect(await findByText('仅剩1件')).toBeDefined()

    // 清理spy
    goodsClientSpy.mockRestore()
  })

  it('应该显示广告区域', () => {
    const { container } = renderWithProviders(<CartPage />)
    const adElement = container.querySelector('.cart-advertisement')
    expect(adElement).toBeDefined()
  })

  describe('空购物车状态', () => {
    beforeEach(() => {
      // 设置空购物车数据
      mockGetStorageSync.mockReturnValue({ items: [] })
      // 确保其他mock被清除
      mockShowModal.mockImplementation(() => Promise.resolve({ confirm: true }))
      api.goodsClient[':id'].$get.mockClear()
      mockRequest.mockClear()
    })

    it('应该显示空购物车状态', async () => {
      const { findByText } = renderWithProviders(<CartPage />)
      expect(await findByText('购物车是空的')).toBeDefined()
      expect(await findByText('去首页逛逛')).toBeDefined()
    })

    it('应该隐藏底部结算栏', async () => {
      const { queryByText, findByText } = renderWithProviders(<CartPage />)
      // 等待空状态显示，确保骨架屏已消失
      await findByText('购物车是空的')
      expect(queryByText('去结算')).toBeNull()
    })
  })

  describe('结算功能', () => {
    it('应该阻止未选择商品时结算', () => {
      const { getByText } = renderWithProviders(<CartPage />)
      const checkoutButton = getByText('去结算(0)')

      fireEvent.click(checkoutButton)

      expect(mockShowToast).toHaveBeenCalledWith({
        title: '请选择商品',
        icon: 'none',
      })
    })

    it('应该允许选择商品后结算', () => {
      const { getByText } = renderWithProviders(<CartPage />)
      const selectAllButton = getByText('全选')
      const checkoutButton = getByText('去结算(0)')

      fireEvent.click(selectAllButton)
      fireEvent.click(checkoutButton)

      expect(mockSetStorageSync).toHaveBeenCalledWith('checkoutItems', {
        items: expect.any(Array),
        totalAmount: expect.any(Number),
      })
      expect(mockNavigateTo).toHaveBeenCalledWith({
        url: '/pages/order-submit/index',
      })
    })
  })

  describe('规格切换功能', () => {
    beforeEach(() => {
      // 确保规格选择器API mock被清除
      mockRequest.mockClear()
    })

    it('应该显示规格选择区域', async () => {
      const { findByText } = renderWithProviders(<CartPage />)

      // 检查规格文本是否显示
      expect(await findByText('红色/M')).toBeDefined()
      expect(await findByText('蓝色/L')).toBeDefined()
    })

    it('规格区域应该可点击并打开规格选择器', async () => {
      const { findByText, container } = renderWithProviders(<CartPage />)

      // 获取规格元素
      const specElement = await findByText('红色/M')

      // 验证元素存在
      expect(specElement).toBeDefined()

      // 点击规格区域 - 点击规格文本的父元素（div.goods-specs）
      const specContainer = container.querySelector('.goods-specs')
      fireEvent.click(specContainer || specElement)

      // 验证规格选择器应该显示（通过检查规格选择器组件是否被渲染）
      // 由于GoodsSpecSelector组件是真实组件，我们需要检查其props
      // 规格选择器标题"选择规格"应该显示
      await waitFor(() => {
        expect(container.querySelector('.spec-modal-title')).toBeDefined()
      })
    })

    it('应该加载子商品数据并显示规格选择器', async () => {
      // Mock子商品API调用
      const mockChildGoodsResponse = {
        status: 200,
        json: () => Promise.resolve({
          data: [
            {
              id: 101, // 新子商品ID
              name: '测试商品1 - 蓝色/S',
              price: 29.9,
              stock: 8,
              imageFile: { fullUrl: 'test-image1-blue.jpg' }
            },
            {
              id: 102, // 另一个子商品ID
              name: '测试商品1 - 黑色/M',
              price: 34.9,
              stock: 5,
              imageFile: { fullUrl: 'test-image1-black.jpg' }
            }
          ],
          total: 2,
          page: 1,
          pageSize: 100
        })
      }

      // Mock goodsClient的children API - 使用导入的api模块
      const childrenSpy = jest.spyOn(api.goodsClient[':id'].children, '$get')
      childrenSpy.mockImplementation(({ param, query }: any) => {
        return Promise.resolve(mockChildGoodsResponse)
      })

      const { findByText, container } = renderWithProviders(<CartPage />)

      // 首先等待商品API被调用，确保商品数据加载
      await waitFor(() => {
        expect(api.goodsClient[':id'].$get).toHaveBeenCalled()
      })

      // 等待商品名称显示
      await findByText(/测试商品1/)

      // 点击规格区域打开选择器 - 规格区域显示的是规格名称"红色/M"
      const specElement = await findByText('红色/M')
      fireEvent.click(specElement)

      // 等待API调用
      await waitFor(() => {
        expect(childrenSpy).toHaveBeenCalled()
      })

      // 清理spy
      childrenSpy.mockRestore()
    })

    it('应该支持切换规格并更新商品信息', async () => {
      // Mock子商品API调用
      const mockChildGoodsResponse = {
        status: 200,
        json: () => Promise.resolve({
          data: [
            {
              id: 101, // 新子商品ID
              name: '测试商品1 - 蓝色/S',
              price: 29.9,
              stock: 8,
              imageFile: { fullUrl: 'test-image1-blue.jpg' }
            },
            {
              id: 1, // 当前子商品ID
              name: '测试商品1 - 红色/M',
              price: 29.9,
              stock: 10,
              imageFile: { fullUrl: 'test-image1.jpg' }
            }
          ],
          total: 2,
          page: 1,
          pageSize: 100
        })
      }

      // Mock goodsClient的children API
      const api = require('@/api')
      const childrenSpy = jest.spyOn(api.goodsClient[':id'].children, '$get')
      childrenSpy.mockImplementation(({ param, query }: any) => {
        return Promise.resolve(mockChildGoodsResponse)
      })

      // Mock switchSpec调用
      const { getByText, container } = renderWithProviders(<CartPage />)

      // 点击规格区域打开选择器 - 点击规格文本的父元素（div.goods-specs）
      const specElement = getByText('红色/M')
      const specContainer = container.querySelector('.goods-specs')
      fireEvent.click(specContainer || specElement)

      // 等待API调用
      await waitFor(() => {
        expect(childrenSpy).toHaveBeenCalled()
      })

      // 注意：由于GoodsSpecSelector是真实组件，在测试环境中无法直接模拟其内部状态
      // 这里我们验证API调用和基本交互

      // 清理spy
      childrenSpy.mockRestore()
    })

    it('切换规格后应该更新购物车总价', async () => {
      const { getByText, getAllByText } = renderWithProviders(<CartPage />)

      // 先全选商品
      const selectAllButton = getByText('全选')
      fireEvent.click(selectAllButton)

      // 获取切换前的总价 - 使用更具体的查询
      const totalAmountElements = getAllByText(/¥\d+\.\d{2}/)
      // 最后一个元素应该是总计金额
      const totalAmountElement = totalAmountElements[totalAmountElements.length - 1]
      const totalAmountBefore = totalAmountElement.textContent

      // 验证总价显示存在
      expect(getByText(/总计/)).toBeDefined()
      expect(totalAmountBefore).toMatch(/¥\d+\.\d{2}/)
    })

    it('库存不足的规格应该被禁用或提示', async () => {
      // Mock子商品API调用，包含库存不足的商品
      const mockChildGoodsResponse = {
        status: 200,
        json: () => Promise.resolve({
          data: [
            {
              id: 103,
              name: '测试商品1 - 白色/XL',
              price: 39.9,
              stock: 0, // 库存为0
              imageFile: { fullUrl: 'test-image1-white.jpg' }
            },
            {
              id: 104,
              name: '测试商品1 - 黄色/L',
              price: 32.9,
              stock: 1, // 低库存
              imageFile: { fullUrl: 'test-image1-yellow.jpg' }
            }
          ],
          total: 2,
          page: 1,
          pageSize: 100
        })
      }

      const api = require('@/api')
      const childrenSpy = jest.spyOn(api.goodsClient[':id'].children, '$get')
      childrenSpy.mockImplementation(({ param, query }: any) => {
        return Promise.resolve(mockChildGoodsResponse)
      })

      const { getByText, container } = renderWithProviders(<CartPage />)

      // 点击规格区域 - 点击规格文本的父元素（div.goods-specs）
      const specElement = getByText('红色/M')
      const specContainer = container.querySelector('.goods-specs')
      fireEvent.click(specContainer || specElement)

      // 验证API被调用
      await waitFor(() => {
        expect(childrenSpy).toHaveBeenCalled()
      })

      childrenSpy.mockRestore()
    })

    it('应该处理API返回404错误（父商品不存在）', async () => {
      // Mock API返回404错误
      const mockErrorResponse = {
        status: 404,
        json: () => Promise.resolve({
          code: 404,
          message: '父商品不存在或不是有效的父商品'
        })
      }

      const api = require('@/api')
      const childrenSpy = jest.spyOn(api.goodsClient[':id'].children, '$get')
      childrenSpy.mockImplementation(({ param, query }: any) => {
        return Promise.resolve(mockErrorResponse)
      })

      const { getByText, findByText, container } = renderWithProviders(<CartPage />)

      // 点击规格区域打开选择器 - 点击规格文本的父元素（div.goods-specs）
      const specElement = getByText('红色/M')
      // 查找父元素div.goods-specs
      const specContainer = container.querySelector('.goods-specs')
      fireEvent.click(specContainer || specElement)

      // 等待规格选择器显示 - 精确匹配标题
      await waitFor(() => {
        expect(getByText(/^选择规格$/)).toBeDefined()
      })

      // 验证API被调用
      await waitFor(() => {
        expect(childrenSpy).toHaveBeenCalled()
      })

      // 等待错误消息显示 - GoodsSpecSelector会显示API返回的错误信息
      await waitFor(() => {
        expect(getByText('父商品不存在或不是有效的父商品')).toBeDefined()
      })

      childrenSpy.mockRestore()
    })

    it('单规格商品不应该显示规格切换区域', () => {
      // 设置购物车数据，包含单规格商品
      const singleSpecCartItems = [
        {
          id: 300,
          parentGoodsId: 0, // 单规格商品
          name: '单规格商品',
          price: 99.9,
          image: 'single.jpg',
          stock: 10,
          quantity: 1,
          // 没有spec字段
        }
      ]
      mockGetStorageSync.mockReturnValue({ items: singleSpecCartItems })
      // Mock goodsClient 返回单规格商品数据（无parent对象）
      api.goodsClient[':id'].$get.mockImplementation(({ param }: any) => {
        const goodsId = param?.id
        if (goodsId === 300) {
          const singleSpecGoodsData = {
            id: 300,
            name: '单规格商品',
            price: 99.9,
            imageFile: { fullUrl: 'single.jpg' },
            stock: 10
            // 无parent字段，因为不是子商品
          }
          return Promise.resolve({
            status: 200,
            json: () => Promise.resolve(singleSpecGoodsData)
          })
        }
        // 默认返回mockGoodsData[1]
        const goodsData = mockGoodsData[1]
        return Promise.resolve({
          status: 200,
          json: () => Promise.resolve(goodsData)
        })
      })

      const { queryByText, getByText, container } = renderWithProviders(<CartPage />)

      // 商品名称应该显示
      expect(getByText('单规格商品')).toBeDefined()

      // 不应该显示"选择规格"文本
      expect(queryByText('选择规格')).toBeNull()

      // 不应该显示规格文本（如"红色/M"）
      const specElements = Array.from(container.querySelectorAll('.goods-specs'))
      expect(specElements.length).toBe(0)
    })
  })
})