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 { useProductPrices } from '../../../features/products/hooks/useProductPrices'
import { useAddProductPrice } from '../../../features/products/hooks/useAddProductPrice'
import type { ProductPrice, AddProductPriceResponse } from '../../../features/products/types'

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

vi.mock('sonner', () => ({
  toast: { success: vi.fn(), error: vi.fn() },
}))

const mockPrice: ProductPrice = {
  id: 1,
  productId: 1,
  price: 500,
  priceValidFrom: '2026-04-01',
  priceValidTo: null,
  isActive: true,
}

const mockResponse: AddProductPriceResponse = {
  created: { id: 2, productId: 1, price: 700, priceValidFrom: '2026-05-01', priceValidTo: null, isActive: true },
  closed: { id: 1, productId: 1, price: 500, priceValidFrom: '2026-04-01', priceValidTo: '2026-04-30', isActive: false },
}

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 { qc, wrapper: ({ children }: { children: React.ReactNode }) =>
    React.createElement(QueryClientProvider, { client: qc }, children) }
}

describe('useProductPrices', () => {
  it('fetches prices for productId and returns data', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, () => HttpResponse.json([mockPrice])),
    )
    const { qc, wrapper } = makeWrapper()
    const { result } = renderHook(() => useProductPrices(1), { wrapper })
    await waitFor(() => expect(result.current.isSuccess).toBe(true))
    expect(result.current.data).toEqual([mockPrice])
    // Verify caching: queryKey should be ['products', 1, 'prices']
    expect(qc.getQueryState(['products', 1, 'prices'])).toBeDefined()
  })

  it('is disabled when productId is 0', async () => {
    // No server handler — if the query fired it would fail with unhandled request
    const { wrapper } = makeWrapper()
    const { result } = renderHook(() => useProductPrices(0), { wrapper })
    // Should never enter loading/success
    expect(result.current.isFetching).toBe(false)
    expect(result.current.data).toBeUndefined()
  })
})

describe('useAddProductPrice', () => {
  it('calls POST and invalidates product prices queries on success', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, () => HttpResponse.json([mockPrice])),
      http.post(`${API_URL}/api/v1/admin/products/1/prices`, () =>
        HttpResponse.json(mockResponse, { status: 201 }),
      ),
    )
    const { qc, wrapper } = makeWrapper()
    const invalidateSpy = vi.spyOn(qc, 'invalidateQueries')

    const { result } = renderHook(() => useAddProductPrice(1), { wrapper })

    await act(async () => {
      result.current.mutate({ price: 700, priceValidFrom: '2026-05-01' })
    })
    await waitFor(() => expect(result.current.isSuccess).toBe(true))
    expect(invalidateSpy).toHaveBeenCalledWith({ queryKey: ['products', 1, 'prices'] })
  })

  it('returns error state on 409', async () => {
    server.use(
      http.post(`${API_URL}/api/v1/admin/products/1/prices`, () =>
        HttpResponse.json({ error: 'product_price_forward_only' }, { status: 409 }),
      ),
    )
    const { wrapper } = makeWrapper()
    const { result } = renderHook(() => useAddProductPrice(1), { wrapper })
    await act(async () => {
      result.current.mutate({ price: 100, priceValidFrom: '2026-04-19' })
    })
    await waitFor(() => expect(result.current.isError).toBe(true))
  })
})
