import React from 'react'
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { LocationSearch } from '../../src/components/LocationSearch'

// Mock API 客户端
const mockLocationClient = {
  $get: jest.fn()
}

jest.mock('../../src/api', () => ({
  locationClient: mockLocationClient
}))

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

// 包装组件
const Wrapper = ({ children }: { children: React.ReactNode }) => {
  const queryClient = createTestQueryClient()
  return (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  )
}

// 模拟数据
const mockLocations = [
  {
    id: 1,
    name: '北京南站',
    province: '北京市',
    city: '北京市',
    district: '丰台区',
    address: '北京市丰台区北京南站'
  },
  {
    id: 2,
    name: '首都机场',
    province: '北京市',
    city: '北京市',
    district: '顺义区',
    address: '北京市顺义区首都机场'
  },
  {
    id: 3,
    name: '天安门广场',
    province: '北京市',
    city: '北京市',
    district: '东城区',
    address: '北京市东城区天安门广场'
  }
]

describe('LocationSearch 组件', () => {
  beforeEach(() => {
    // 重置所有 mock
    jest.clearAllMocks()

    // 设置默认的 mock 返回值
    mockLocationClient.$get.mockResolvedValue({
      status: 200,
      json: async () => mockLocations
    })
  })

  test('应该正确渲染组件', () => {
    const onChange = jest.fn()

    render(
      <Wrapper>
        <LocationSearch onChange={onChange} />
      </Wrapper>
    )

    // 检查输入框
    expect(screen.getByPlaceholderText('搜索地点')).toBeInTheDocument()
  })

  test('应该显示自定义占位符', () => {
    const onChange = jest.fn()

    render(
      <Wrapper>
        <LocationSearch
          onChange={onChange}
          placeholder="搜索出发地"
        />
      </Wrapper>
    )

    expect(screen.getByPlaceholderText('搜索出发地')).toBeInTheDocument()
  })

  test('应该处理输入变化和防抖搜索', async () => {
    const onChange = jest.fn()

    render(
      <Wrapper>
        <LocationSearch onChange={onChange} />
      </Wrapper>
    )

    const input = screen.getByPlaceholderText('搜索地点')

    // 输入搜索关键词
    fireEvent.input(input, { target: { value: '北京' } })

    // 检查输入值
    expect(input).toHaveValue('北京')

    // 等待防抖时间
    await waitFor(() => {
      expect(mockLocationClient.$get).toHaveBeenCalledWith({
        query: {
          keyword: '北京',
          provinceId: undefined,
          cityId: undefined,
          districtId: undefined
        }
      })
    }, { timeout: 500 })
  })

  test('应该显示搜索结果', async () => {
    const onChange = jest.fn()

    render(
      <Wrapper>
        <LocationSearch onChange={onChange} />
      </Wrapper>
    )

    const input = screen.getByPlaceholderText('搜索地点')

    // 输入搜索关键词
    fireEvent.input(input, { target: { value: '北京' } })

    // 等待搜索结果
    await waitFor(() => {
      expect(screen.getByText('北京南站')).toBeInTheDocument()
      expect(screen.getByText('首都机场')).toBeInTheDocument()
      expect(screen.getByText('天安门广场')).toBeInTheDocument()
    })

    // 检查地点信息显示
    expect(screen.getByText('北京南站 · 丰台区 · 北京市 · 北京市')).toBeInTheDocument()
    expect(screen.getByText('北京市丰台区北京南站')).toBeInTheDocument()
  })

  test('应该处理地点选择', async () => {
    const onChange = jest.fn()

    render(
      <Wrapper>
        <LocationSearch onChange={onChange} />
      </Wrapper>
    )

    const input = screen.getByPlaceholderText('搜索地点')

    // 输入搜索关键词
    fireEvent.input(input, { target: { value: '北京' } })

    // 等待搜索结果
    await waitFor(() => {
      expect(screen.getByText('北京南站')).toBeInTheDocument()
    })

    // 选择地点
    const locationItem = screen.getByText('北京南站')
    fireEvent.click(locationItem)

    // 检查 onChange 被调用
    expect(onChange).toHaveBeenCalledWith(mockLocations[0])

    // 检查输入框值更新
    expect(input).toHaveValue('北京南站')

    // 检查搜索结果隐藏
    expect(screen.queryByText('首都机场')).not.toBeInTheDocument()
  })

  test('应该处理清除操作', () => {
    const onChange = jest.fn()

    render(
      <Wrapper>
        <LocationSearch onChange={onChange} />
      </Wrapper>
    )

    const input = screen.getByPlaceholderText('搜索地点')

    // 输入搜索关键词
    fireEvent.input(input, { target: { value: '北京' } })

    // 检查清除按钮出现
    const clearButton = screen.getByText('×')
    expect(clearButton).toBeInTheDocument()

    // 点击清除按钮
    fireEvent.click(clearButton)

    // 检查输入框被清空
    expect(input).toHaveValue('')

    // 检查 onChange 被调用
    expect(onChange).toHaveBeenCalledWith(null)
  })

  test('应该显示当前选择的地点', () => {
    const selectedLocation = {
      id: 1,
      name: '北京南站',
      province: '北京市',
      city: '北京市',
      district: '丰台区',
      address: '北京市丰台区北京南站'
    }

    const onChange = jest.fn()

    render(
      <Wrapper>
        <LocationSearch
          value={selectedLocation}
          onChange={onChange}
        />
      </Wrapper>
    )

    // 检查已选择的地点显示
    expect(screen.getByText('已选择: 北京南站 · 丰台区 · 北京市 · 北京市')).toBeInTheDocument()
  })

  test('应该支持地区筛选', async () => {
    const onChange = jest.fn()
    const areaFilter = {
      provinceId: 1,
      cityId: 11,
      districtId: 101
    }

    render(
      <Wrapper>
        <LocationSearch
          onChange={onChange}
          areaFilter={areaFilter}
        />
      </Wrapper>
    )

    const input = screen.getByPlaceholderText('搜索地点')

    // 输入搜索关键词
    fireEvent.input(input, { target: { value: '北京' } })

    // 等待搜索调用
    await waitFor(() => {
      expect(mockLocationClient.$get).toHaveBeenCalledWith({
        query: {
          keyword: '北京',
          provinceId: 1,
          cityId: 11,
          districtId: 101
        }
      })
    }, { timeout: 500 })
  })

  test('应该显示搜索中状态', async () => {
    // 模拟延迟响应
    mockLocationClient.$get.mockImplementation(() =>
      new Promise(resolve => {
        setTimeout(() => {
          resolve({
            status: 200,
            json: async () => mockLocations
          })
        }, 100)
      })
    )

    const onChange = jest.fn()

    render(
      <Wrapper>
        <LocationSearch onChange={onChange} />
      </Wrapper>
    )

    const input = screen.getByPlaceholderText('搜索地点')

    // 输入搜索关键词
    fireEvent.input(input, { target: { value: '北京' } })

    // 检查搜索中状态
    await waitFor(() => {
      expect(screen.getByText('搜索中...')).toBeInTheDocument()
    })
  })

  test('应该显示无结果状态', async () => {
    // 模拟空结果
    mockLocationClient.$get.mockResolvedValue({
      status: 200,
      json: async () => []
    })

    const onChange = jest.fn()

    render(
      <Wrapper>
        <LocationSearch onChange={onChange} />
      </Wrapper>
    )

    const input = screen.getByPlaceholderText('搜索地点')

    // 输入搜索关键词
    fireEvent.input(input, { target: { value: '不存在的地点' } })

    // 检查无结果状态
    await waitFor(() => {
      expect(screen.getByText('未找到相关地点')).toBeInTheDocument()
    })
  })

  test('应该显示请输入关键词状态', () => {
    const onChange = jest.fn()

    render(
      <Wrapper>
        <LocationSearch onChange={onChange} />
      </Wrapper>
    )

    const input = screen.getByPlaceholderText('搜索地点')

    // 聚焦输入框但不输入内容
    fireEvent.focus(input)

    // 检查提示状态
    expect(screen.getByText('请输入地点名称')).toBeInTheDocument()
  })

  test('应该处理 API 错误', async () => {
    // 模拟 API 错误
    mockLocationClient.$get.mockResolvedValue({
      status: 500,
      json: async () => ({ success: false, message: '服务器错误' })
    })

    const onChange = jest.fn()

    render(
      <Wrapper>
        <LocationSearch onChange={onChange} />
      </Wrapper>
    )

    const input = screen.getByPlaceholderText('搜索地点')

    // 输入搜索关键词
    fireEvent.input(input, { target: { value: '北京' } })

    // 组件应该正常处理错误，不显示搜索结果
    await waitFor(() => {
      expect(mockLocationClient.$get).toHaveBeenCalled()
    })

    // 检查没有显示错误信息（组件应该静默处理错误）
    expect(screen.queryByText('服务器错误')).not.toBeInTheDocument()
  })
})