/**
 * Tests for useAugmentation hook.
 *
 * TDD Phase 1: RED - Write tests first, then implement to pass.
 */

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { renderHook, waitFor } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { augmentationApi } from '../api/endpoints/augmentation'
import { useAugmentation } from './useAugmentation'
import type { ReactNode } from 'react'

// Mock the API
vi.mock('../api/endpoints/augmentation', () => ({
  augmentationApi: {
    getTypes: vi.fn(),
    getPresets: vi.fn(),
    preview: vi.fn(),
    previewConfig: vi.fn(),
    createBatch: vi.fn(),
  },
}))

// Test wrapper with QueryClient
const createWrapper = () => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
      },
    },
  })
  return ({ children }: { children: ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  )
}

describe('useAugmentation', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

  describe('getTypes', () => {
    it('should fetch augmentation types', async () => {
      const mockTypes = {
        augmentation_types: [
          {
            name: 'gaussian_noise',
            description: 'Adds Gaussian noise',
            affects_geometry: false,
            stage: 'noise',
            default_params: { mean: 0, std: 15 },
          },
          {
            name: 'perspective_warp',
            description: 'Applies perspective warp',
            affects_geometry: true,
            stage: 'geometric',
            default_params: { max_warp: 0.02 },
          },
        ],
      }
      vi.mocked(augmentationApi.getTypes).mockResolvedValueOnce(mockTypes)

      const { result } = renderHook(() => useAugmentation(), {
        wrapper: createWrapper(),
      })

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

      expect(result.current.augmentationTypes).toHaveLength(2)
      expect(result.current.augmentationTypes[0].name).toBe('gaussian_noise')
    })

    it('should handle error when fetching types', async () => {
      vi.mocked(augmentationApi.getTypes).mockRejectedValueOnce(new Error('Network error'))

      const { result } = renderHook(() => useAugmentation(), {
        wrapper: createWrapper(),
      })

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

      expect(result.current.typesError).toBeTruthy()
    })
  })

  describe('getPresets', () => {
    it('should fetch augmentation presets', async () => {
      const mockPresets = {
        presets: [
          { name: 'conservative', description: 'Safe augmentations' },
          { name: 'moderate', description: 'Balanced augmentations' },
          { name: 'aggressive', description: 'Strong augmentations' },
        ],
      }
      vi.mocked(augmentationApi.getTypes).mockResolvedValueOnce({ augmentation_types: [] })
      vi.mocked(augmentationApi.getPresets).mockResolvedValueOnce(mockPresets)

      const { result } = renderHook(() => useAugmentation(), {
        wrapper: createWrapper(),
      })

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

      expect(result.current.presets).toHaveLength(3)
      expect(result.current.presets[0].name).toBe('conservative')
    })
  })

  describe('preview', () => {
    it('should preview single augmentation', async () => {
      const mockPreview = {
        preview_url: 'data:image/png;base64,xxx',
        original_url: 'data:image/png;base64,yyy',
        applied_params: { std: 15 },
      }
      vi.mocked(augmentationApi.getTypes).mockResolvedValueOnce({ augmentation_types: [] })
      vi.mocked(augmentationApi.getPresets).mockResolvedValueOnce({ presets: [] })
      vi.mocked(augmentationApi.preview).mockResolvedValueOnce(mockPreview)

      const { result } = renderHook(() => useAugmentation(), {
        wrapper: createWrapper(),
      })

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

      // Call preview mutation
      result.current.preview({
        documentId: 'doc-123',
        augmentationType: 'gaussian_noise',
        params: { std: 15 },
        page: 1,
      })

      await waitFor(() => {
        expect(augmentationApi.preview).toHaveBeenCalledWith(
          'doc-123',
          { augmentation_type: 'gaussian_noise', params: { std: 15 } },
          1
        )
      })
    })

    it('should track preview loading state', async () => {
      vi.mocked(augmentationApi.getTypes).mockResolvedValueOnce({ augmentation_types: [] })
      vi.mocked(augmentationApi.getPresets).mockResolvedValueOnce({ presets: [] })
      vi.mocked(augmentationApi.preview).mockImplementation(
        () => new Promise((resolve) => setTimeout(resolve, 100))
      )

      const { result } = renderHook(() => useAugmentation(), {
        wrapper: createWrapper(),
      })

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

      expect(result.current.isPreviewing).toBe(false)

      result.current.preview({
        documentId: 'doc-123',
        augmentationType: 'gaussian_noise',
        params: {},
        page: 1,
      })

      // State update happens asynchronously
      await waitFor(() => {
        expect(result.current.isPreviewing).toBe(true)
      })
    })
  })

  describe('createBatch', () => {
    it('should create augmented dataset', async () => {
      const mockResponse = {
        task_id: 'task-123',
        status: 'pending',
        message: 'Augmentation task queued',
        estimated_images: 100,
      }
      vi.mocked(augmentationApi.getTypes).mockResolvedValueOnce({ augmentation_types: [] })
      vi.mocked(augmentationApi.getPresets).mockResolvedValueOnce({ presets: [] })
      vi.mocked(augmentationApi.createBatch).mockResolvedValueOnce(mockResponse)

      const { result } = renderHook(() => useAugmentation(), {
        wrapper: createWrapper(),
      })

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

      result.current.createBatch({
        dataset_id: 'dataset-123',
        config: {
          gaussian_noise: { enabled: true, probability: 0.5, params: {} },
        },
        output_name: 'augmented-dataset',
        multiplier: 2,
      })

      await waitFor(() => {
        expect(augmentationApi.createBatch).toHaveBeenCalledWith({
          dataset_id: 'dataset-123',
          config: {
            gaussian_noise: { enabled: true, probability: 0.5, params: {} },
          },
          output_name: 'augmented-dataset',
          multiplier: 2,
        })
      })
    })
  })
})
