import { describe, it, expect, vi, beforeEach } from 'vitest'
import { useQuery } from '@tanstack/react-query'
import { dataService } from '../../shared/data/dataService'
import {
  queryKeys,
  useWorkouts,
  useWorkout,
  useWorkoutsByCategory,
  useWorkoutsByTrainer,
  useFeaturedWorkouts,
  usePopularWorkouts,
  useTrainers,
  useTrainer,
  useCollections,
  useCollection,
  usePrograms,
} from '../../shared/hooks/useSupabaseData'

// Mock dataService
vi.mock('../../shared/data/dataService', () => ({
  dataService: {
    getAllWorkouts: vi.fn().mockResolvedValue([
      { id: 'w1', title: 'Workout 1' },
      { id: 'w2', title: 'Workout 2' },
      { id: 'w3', title: 'Workout 3' },
    ]),
    getWorkoutById: vi.fn().mockResolvedValue({ id: 'w1', title: 'Workout 1' }),
    getWorkoutsByCategory: vi.fn().mockResolvedValue([{ id: 'w1', title: 'Workout 1' }]),
    getWorkoutsByTrainer: vi.fn().mockResolvedValue([{ id: 'w2', title: 'Workout 2' }]),
    getFeaturedWorkouts: vi.fn().mockResolvedValue([{ id: 'w1', title: 'Featured' }]),
    getAllTrainers: vi.fn().mockResolvedValue([{ id: 't1', name: 'Trainer 1' }]),
    getTrainerById: vi.fn().mockResolvedValue({ id: 't1', name: 'Trainer 1' }),
    getAllCollections: vi.fn().mockResolvedValue([{ id: 'c1', title: 'Collection 1' }]),
    getCollectionById: vi.fn().mockResolvedValue({ id: 'c1', title: 'Collection 1' }),
    getAllPrograms: vi.fn().mockResolvedValue([{ id: 'p1', title: 'Program 1' }]),
  },
}))

// Mock React Query — capture the options passed to useQuery
const mockUseQuery = vi.fn((options: any) => ({
  data: undefined,
  isLoading: true,
  error: null,
  ...options,
}))

vi.mock('@tanstack/react-query', () => ({
  useQuery: (options: any) => mockUseQuery(options),
}))

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

  describe('queryKeys', () => {
    it('should have correct static keys', () => {
      expect(queryKeys.workouts).toBe('workouts')
      expect(queryKeys.trainers).toBe('trainers')
      expect(queryKeys.collections).toBe('collections')
      expect(queryKeys.programs).toBe('programs')
    })

    it('should generate correct workout key', () => {
      expect(queryKeys.workout('abc')).toEqual(['workouts', 'abc'])
    })

    it('should generate correct workoutsByCategory key', () => {
      expect(queryKeys.workoutsByCategory('full-body')).toEqual([
        'workouts',
        'category',
        'full-body',
      ])
    })

    it('should generate correct workoutsByTrainer key', () => {
      expect(queryKeys.workoutsByTrainer('trainer-1')).toEqual([
        'workouts',
        'trainer',
        'trainer-1',
      ])
    })

    it('should have correct featuredWorkouts key', () => {
      expect(queryKeys.featuredWorkouts).toEqual(['workouts', 'featured'])
    })

    it('should generate correct trainer key', () => {
      expect(queryKeys.trainer('t1')).toEqual(['trainers', 't1'])
    })

    it('should generate correct collection key', () => {
      expect(queryKeys.collection('c1')).toEqual(['collections', 'c1'])
    })
  })

  describe('useWorkouts', () => {
    it('should use correct queryKey and staleTime', () => {
      useWorkouts()

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          queryKey: ['workouts'],
          staleTime: 300000,
        })
      )
    })
  })

  describe('useWorkout', () => {
    it('should be disabled when id is undefined', () => {
      useWorkout(undefined)

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          enabled: false,
        })
      )
    })

    it('should be enabled when id is provided', () => {
      useWorkout('workout-123')

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          queryKey: ['workouts', 'workout-123'],
          enabled: true,
        })
      )
    })

    it('should use empty string as fallback key when id is undefined', () => {
      useWorkout(undefined)

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          queryKey: ['workouts', ''],
        })
      )
    })
  })

  describe('useWorkoutsByCategory', () => {
    it('should pass correct queryKey', () => {
      useWorkoutsByCategory('full-body')

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          queryKey: ['workouts', 'category', 'full-body'],
          enabled: true,
        })
      )
    })

    it('should be disabled with empty category', () => {
      useWorkoutsByCategory('')

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          enabled: false,
        })
      )
    })
  })

  describe('useWorkoutsByTrainer', () => {
    it('should pass correct queryKey', () => {
      useWorkoutsByTrainer('trainer-1')

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          queryKey: ['workouts', 'trainer', 'trainer-1'],
          enabled: true,
        })
      )
    })

    it('should be disabled with empty trainerId', () => {
      useWorkoutsByTrainer('')

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          enabled: false,
        })
      )
    })
  })

  describe('useFeaturedWorkouts', () => {
    it('should use correct queryKey', () => {
      useFeaturedWorkouts()

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          queryKey: ['workouts', 'featured'],
        })
      )
    })
  })

  describe('usePopularWorkouts', () => {
    it('should default to count 8', () => {
      usePopularWorkouts()

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          queryKey: ['workouts', 'popular', 8],
        })
      )
    })

    it('should accept custom count', () => {
      usePopularWorkouts(5)

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          queryKey: ['workouts', 'popular', 5],
        })
      )
    })

    it('queryFn should slice workouts correctly', async () => {
      usePopularWorkouts(2)

      const lastCall = mockUseQuery.mock.calls[mockUseQuery.mock.calls.length - 1][0]
      const result = await lastCall.queryFn()

      expect(dataService.getAllWorkouts).toHaveBeenCalled()
      expect(result).toHaveLength(2)
      expect(result[0].id).toBe('w1')
      expect(result[1].id).toBe('w2')
    })
  })

  describe('useTrainers', () => {
    it('should use correct queryKey', () => {
      useTrainers()

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          queryKey: ['trainers'],
        })
      )
    })
  })

  describe('useTrainer', () => {
    it('should be disabled when id is undefined', () => {
      useTrainer(undefined)

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          enabled: false,
        })
      )
    })

    it('should be enabled when id is provided', () => {
      useTrainer('t1')

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          queryKey: ['trainers', 't1'],
          enabled: true,
        })
      )
    })
  })

  describe('useCollections', () => {
    it('should use correct queryKey', () => {
      useCollections()

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          queryKey: ['collections'],
        })
      )
    })
  })

  describe('useCollection', () => {
    it('should be disabled when id is undefined', () => {
      useCollection(undefined)

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          enabled: false,
        })
      )
    })

    it('should be enabled when id is provided', () => {
      useCollection('c1')

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          queryKey: ['collections', 'c1'],
          enabled: true,
        })
      )
    })
  })

  describe('usePrograms', () => {
    it('should use correct queryKey', () => {
      usePrograms()

      expect(mockUseQuery).toHaveBeenCalledWith(
        expect.objectContaining({
          queryKey: ['programs'],
        })
      )
    })
  })

  describe('staleTime consistency', () => {
    it('all hooks should have 5 minute staleTime', () => {
      mockUseQuery.mockClear()

      useWorkouts()
      useFeaturedWorkouts()
      useTrainers()
      useCollections()
      usePrograms()

      const calls = mockUseQuery.mock.calls
      calls.forEach((call: any[]) => {
        expect(call[0].staleTime).toBe(1000 * 60 * 5)
      })
    })
  })
})
