import { describe, it, expect, beforeAll, afterAll, afterEach, vi } from 'vitest'
import { renderHook, waitFor, act } from '@testing-library/react'
import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import React from 'react'
import { useProducts } from '../../../features/products/hooks/useProducts'
import { useCreateProduct } from '../../../features/products/hooks/useCreateProduct'
import { useUpdateProduct } from '../../../features/products/hooks/useUpdateProduct'
import { useDeactivateProduct } from '../../../features/products/hooks/useDeactivateProduct'
import type { ProductListItem, PagedResult } from '../../../features/products/types'

const API_URL = 'http://localhost:5000'

const mockItem: ProductListItem = {
  id: 1,
  nombre: 'Clasificado',
  medioId: 1,
  productTypeId: 2,
  rubroId: null,
  basePrice: 50,
  priceDurationDays: null,
  isActive: true,
}

const mockPaged: PagedResult<ProductListItem> = {
  items: [mockItem],
  page: 1,
  pageSize: 20,
  total: 1,
}

const server = setupServer()

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

function makeWrapper() {
  const qc = new QueryClient({
    defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
  })
  return ({ children }: { children: React.ReactNode }) =>
    React.createElement(QueryClientProvider, { client: qc }, children)
}

describe('useProducts', () => {
  it('returns paged data on success', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/products`, () => HttpResponse.json(mockPaged)),
    )
    const { result } = renderHook(() => useProducts(), { wrapper: makeWrapper() })
    await waitFor(() => expect(result.current.isSuccess).toBe(true))
    expect(result.current.data).toEqual(mockPaged)
  })

  it('returns error state on failure', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/products`, () =>
        HttpResponse.json({ error: 'server_error' }, { status: 500 }),
      ),
    )
    const { result } = renderHook(() => useProducts(), { wrapper: makeWrapper() })
    await waitFor(() => expect(result.current.isError).toBe(true))
  })
})

describe('useCreateProduct', () => {
  it('calls create and invalidates products queries on success', async () => {
    server.use(
      http.post(`${API_URL}/api/v1/admin/products`, () =>
        HttpResponse.json(mockItem, { status: 201 }),
      ),
    )
    const qc = new QueryClient({
      defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
    })
    const invalidateSpy = vi.spyOn(qc, 'invalidateQueries')
    const wrapper = ({ children }: { children: React.ReactNode }) =>
      React.createElement(QueryClientProvider, { client: qc }, children)

    const { result } = renderHook(() => useCreateProduct(), { wrapper })
    await act(async () => {
      result.current.mutate({
        nombre: 'Nuevo Producto',
        medioId: 1,
        productTypeId: 2,
        basePrice: 100,
      })
    })
    await waitFor(() => expect(result.current.isSuccess).toBe(true))
    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['products'] })
  })
})

describe('useUpdateProduct', () => {
  it('calls update and invalidates products queries on success', async () => {
    server.use(
      http.put(`${API_URL}/api/v1/admin/products/1`, () =>
        HttpResponse.json(mockItem),
      ),
    )
    const qc = new QueryClient({
      defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
    })
    const invalidateSpy = vi.spyOn(qc, 'invalidateQueries')
    const wrapper = ({ children }: { children: React.ReactNode }) =>
      React.createElement(QueryClientProvider, { client: qc }, children)

    const { result } = renderHook(() => useUpdateProduct(), { wrapper })
    await act(async () => {
      result.current.mutate({ id: 1, data: { nombre: 'Modificado', basePrice: 200 } })
    })
    await waitFor(() => expect(result.current.isSuccess).toBe(true))
    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['products'] })
  })
})

describe('useDeactivateProduct', () => {
  it('calls deactivate and invalidates products queries on success', async () => {
    server.use(
      http.delete(`${API_URL}/api/v1/admin/products/1`, () =>
        new HttpResponse(null, { status: 204 }),
      ),
    )
    const qc = new QueryClient({
      defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
    })
    const invalidateSpy = vi.spyOn(qc, 'invalidateQueries')
    const wrapper = ({ children }: { children: React.ReactNode }) =>
      React.createElement(QueryClientProvider, { client: qc }, children)

    const { result } = renderHook(() => useDeactivateProduct(), { wrapper })
    await act(async () => {
      result.current.mutate(1)
    })
    await waitFor(() => expect(result.current.isSuccess).toBe(true))
    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['products'] })
  })
})
