import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { mockShowToast, mockNavigateTo, mockGetStorageSync, mockSetStorageSync } from '~/__mocks__/taroMock'
import GoodsDetailPage from '@/pages/goods-detail/index'
import { goodsClient } from '@/api'
import { useCart } from '@/contexts/CartContext'

// Mock API客户端
jest.mock('@/api', () => ({
  goodsClient: {
    ':id': {
      $get: jest.fn(),
      children: {
        $get: jest.fn()
      }
    }
  }
}))

// Mock Cart Context
const mockAddToCart = jest.fn()
jest.mock('@/contexts/CartContext', () => ({
  useCart: () => ({
    addToCart: mockAddToCart
  })
}))

// Mock Taro useRouter
const mockUseRouter = jest.fn()
jest.mock('@tarojs/taro', () => ({
  ...jest.requireActual('~/__mocks__/taroMock'),
  useRouter: () => mockUseRouter(),
  useShareAppMessage: () => {},
  previewImage: jest.fn(),
  navigateBack: jest.fn(),
  switchTab: jest.fn()
}))

// Mock Taro组件
jest.mock('@tarojs/components', () => ({
  View: ({ children, className, onClick }: any) => (
    <div className={className} onClick={onClick}>
      {children}
    </div>
  ),
  Text: ({ children, className }: any) => (
    <span className={className}>{children}</span>
  ),
  ScrollView: ({ children, className }: any) => (
    <div className={className}>{children}</div>
  ),
  Input: ({ value, className, onInput, onBlur, placeholder, maxlength, confirmType }: any) => (
    <input
      className={className}
      value={value}
      onChange={(e) => onInput && onInput({ detail: { value: e.target.value } })}
      onBlur={onBlur}
      placeholder={placeholder}
      maxLength={maxlength}
      type="number"
    />
  ),
  RichText: ({ nodes, className }: any) => (
    <div className={className} dangerouslySetInnerHTML={{ __html: nodes }} />
  )
}))

// Mock UI组件
jest.mock('@/components/ui/navbar', () => ({
  Navbar: ({ title, leftIcon, onClickLeft }: any) => (
    <div className="navbar" onClick={onClickLeft}>
      {title}
    </div>
  )
}))

jest.mock('@/components/ui/button', () => ({
  Button: ({ children, onClick, className, disabled, size, variant }: any) => (
    <button className={className} onClick={onClick} disabled={disabled}>
      {children}
    </button>
  )
}))

jest.mock('@/components/ui/carousel', () => ({
  Carousel: ({ items, height, autoplay, interval, circular, onItemClick }: any) => (
    <div className="carousel">
      {items.map((item: any, index: number) => (
        <div key={index} onClick={() => onItemClick && onItemClick(item, index)}>
          {/* 图片轮播不显示文本内容 */}
        </div>
      ))}
    </div>
  )
}))

// 使用真实的GoodsSpecSelector组件，不模拟
// jest.mock('@/components/goods-spec-selector', () => ({ ... })) 已移除

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

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

describe('GoodsDetailPage集成测试', () => {
  beforeEach(() => {
    jest.clearAllMocks()
    mockUseRouter.mockReturnValue({ params: { id: '1' } })
    mockGetStorageSync.mockReturnValue(null)
  })

  // Mock商品数据
  const mockGoods = {
    id: 1,
    name: '测试商品',
    price: 299,
    costPrice: 399,
    stock: 100,
    salesNum: 50,
    instructions: '测试商品描述',
    detail: '<p>商品详情</p>',
    slideImages: [
      { fullUrl: 'http://example.com/image1.jpg' },
      { fullUrl: 'http://example.com/image2.jpg' }
    ],
    imageFile: { fullUrl: 'http://example.com/main.jpg' },
    spuId: 0 // 父商品，spuId为0
  }

  // Mock子商品数据
  const mockChildren = {
    data: [
      { id: 101, name: '红色款', price: 299, stock: 50, imageFile: null },
      { id: 102, name: '蓝色款', price: 319, stock: 30, imageFile: null }
    ],
    total: 2,
    page: 1,
    pageSize: 100,
    totalPages: 1
  }

  it('渲染商品详情页面', async () => {
    // Mock商品详情API响应
    const mockGoodsResponse = {
      status: 200,
      json: async () => mockGoods
    }
    ;(goodsClient[':id'].$get as jest.Mock).mockResolvedValue(mockGoodsResponse)

    // Mock子商品API响应
    const mockChildrenResponse = {
      status: 200,
      json: async () => mockChildren
    }
    ;(goodsClient[':id'].children.$get as jest.Mock).mockResolvedValue(mockChildrenResponse)

    render(
      <TestWrapper>
        <GoodsDetailPage />
      </TestWrapper>
    )

    // 等待商品加载 - 通过商品标题class来定位
    await waitFor(() => {
      expect(screen.getByText('测试商品', { selector: '.goods-title' })).toBeInTheDocument()
    })

    // 验证商品信息显示
    expect(screen.getByText('¥299.00')).toBeInTheDocument()
    expect(screen.getByText('¥399.00')).toBeInTheDocument()
    expect(screen.getByText('已售50件')).toBeInTheDocument()
    expect(screen.getByText('测试商品描述')).toBeInTheDocument()
    expect(screen.getByText('加入购物车')).toBeInTheDocument()
    expect(screen.getByText('立即购买')).toBeInTheDocument()
  })

  it('打开规格选择弹窗', async () => {
    const mockGoodsResponse = {
      status: 200,
      json: async () => mockGoods
    }
    ;(goodsClient[':id'].$get as jest.Mock).mockResolvedValue(mockGoodsResponse)
    ;(goodsClient[':id'].children.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: async () => mockChildren
    })

    render(
      <TestWrapper>
        <GoodsDetailPage />
      </TestWrapper>
    )

    await waitFor(() => {
      expect(screen.getByText('测试商品', { selector: '.goods-title' })).toBeInTheDocument()
    })

    // 点击加入购物车按钮 - 新流程：有规格选项且未选择规格时自动弹出规格选择器
    const addToCartButton = screen.getByText('加入购物车')
    fireEvent.click(addToCartButton)

    // 验证规格选择弹窗显示并加载规格选项
    await waitFor(() => {
      // 等待规格选项加载完成
      expect(screen.getByText('红色款')).toBeInTheDocument()
    })
    // 同时也验证弹窗标题和另一个规格选项
    expect(screen.getByText('选择规格', { selector: '.spec-modal-title' })).toBeInTheDocument()
    expect(screen.getByText('蓝色款')).toBeInTheDocument()
  })

  it('选择规格后执行操作', async () => {
    const mockGoodsResponse = {
      status: 200,
      json: async () => mockGoods
    }
    ;(goodsClient[':id'].$get as jest.Mock).mockResolvedValue(mockGoodsResponse)
    ;(goodsClient[':id'].children.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: async () => mockChildren
    })

    render(
      <TestWrapper>
        <GoodsDetailPage />
      </TestWrapper>
    )

    await waitFor(() => {
      expect(screen.getByText('测试商品', { selector: '.goods-title' })).toBeInTheDocument()
    })

    // 打开规格选择弹窗 - 新流程：点击加入购物车按钮
    const addToCartButton = screen.getByText('加入购物车')
    fireEvent.click(addToCartButton)

    // 等待规格弹窗加载
    await waitFor(() => {
      expect(screen.getByText('红色款')).toBeInTheDocument()
    })

    // 选择规格
    const redSpec = screen.getByText('红色款')
    fireEvent.click(redSpec)

    // 点击确认按钮 - 根据新流程，确认后直接执行加入购物车操作
    const confirmButton = screen.getByText(/加入购物车/, { selector: '.spec-confirm-btn' })
    fireEvent.click(confirmButton)

    // 验证addToCart被调用，使用规格信息
    // 注意：根据新流程，选择规格后直接执行操作，所以不需要等待页面显示规格信息
    expect(mockAddToCart).toHaveBeenCalledWith({
      id: 101, // 子商品ID
      parentGoodsId: 1, // 父商品ID（goods.id）
      name: '红色款',
      price: 299,
      image: 'http://example.com/main.jpg',
      stock: 50,
      quantity: 1
      // spec字段已移除，因为不再需要
    })
  })

  it('选择规格后加入购物车', async () => {
    const mockGoodsResponse = {
      status: 200,
      json: async () => mockGoods
    }
    ;(goodsClient[':id'].$get as jest.Mock).mockResolvedValue(mockGoodsResponse)
    ;(goodsClient[':id'].children.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: async () => mockChildren
    })

    render(
      <TestWrapper>
        <GoodsDetailPage />
      </TestWrapper>
    )

    await waitFor(() => {
      expect(screen.getByText('测试商品', { selector: '.goods-title' })).toBeInTheDocument()
    })

    // 打开规格选择弹窗并选择规格 - 新流程：点击加入购物车按钮
    const addToCartButton = screen.getByText('加入购物车')
    fireEvent.click(addToCartButton)

    // 等待规格弹窗加载
    await waitFor(() => {
      expect(screen.getByText('红色款')).toBeInTheDocument()
    })

    // 选择规格
    const redSpec = screen.getByText('红色款')
    fireEvent.click(redSpec)

    // 点击确认按钮 - 根据新流程，确认后直接执行加入购物车操作
    const confirmButton = screen.getByText(/加入购物车/, { selector: '.spec-confirm-btn' })
    fireEvent.click(confirmButton)

    // 验证addToCart被调用，使用规格信息
    // 注意：根据新流程，选择规格后直接执行操作，所以不需要等待页面显示规格信息
    expect(mockAddToCart).toHaveBeenCalledWith({
      id: 101, // 子商品ID
      parentGoodsId: 1, // 父商品ID（goods.id）
      name: '红色款',
      price: 299,
      image: 'http://example.com/main.jpg',
      stock: 50,
      quantity: 1
      // spec字段已移除，因为不再需要
    })
  })

  it('选择规格后立即购买', async () => {
    const mockGoodsResponse = {
      status: 200,
      json: async () => mockGoods
    }
    ;(goodsClient[':id'].$get as jest.Mock).mockResolvedValue(mockGoodsResponse)
    ;(goodsClient[':id'].children.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: async () => mockChildren
    })

    render(
      <TestWrapper>
        <GoodsDetailPage />
      </TestWrapper>
    )

    await waitFor(() => {
      expect(screen.getByText('测试商品', { selector: '.goods-title' })).toBeInTheDocument()
    })

    // 打开规格选择弹窗并选择规格 - 新流程：点击立即购买按钮
    const buyNowButton = screen.getByText('立即购买')
    fireEvent.click(buyNowButton)

    // 等待规格弹窗加载
    await waitFor(() => {
      expect(screen.getByText('红色款')).toBeInTheDocument()
    })

    // 选择规格
    const redSpec = screen.getByText('红色款')
    fireEvent.click(redSpec)

    // 点击确认按钮
    const confirmButton = screen.getByText(/立即购买/, { selector: '.spec-confirm-btn' })
    fireEvent.click(confirmButton)


    // 确认按钮点击后直接执行立即购买操作，不需要再次点击立即购买按钮
    // 验证setStorageSync已被调用，存储购买信息

    expect(mockSetStorageSync).toHaveBeenCalledWith('buyNow', {
      goods: {
        id: 101,
        name: '红色款',
        price: 299,
        image: 'http://example.com/main.jpg',
        quantity: 1
      },
      totalAmount: 299
    })
    expect(mockNavigateTo).toHaveBeenCalledWith({
      url: '/pages/order-submit/index'
    })
  })

  it('无规格商品时使用父商品信息', async () => {
    const mockGoodsResponse = {
      status: 200,
      json: async () => mockGoods
    }
    ;(goodsClient[':id'].$get as jest.Mock).mockResolvedValue(mockGoodsResponse)
    ;(goodsClient[':id'].children.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: async () => ({ data: [], total: 0, page: 1, pageSize: 100, totalPages: 0 })
    })

    render(
      <TestWrapper>
        <GoodsDetailPage />
      </TestWrapper>
    )

    await waitFor(() => {
      expect(screen.getByText('测试商品', { selector: '.goods-title' })).toBeInTheDocument()
    })

    // 不选择规格，直接加入购物车
    const addToCartButton = screen.getByText('加入购物车')
    fireEvent.click(addToCartButton)

    // 验证addToCart被调用，使用父商品信息
    expect(mockAddToCart).toHaveBeenCalledWith({
      id: 1, // 父商品ID
      parentGoodsId: 0, // 父商品，无父商品
      name: '测试商品',
      price: 299,
      image: 'http://example.com/main.jpg',
      stock: 100,
      quantity: 1
    })
  })

  it('规格库存限制数量选择', async () => {
    const mockGoodsResponse = {
      status: 200,
      json: async () => mockGoods
    }
    ;(goodsClient[':id'].$get as jest.Mock).mockResolvedValue(mockGoodsResponse)
    ;(goodsClient[':id'].children.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: async () => mockChildren
    })

    render(
      <TestWrapper>
        <GoodsDetailPage />
      </TestWrapper>
    )

    await waitFor(() => {
      expect(screen.getByText('测试商品', { selector: '.goods-title' })).toBeInTheDocument()
    })

    // 打开规格选择弹窗并选择库存较少的规格 - 新流程：点击加入购物车按钮
    const addToCartButton = screen.getByText('加入购物车')
    fireEvent.click(addToCartButton)

    // 等待规格弹窗加载
    await waitFor(() => {
      expect(screen.getByText('蓝色款')).toBeInTheDocument()
    })

    // 选择规格
    const blueSpec = screen.getByText('蓝色款') // 库存30
    fireEvent.click(blueSpec)

    // 点击确认按钮
    const confirmButton = screen.getByText(/加入购物车/, { selector: '.spec-confirm-btn' })
    fireEvent.click(confirmButton)

    // 规格选择器关闭后，页面不显示规格信息
    // 等待规格选择器关闭
    await waitFor(() => {
      expect(screen.queryByText('蓝色款')).not.toBeInTheDocument()
    })

    // 获取数量输入框 - 商品详情页上的数量输入框
    const quantityInput = screen.getByDisplayValue('1')

    // 尝试输入超过库存的数量
    fireEvent.change(quantityInput, { target: { value: '50' } })
    fireEvent.blur(quantityInput)

    // 验证toast显示库存限制
    expect(mockShowToast).toHaveBeenCalledWith({
      title: '库存只有30件',
      icon: 'none',
      duration: 1500
    })
  })

  it('商品详情API加载失败时按钮应禁用', async () => {
    // Mock商品详情API失败
    ;(goodsClient[':id'].$get as jest.Mock).mockRejectedValue(new Error('网络错误'))
    // Mock子商品API正常返回空列表
    ;(goodsClient[':id'].children.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: async () => ({ data: [], total: 0, page: 1, pageSize: 100, totalPages: 0 })
    })

    render(
      <TestWrapper>
        <GoodsDetailPage />
      </TestWrapper>
    )

    // 等待组件渲染完成（可能显示加载或错误状态）
    await waitFor(() => {
      // 验证页面显示商品不存在提示
      expect(screen.getByText('商品不存在')).toBeInTheDocument()
    })

    // 验证按钮不存在（因为goods为null，按钮不会渲染）
    expect(screen.queryByText('加入购物车')).not.toBeInTheDocument()
    expect(screen.queryByText('立即购买')).not.toBeInTheDocument()
  })

  it('子商品列表API加载失败时按钮状态正确', async () => {
    // Mock商品详情API成功
    const mockGoodsResponse = {
      status: 200,
      json: async () => mockGoods
    }
    ;(goodsClient[':id'].$get as jest.Mock).mockResolvedValue(mockGoodsResponse)
    // Mock子商品API失败
    ;(goodsClient[':id'].children.$get as jest.Mock).mockRejectedValue(new Error('获取子商品列表失败'))

    render(
      <TestWrapper>
        <GoodsDetailPage />
      </TestWrapper>
    )

    // 等待商品加载完成
    await waitFor(() => {
      expect(screen.getByText('测试商品', { selector: '.goods-title' })).toBeInTheDocument()
    })

    // 验证按钮存在且不禁用（因为商品库存>0，且hasSpecOptions为false）
    const addToCartButton = screen.getByText('加入购物车')
    const buyNowButton = screen.getByText('立即购买')
    expect(addToCartButton).toBeInTheDocument()
    expect(buyNowButton).toBeInTheDocument()
    expect(addToCartButton).not.toBeDisabled()
    expect(buyNowButton).not.toBeDisabled()
  })

  it('有规格选项但库存为0时按钮不禁用', async () => {
    // 创建库存为0的子商品数据
    const zeroStockChildren = {
      data: [
        { id: 103, name: '黑色款', price: 299, stock: 0, imageFile: null }
      ],
      total: 1,
      page: 1,
      pageSize: 100,
      totalPages: 1
    }

    // Mock商品详情API成功
    const mockGoodsResponse = {
      status: 200,
      json: async () => mockGoods
    }
    ;(goodsClient[':id'].$get as jest.Mock).mockResolvedValue(mockGoodsResponse)
    // Mock子商品API返回库存为0的子商品
    ;(goodsClient[':id'].children.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: async () => zeroStockChildren
    })

    render(
      <TestWrapper>
        <GoodsDetailPage />
      </TestWrapper>
    )

    // 等待商品加载完成
    await waitFor(() => {
      expect(screen.getByText('测试商品', { selector: '.goods-title' })).toBeInTheDocument()
    })

    // 打开规格选择弹窗 - 新流程：点击加入购物车按钮
    const addToCartButton = screen.getByText('加入购物车')
    fireEvent.click(addToCartButton)

    // 等待规格弹窗加载
    await waitFor(() => {
      expect(screen.getByText('黑色款')).toBeInTheDocument()
    })

    // 选择库存为0的规格
    const blackSpec = screen.getByText('黑色款')
    fireEvent.click(blackSpec)

    // 点击确认按钮
    const confirmButton = screen.getByText(/加入购物车/, { selector: '.spec-confirm-btn' })
    fireEvent.click(confirmButton)

    // 等待规格弹窗关闭，页面更新
    // 注意：根据新流程，选择规格后直接执行操作，页面不显示规格信息
    // 所以不需要等待黑色款文本显示在页面上

    // 验证按钮不禁用（根据新逻辑，多规格商品按钮总是不禁用）
    const addToCartButtonAfter = screen.getByText('加入购物车')
    const buyNowButton = screen.getByText('立即购买')
    expect(addToCartButtonAfter).not.toBeDisabled()
    expect(buyNowButton).not.toBeDisabled()
  })

  it('无规格选项且商品库存为0时按钮应禁用', async () => {
    // 创建库存为0的商品
    const zeroStockGoods = {
      ...mockGoods,
      stock: 0
    }

    // Mock商品详情API成功
    const mockGoodsResponse = {
      status: 200,
      json: async () => zeroStockGoods
    }
    ;(goodsClient[':id'].$get as jest.Mock).mockResolvedValue(mockGoodsResponse)
    // Mock子商品API返回空列表
    ;(goodsClient[':id'].children.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: async () => ({ data: [], total: 0, page: 1, pageSize: 100, totalPages: 0 })
    })

    render(
      <TestWrapper>
        <GoodsDetailPage />
      </TestWrapper>
    )

    // 等待商品加载完成
    await waitFor(() => {
      expect(screen.getByText('测试商品', { selector: '.goods-title' })).toBeInTheDocument()
    })

    // 验证按钮禁用（因为商品库存为0）
    const addToCartButton = screen.getByText('加入购物车')
    const buyNowButton = screen.getByText('立即购买')
    expect(addToCartButton).toBeDisabled()
    expect(buyNowButton).toBeDisabled()
  })

  it('数量输入最小值边界', async () => {
    // Mock商品详情API成功
    const mockGoodsResponse = {
      status: 200,
      json: async () => mockGoods
    }
    ;(goodsClient[':id'].$get as jest.Mock).mockResolvedValue(mockGoodsResponse)
    // Mock子商品API返回空列表（无规格）
    ;(goodsClient[':id'].children.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: async () => ({ data: [], total: 0, page: 1, pageSize: 100, totalPages: 0 })
    })

    render(
      <TestWrapper>
        <GoodsDetailPage />
      </TestWrapper>
    )

    // 等待商品加载完成
    await waitFor(() => {
      expect(screen.getByText('测试商品', { selector: '.goods-title' })).toBeInTheDocument()
    })

    // 获取数量输入框
    const quantityInput = screen.getByDisplayValue('1')

    // 尝试输入0
    fireEvent.change(quantityInput, { target: { value: '0' } })
    fireEvent.blur(quantityInput)

    // 验证数量自动纠正为1
    await waitFor(() => {
      expect(quantityInput).toHaveValue(1)
    })

    // 尝试输入负数（负号会被移除，变为5）
    fireEvent.change(quantityInput, { target: { value: '-5' } })
    fireEvent.blur(quantityInput)

    // 验证负号被移除，值变为5
    await waitFor(() => {
      expect(quantityInput).toHaveValue(5)
    })
  })

  it('规格选择弹窗取消操作', async () => {
    // Mock商品详情API成功
    const mockGoodsResponse = {
      status: 200,
      json: async () => mockGoods
    }
    ;(goodsClient[':id'].$get as jest.Mock).mockResolvedValue(mockGoodsResponse)
    ;(goodsClient[':id'].children.$get as jest.Mock).mockResolvedValue({
      status: 200,
      json: async () => mockChildren
    })

    render(
      <TestWrapper>
        <GoodsDetailPage />
      </TestWrapper>
    )

    // 等待商品加载完成
    await waitFor(() => {
      expect(screen.getByText('测试商品', { selector: '.goods-title' })).toBeInTheDocument()
    })

    // 打开规格选择弹窗 - 新流程：点击加入购物车按钮
    const addToCartButton = screen.getByText('加入购物车')
    fireEvent.click(addToCartButton)

    // 等待规格弹窗加载
    await waitFor(() => {
      expect(screen.getByText('红色款')).toBeInTheDocument()
    })

    // 不选择规格，直接关闭弹窗（点击弹窗外部或关闭按钮）
    // 查找关闭按钮（通过class或文本）
    const closeButton = screen.getByText('选择规格', { selector: '.spec-modal-title' }).closest('.spec-modal-content')?.querySelector('.spec-modal-close')
    if (closeButton) {
      fireEvent.click(closeButton)
    } else {
      // 如果没有关闭按钮，直接模拟关闭操作
      // 点击弹窗外部（通过点击页面其他区域）
      fireEvent.click(document.body)
    }

    // 等待弹窗关闭
    await waitFor(() => {
      expect(screen.queryByText('红色款')).not.toBeInTheDocument()
    })

    // 验证页面没有选择规格
    expect(screen.queryByText('红色款', { selector: '.spec-price' })).not.toBeInTheDocument()

    // 验证按钮不禁用（根据新逻辑，多规格商品按钮总是不禁用）
    const addToCartButtonAfterClose = screen.getByText('加入购物车')
    const buyNowButton = screen.getByText('立即购买')
    expect(addToCartButtonAfterClose).not.toBeDisabled()
    expect(buyNowButton).not.toBeDisabled()
  })
})