/**
 * Tests for DataProviderContext - data-provider-context.tsx
 *
 * TDD RED phase: Write failing tests first.
 */

import { describe, it, expect, vi } from 'vitest'
import { render, screen } from '@testing-library/react'
import { DataProviderProvider, useDataProvider } from '../../context/data-provider-context'
import type { DataProvider } from '../../types'

// Helper component to consume data provider context
function DataProviderConsumer({ onProvider }: { onProvider: (dp: DataProvider) => void }) {
  const dataProvider = useDataProvider()
  onProvider(dataProvider)
  return <div data-testid="has-provider">true</div>
}

function createMockDataProvider(overrides: Partial<DataProvider> = {}): DataProvider {
  return {
    getList: vi.fn().mockResolvedValue({ data: [], total: 0 }),
    getOne: vi.fn().mockResolvedValue({ data: { id: '1' } }),
    getMany: vi.fn().mockResolvedValue({ data: [] }),
    create: vi.fn().mockResolvedValue({ data: { id: '1' } }),
    update: vi.fn().mockResolvedValue({ data: { id: '1' } }),
    delete: vi.fn().mockResolvedValue({ data: { id: '1' } }),
    ...overrides,
  }
}

describe('DataProviderContext', () => {
  describe('DataProviderProvider', () => {
    it('should provide the data provider to children', () => {
      const mockProvider = createMockDataProvider()
      let receivedProvider: DataProvider | undefined

      render(
        <DataProviderProvider dataProvider={mockProvider}>
          <DataProviderConsumer onProvider={(dp) => { receivedProvider = dp }} />
        </DataProviderProvider>
      )

      expect(screen.getByTestId('has-provider').textContent).toBe('true')
      expect(receivedProvider).toBe(mockProvider)
    })

    it('should allow calling data provider methods', async () => {
      const mockGetList = vi.fn().mockResolvedValue({ data: [{ id: '1', name: 'Test' }], total: 1 })
      const mockProvider = createMockDataProvider({ getList: mockGetList })
      let receivedProvider: DataProvider | undefined

      render(
        <DataProviderProvider dataProvider={mockProvider}>
          <DataProviderConsumer onProvider={(dp) => { receivedProvider = dp }} />
        </DataProviderProvider>
      )

      const result = await receivedProvider!.getList('users', { pagination: { page: 1, perPage: 10 } })

      expect(mockGetList).toHaveBeenCalledWith('users', { pagination: { page: 1, perPage: 10 } })
      expect(result.data).toEqual([{ id: '1', name: 'Test' }])
      expect(result.total).toBe(1)
    })

    it('should support all DataProvider methods', async () => {
      const mockProvider = createMockDataProvider()
      let receivedProvider: DataProvider | undefined

      render(
        <DataProviderProvider dataProvider={mockProvider}>
          <DataProviderConsumer onProvider={(dp) => { receivedProvider = dp }} />
        </DataProviderProvider>
      )

      // Call all methods
      await receivedProvider!.getList('users')
      await receivedProvider!.getOne('users', { id: '1' })
      await receivedProvider!.getMany('users', { ids: ['1', '2'] })
      await receivedProvider!.create('users', { data: { name: 'New' } })
      await receivedProvider!.update('users', { id: '1', data: { name: 'Updated' } })
      await receivedProvider!.delete('users', { id: '1' })

      expect(mockProvider.getList).toHaveBeenCalled()
      expect(mockProvider.getOne).toHaveBeenCalled()
      expect(mockProvider.getMany).toHaveBeenCalled()
      expect(mockProvider.create).toHaveBeenCalled()
      expect(mockProvider.update).toHaveBeenCalled()
      expect(mockProvider.delete).toHaveBeenCalled()
    })
  })

  describe('useDataProvider', () => {
    it('should throw error when used outside DataProviderProvider', () => {
      const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})

      expect(() => {
        render(<DataProviderConsumer onProvider={() => {}} />)
      }).toThrow('useDataProvider must be used within a DataProviderProvider')

      consoleError.mockRestore()
    })
  })
})
