import React from 'react'

import {renderHook, waitFor} from '@testing-library/react'
import {describe, expect, it, vi} from 'vitest'

import {MinisQueryProvider} from './MinisQueryProvider'
import {useShopActionQuery} from './useShopActionQuery'

describe('useShopActionQuery', () => {
  const wrapper = ({children}: {children: React.ReactNode}) => (
    <MinisQueryProvider>{children}</MinisQueryProvider>
  )

  describe('Successful Data Fetching', () => {
    it('fetches and returns data', async () => {
      const mockAction = vi.fn().mockResolvedValue({
        ok: true,
        data: {data: {id: '1', name: 'Test Product'}},
      })

      const {result} = renderHook(
        () => useShopActionQuery(['test-1'], mockAction, {}),
        {wrapper}
      )

      await waitFor(() => {
        expect(result.current.loading).toBe(false)
      })

      expect(result.current.data).toEqual({id: '1', name: 'Test Product'})
      expect(result.current.error).toBeNull()
      expect(mockAction).toHaveBeenCalledWith({})
    })

    it('returns null when data is undefined', async () => {
      const mockAction = vi.fn().mockResolvedValue({
        ok: true,
        data: {data: undefined},
      })

      const {result} = renderHook(
        () => useShopActionQuery(['test-undefined'], mockAction, {}),
        {wrapper}
      )

      await waitFor(() => {
        expect(result.current.loading).toBe(false)
      })

      expect(result.current.data).toBeNull()
    })
  })

  describe('Error Handling', () => {
    it('handles action errors (ok: false)', async () => {
      const mockAction = vi.fn().mockRejectedValue(new Error('API Error'))

      const {result} = renderHook(
        () => useShopActionQuery(['test-action-error'], mockAction, {}),
        {wrapper}
      )

      // Wait for loading to complete (after retries)
      await waitFor(
        () => {
          expect(result.current.loading).toBe(false)
        },
        {timeout: 3000}
      )

      expect(result.current.data).toBeNull()
      expect(result.current.error).toBeInstanceOf(Error)
      expect(result.current.error?.message).toBe('API Error')
    })
  })

  describe('Skip Parameter', () => {
    it('does not fetch when skip is true', async () => {
      const mockAction = vi.fn()

      renderHook(
        () => useShopActionQuery(['test-skip'], mockAction, {}, {skip: true}),
        {wrapper}
      )

      // Wait a bit to ensure no fetch happens
      await new Promise(resolve => setTimeout(resolve, 100))

      expect(mockAction).not.toHaveBeenCalled()
    })

    it('fetches when skip is false', async () => {
      const mockAction = vi.fn().mockResolvedValue({
        ok: true,
        data: {data: 'skip-false-data'},
      })

      const {result} = renderHook(
        () =>
          useShopActionQuery(
            ['test-noskip-query'],
            mockAction,
            {},
            {skip: false}
          ),
        {wrapper}
      )

      await waitFor(() => {
        expect(result.current.data).toBe('skip-false-data')
      })

      expect(mockAction).toHaveBeenCalled()
    })
  })

  describe('API Contract', () => {
    it('returns expected shape', async () => {
      const mockAction = vi.fn().mockResolvedValue({
        ok: true,
        data: {data: 'contract-test'},
      })

      const {result} = renderHook(
        () => useShopActionQuery(['test-contract'], mockAction, {}),
        {wrapper}
      )

      await waitFor(() => {
        expect(result.current.loading).toBe(false)
      })

      // Verify all expected properties exist
      expect(result.current).toHaveProperty('data')
      expect(result.current).toHaveProperty('loading')
      expect(result.current).toHaveProperty('error')
      expect(result.current).toHaveProperty('refetch')

      // Verify types
      expect(typeof result.current.loading).toBe('boolean')
      expect(typeof result.current.refetch).toBe('function')
    })
  })

  describe('fetchPolicy Parameter', () => {
    it('passes fetchPolicy to action', async () => {
      const mockAction = vi.fn().mockResolvedValue({
        ok: true,
        data: {data: 'test'},
      })

      renderHook(
        () =>
          useShopActionQuery(['test-network-only'], mockAction, {
            fetchPolicy: 'network-only',
          }),
        {wrapper}
      )

      await waitFor(() => {
        expect(mockAction).toHaveBeenCalledWith({fetchPolicy: 'network-only'})
      })
    })

    it('passes cache-first fetchPolicy to action', async () => {
      const mockAction = vi.fn().mockResolvedValue({
        ok: true,
        data: {data: 'test'},
      })

      renderHook(
        () =>
          useShopActionQuery(['test-cache-first'], mockAction, {
            fetchPolicy: 'cache-first',
          }),
        {wrapper}
      )

      await waitFor(() => {
        expect(mockAction).toHaveBeenCalledWith({fetchPolicy: 'cache-first'})
      })
    })
  })
})
