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 { useModels, useCreateModel, useUpdateModel, useDeleteModel } from '@/hooks/useModels'
import type { Model, CreateModelInput, UpdateModelInput } from '@/types'

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

// Test data
const mockModels: Model[] = [
  {
    id: 'model-1',
    providerId: 'provider-1',
    modelName: 'gpt-4o',
    enabled: true,
    createdAt: '2026-01-01T00:00:00Z',
    unifiedId: 'gpt-4o',
  },
  {
    id: 'model-2',
    providerId: 'provider-1',
    modelName: 'gpt-4o-mini',
    enabled: true,
    createdAt: '2026-01-02T00:00:00Z',
    unifiedId: 'gpt-4o-mini',
  },
]

const mockFilteredModels: Model[] = [
  {
    id: 'model-3',
    providerId: 'provider-2',
    modelName: 'claude-sonnet-4-5',
    enabled: true,
    createdAt: '2026-02-01T00:00:00Z',
    unifiedId: 'claude-sonnet-4-5',
  },
]

const mockCreatedModel: Model = {
  id: 'model-4',
  providerId: 'provider-1',
  modelName: 'gpt-4.1',
  enabled: true,
  createdAt: '2026-03-01T00:00:00Z',
  unifiedId: 'gpt-4.1',
}

// MSW handlers
const handlers = [
  http.get('/api/models', ({ request }) => {
    const url = new URL(request.url)
    const providerId = url.searchParams.get('provider_id')
    if (providerId === 'provider-2') {
      return HttpResponse.json(mockFilteredModels)
    }
    return HttpResponse.json(mockModels)
  }),
  http.post('/api/models', async ({ request }) => {
    const body = (await request.json()) as Record<string, unknown>
    return HttpResponse.json({
      ...mockCreatedModel,
      ...body,
    })
  }),
  http.put('/api/models/:id', async ({ request, params }) => {
    const body = (await request.json()) as Record<string, unknown>
    const existing = mockModels.find((m) => m.id === params['id'])
    return HttpResponse.json({ ...existing, ...body })
  }),
  http.delete('/api/models/: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('useModels', () => {
  it('fetches model list', async () => {
    const { result } = renderHook(() => useModels(), {
      wrapper: createWrapper(),
    })

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

    expect(result.current.data).toEqual(mockModels)
    expect(result.current.data).toHaveLength(2)
    expect(result.current.data![0]!.modelName).toBe('gpt-4o')
  })

  it('with providerId passes it to API and returns filtered models', async () => {
    const { result } = renderHook(() => useModels('provider-2'), {
      wrapper: createWrapper(),
    })

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

    expect(result.current.data).toEqual(mockFilteredModels)
    expect(result.current.data).toHaveLength(1)
    expect(result.current.data![0]!.modelName).toBe('claude-sonnet-4-5')
  })
})

describe('useCreateModel', () => {
  it('calls API and invalidates model 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(() => useCreateModel(), {
      wrapper: Wrapper,
    })

    const input: CreateModelInput = {
      id: 'model-4',
      providerId: 'provider-1',
      modelName: 'gpt-4.1',
      enabled: true,
    }

    result.current.mutate(input)

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

    expect(result.current.data).toMatchObject({
      id: 'model-4',
      modelName: 'gpt-4.1',
    })
    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['models'] })
    expect(MessagePlugin.success).toHaveBeenCalledWith('模型创建成功')
  })

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

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

    const input: CreateModelInput = {
      id: 'model-4',
      providerId: 'provider-1',
      modelName: 'gpt-4.1',
      enabled: true,
    }

    result.current.mutate(input)

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

describe('useUpdateModel', () => {
  it('calls API and invalidates model 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(() => useUpdateModel(), {
      wrapper: Wrapper,
    })

    const input: UpdateModelInput = { modelName: 'gpt-4o-updated' }

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

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

    expect(result.current.data).toMatchObject({
      modelName: 'gpt-4o-updated',
    })
    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['models'] })
    expect(MessagePlugin.success).toHaveBeenCalledWith('模型更新成功')
  })

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

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

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

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

describe('useDeleteModel', () => {
  it('calls API and invalidates model 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(() => useDeleteModel(), {
      wrapper: Wrapper,
    })

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

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

    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['models'] })
    expect(MessagePlugin.success).toHaveBeenCalledWith('模型删除成功')
  })

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

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

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

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