import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { renderHook, waitFor } from '@testing-library/react'
import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
import React from 'react'
import { MessagePlugin } from 'tdesign-react'
import { useProviders, useCreateProvider, useUpdateProvider, useDeleteProvider } from '@/hooks/useProviders'
import type { Provider, CreateProviderInput, UpdateProviderInput } from '@/types'

// Mock MessagePlugin
vi.mock('tdesign-react', () => ({
  MessagePlugin: {
    success: vi.fn(),
    error: vi.fn(),
  },
}))

// Test data
const mockProviders: Provider[] = [
  {
    id: 'provider-1',
    name: 'OpenAI',
    apiKey: 'sk-xxx',
    baseUrl: 'https://api.openai.com',
    protocol: 'openai',
    enabled: true,
    createdAt: '2026-01-01T00:00:00Z',
    updatedAt: '2026-01-01T00:00:00Z',
  },
  {
    id: 'provider-2',
    name: 'Anthropic',
    apiKey: 'sk-yyy',
    baseUrl: 'https://api.anthropic.com',
    protocol: 'anthropic',
    enabled: false,
    createdAt: '2026-02-01T00:00:00Z',
    updatedAt: '2026-02-01T00:00:00Z',
  },
]

const mockCreatedProvider: Provider = {
  id: 'provider-3',
  name: 'NewProvider',
  apiKey: 'sk-zzz',
  baseUrl: 'https://api.newprovider.com',
  protocol: 'openai',
  enabled: true,
  createdAt: '2026-03-01T00:00:00Z',
  updatedAt: '2026-03-01T00:00:00Z',
}

// MSW handlers
const handlers = [
  http.get('/api/providers', () => {
    return HttpResponse.json(mockProviders)
  }),
  http.post('/api/providers', async ({ request }) => {
    const body = (await request.json()) as Record<string, unknown>
    return HttpResponse.json({
      ...mockCreatedProvider,
      ...body,
    })
  }),
  http.put('/api/providers/:id', async ({ request, params }) => {
    const body = (await request.json()) as Record<string, unknown>
    const existing = mockProviders.find((p) => p.id === params['id'])
    return HttpResponse.json({ ...existing, ...body })
  }),
  http.delete('/api/providers/:id', () => {
    return new HttpResponse(null, { status: 204 })
  }),
]

const server = setupServer(...handlers)

function createTestQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  })
}

function createWrapper() {
  const testQueryClient = createTestQueryClient()
  return function Wrapper({ children }: { children: React.ReactNode }) {
    return <QueryClientProvider client={testQueryClient}>{children}</QueryClientProvider>
  }
}

beforeAll(() => server.listen())
afterEach(() => {
  server.resetHandlers()
  vi.clearAllMocks()
})
afterAll(() => server.close())

describe('useProviders', () => {
  it('fetches and returns provider list', async () => {
    const { result } = renderHook(() => useProviders(), {
      wrapper: createWrapper(),
    })

    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect(result.current.data).toEqual(mockProviders)
    expect(result.current.data).toHaveLength(2)
    expect(result.current.data![0]!.name).toBe('OpenAI')
    expect(result.current.data![1]!.name).toBe('Anthropic')
  })
})

describe('useCreateProvider', () => {
  it('calls API and invalidates provider queries', async () => {
    const queryClient = createTestQueryClient()
    const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries')

    function Wrapper({ children }: { children: React.ReactNode }) {
      return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
    }

    const { result } = renderHook(() => useCreateProvider(), {
      wrapper: Wrapper,
    })

    const input: CreateProviderInput = {
      id: 'provider-3',
      name: 'NewProvider',
      apiKey: 'sk-zzz',
      baseUrl: 'https://api.newprovider.com',
      protocol: 'openai',
      enabled: true,
    }

    result.current.mutate(input)

    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect(result.current.data).toMatchObject({
      id: 'provider-3',
      name: 'NewProvider',
    })
    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['providers'] })
    expect(MessagePlugin.success).toHaveBeenCalledWith('供应商创建成功')
  })

  it('calls message.error on failure', async () => {
    server.use(
      http.post('/api/providers', () => {
        return HttpResponse.json({ message: '创建失败' }, { status: 500 })
      })
    )

    const { result } = renderHook(() => useCreateProvider(), {
      wrapper: createWrapper(),
    })

    const input: CreateProviderInput = {
      id: 'provider-3',
      name: 'NewProvider',
      apiKey: 'sk-zzz',
      baseUrl: 'https://api.newprovider.com',
      protocol: 'openai',
      enabled: true,
    }

    result.current.mutate(input)

    await waitFor(() => expect(result.current.isError).toBe(true))
    expect(MessagePlugin.error).toHaveBeenCalled()
  })
})

describe('useUpdateProvider', () => {
  it('calls API and invalidates provider queries', async () => {
    const queryClient = createTestQueryClient()
    const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries')

    function Wrapper({ children }: { children: React.ReactNode }) {
      return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
    }

    const { result } = renderHook(() => useUpdateProvider(), {
      wrapper: Wrapper,
    })

    const input: UpdateProviderInput = { name: 'UpdatedProvider' }

    result.current.mutate({ id: 'provider-1', input })

    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect(result.current.data).toMatchObject({
      name: 'UpdatedProvider',
    })
    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['providers'] })
    expect(MessagePlugin.success).toHaveBeenCalledWith('供应商更新成功')
  })

  it('calls message.error on failure', async () => {
    server.use(
      http.put('/api/providers/:id', () => {
        return HttpResponse.json({ message: '更新失败' }, { status: 500 })
      })
    )

    const { result } = renderHook(() => useUpdateProvider(), {
      wrapper: createWrapper(),
    })

    result.current.mutate({ id: 'provider-1', input: { name: 'Updated' } })

    await waitFor(() => expect(result.current.isError).toBe(true))
    expect(MessagePlugin.error).toHaveBeenCalled()
  })
})

describe('useDeleteProvider', () => {
  it('calls API and invalidates provider queries', async () => {
    const queryClient = createTestQueryClient()
    const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries')

    function Wrapper({ children }: { children: React.ReactNode }) {
      return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
    }

    const { result } = renderHook(() => useDeleteProvider(), {
      wrapper: Wrapper,
    })

    result.current.mutate('provider-1')

    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['providers'] })
    expect(MessagePlugin.success).toHaveBeenCalledWith('供应商删除成功')
  })

  it('calls message.error on failure', async () => {
    server.use(
      http.delete('/api/providers/:id', () => {
        return HttpResponse.json({ message: '删除失败' }, { status: 500 })
      })
    )

    const { result } = renderHook(() => useDeleteProvider(), {
      wrapper: createWrapper(),
    })

    result.current.mutate('provider-1')

    await waitFor(() => expect(result.current.isError).toBe(true))
    expect(MessagePlugin.error).toHaveBeenCalled()
  })
})
