import type { ErrorDocsResponse } from '@/models/datasets'
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { retryErrorDocs } from '@/service/datasets'
import { useDatasetErrorDocs } from '@/service/knowledge/use-dataset'
import RetryButton from '../index-failed'

const mockRefetch = vi.fn()

vi.mock('@/service/knowledge/use-dataset', () => ({
  useDatasetErrorDocs: vi.fn(),
}))

vi.mock('@/service/datasets', () => ({
  retryErrorDocs: vi.fn(),
}))

const mockUseDatasetErrorDocs = vi.mocked(useDatasetErrorDocs)
const mockRetryErrorDocs = vi.mocked(retryErrorDocs)

afterEach(() => {
  cleanup()
  vi.clearAllMocks()
})

// Helper to create mock query result
const createMockQueryResult = (
  data: ErrorDocsResponse | undefined,
  isLoading: boolean,
) => ({
  data,
  isLoading,
  refetch: mockRefetch,
  // Required query result properties
  error: null,
  isError: false,
  isFetched: true,
  isFetching: false,
  isSuccess: !isLoading && !!data,
  status: isLoading ? 'pending' : 'success',
  dataUpdatedAt: Date.now(),
  errorUpdatedAt: 0,
  failureCount: 0,
  failureReason: null,
  errorUpdateCount: 0,
  isLoadingError: false,
  isPaused: false,
  isPlaceholderData: false,
  isPending: isLoading,
  isRefetchError: false,
  isRefetching: false,
  isStale: false,
  fetchStatus: 'idle',
  promise: Promise.resolve(data as ErrorDocsResponse),
  isFetchedAfterMount: true,
  isInitialLoading: false,
}) as unknown as ReturnType<typeof useDatasetErrorDocs>

describe('RetryButton (IndexFailed)', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    mockRefetch.mockResolvedValue({})
  })

  describe('Rendering', () => {
    it('should render nothing when loading', () => {
      mockUseDatasetErrorDocs.mockReturnValue(
        createMockQueryResult(undefined, true),
      )

      const { container } = render(<RetryButton datasetId="test-dataset" />)
      expect(container.firstChild).toBeNull()
    })

    it('should render nothing when no error documents', () => {
      mockUseDatasetErrorDocs.mockReturnValue(
        createMockQueryResult({ total: 0, data: [] }, false),
      )

      const { container } = render(<RetryButton datasetId="test-dataset" />)
      expect(container.firstChild).toBeNull()
    })

    it('should render StatusWithAction when error documents exist', () => {
      mockUseDatasetErrorDocs.mockReturnValue(
        createMockQueryResult({
          total: 3,
          data: [
            { id: 'doc1' },
            { id: 'doc2' },
            { id: 'doc3' },
          ] as ErrorDocsResponse['data'],
        }, false),
      )

      render(<RetryButton datasetId="test-dataset" />)
      expect(screen.getByText(/retry/i)).toBeInTheDocument()
    })

    it('should display error count in description', () => {
      mockUseDatasetErrorDocs.mockReturnValue(
        createMockQueryResult({
          total: 5,
          data: [{ id: 'doc1' }] as ErrorDocsResponse['data'],
        }, false),
      )

      render(<RetryButton datasetId="test-dataset" />)
      expect(screen.getByText(/5/)).toBeInTheDocument()
    })
  })

  describe('Props', () => {
    it('should pass datasetId to useDatasetErrorDocs', () => {
      mockUseDatasetErrorDocs.mockReturnValue(
        createMockQueryResult({ total: 0, data: [] }, false),
      )

      render(<RetryButton datasetId="my-dataset-id" />)
      expect(mockUseDatasetErrorDocs).toHaveBeenCalledWith('my-dataset-id')
    })
  })

  describe('User Interactions', () => {
    it('should call retryErrorDocs when retry button is clicked', async () => {
      mockUseDatasetErrorDocs.mockReturnValue(
        createMockQueryResult({
          total: 2,
          data: [{ id: 'doc1' }, { id: 'doc2' }] as ErrorDocsResponse['data'],
        }, false),
      )

      mockRetryErrorDocs.mockResolvedValue({ result: 'success' })

      render(<RetryButton datasetId="test-dataset" />)

      const retryButton = screen.getByText(/retry/i)
      fireEvent.click(retryButton)

      await waitFor(() => {
        expect(mockRetryErrorDocs).toHaveBeenCalledWith({
          datasetId: 'test-dataset',
          document_ids: ['doc1', 'doc2'],
        })
      })

      // Wait for all state updates to complete
      await waitFor(() => {
        expect(mockRefetch).toHaveBeenCalled()
      })
    })

    it('should refetch error docs after successful retry', async () => {
      mockUseDatasetErrorDocs.mockReturnValue(
        createMockQueryResult({
          total: 1,
          data: [{ id: 'doc1' }] as ErrorDocsResponse['data'],
        }, false),
      )

      mockRetryErrorDocs.mockResolvedValue({ result: 'success' })

      render(<RetryButton datasetId="test-dataset" />)

      const retryButton = screen.getByText(/retry/i)
      fireEvent.click(retryButton)

      await waitFor(() => {
        expect(mockRefetch).toHaveBeenCalled()
      })
    })

    it('should disable button while retrying', async () => {
      mockUseDatasetErrorDocs.mockReturnValue(
        createMockQueryResult({
          total: 1,
          data: [{ id: 'doc1' }] as ErrorDocsResponse['data'],
        }, false),
      )

      let resolveRetry: ((value: { result: 'success' }) => void) | undefined
      mockRetryErrorDocs.mockImplementation(() => new Promise((resolve) => {
        resolveRetry = resolve
      }))

      render(<RetryButton datasetId="test-dataset" />)

      const retryButton = screen.getByText(/retry/i)
      fireEvent.click(retryButton)

      // Button should show disabled styling during retry
      await waitFor(() => {
        const button = screen.getByText(/retry/i)
        expect(button).toHaveClass('cursor-not-allowed')
        expect(button).toHaveClass('text-text-disabled')
      })

      resolveRetry?.({ result: 'success' })
      await waitFor(() => {
        expect(mockRefetch).toHaveBeenCalled()
      })
    })
  })

  describe('State Management', () => {
    it('should transition to error state when retry fails', async () => {
      mockUseDatasetErrorDocs.mockReturnValue(
        createMockQueryResult({
          total: 1,
          data: [{ id: 'doc1' }] as ErrorDocsResponse['data'],
        }, false),
      )

      mockRetryErrorDocs.mockResolvedValue({ result: 'fail' })

      render(<RetryButton datasetId="test-dataset" />)

      const retryButton = screen.getByText(/retry/i)
      fireEvent.click(retryButton)

      // Wait for retry to complete and state to update
      await waitFor(() => {
        expect(mockRetryErrorDocs).toHaveBeenCalled()
      })

      // Button should still be visible after failed retry
      await waitFor(() => {
        expect(screen.getByText(/retry/i)).toBeInTheDocument()
      })
    })

    it('should transition to success state when total becomes 0', async () => {
      const { rerender } = render(<RetryButton datasetId="test-dataset" />)

      // Initially has errors
      mockUseDatasetErrorDocs.mockReturnValue(
        createMockQueryResult({
          total: 1,
          data: [{ id: 'doc1' }] as ErrorDocsResponse['data'],
        }, false),
      )

      rerender(<RetryButton datasetId="test-dataset" />)
      expect(screen.getByText(/retry/i)).toBeInTheDocument()

      // Now no errors
      mockUseDatasetErrorDocs.mockReturnValue(
        createMockQueryResult({ total: 0, data: [] }, false),
      )

      rerender(<RetryButton datasetId="test-dataset" />)

      await waitFor(() => {
        expect(screen.queryByText(/retry/i)).not.toBeInTheDocument()
      })
    })
  })

  describe('Edge Cases', () => {
    it('should handle empty data array', () => {
      mockUseDatasetErrorDocs.mockReturnValue(
        createMockQueryResult({ total: 0, data: [] }, false),
      )

      const { container } = render(<RetryButton datasetId="test-dataset" />)
      expect(container.firstChild).toBeNull()
    })

    it('should handle undefined data by showing error state', () => {
      // When data is undefined but not loading, the component shows error state
      // because errorDocs?.total is not strictly equal to 0
      mockUseDatasetErrorDocs.mockReturnValue(
        createMockQueryResult(undefined, false),
      )

      render(<RetryButton datasetId="test-dataset" />)
      // Component renders with undefined count
      expect(screen.getByText(/retry/i)).toBeInTheDocument()
    })

    it('should handle retry with empty document list', async () => {
      mockUseDatasetErrorDocs.mockReturnValue(
        createMockQueryResult({ total: 1, data: [] }, false),
      )

      mockRetryErrorDocs.mockResolvedValue({ result: 'success' })

      render(<RetryButton datasetId="test-dataset" />)

      const retryButton = screen.getByText(/retry/i)
      fireEvent.click(retryButton)

      await waitFor(() => {
        expect(mockRetryErrorDocs).toHaveBeenCalledWith({
          datasetId: 'test-dataset',
          document_ids: [],
        })
      })

      // Wait for all state updates to complete
      await waitFor(() => {
        expect(mockRefetch).toHaveBeenCalled()
      })
    })
  })
})
