import { describe, it, expect, vi, beforeEach } from 'vitest'
import { enableSync, syncWorkoutSession, deleteSyncedData, getSyncState, hasSyncedData, isAuthenticated } from '../../shared/services/sync'
import { supabase } from '@/src/shared/supabase'

vi.mock('@/src/shared/supabase', () => ({
  supabase: {
    auth: {
      signInAnonymously: vi.fn(),
      getUser: vi.fn(),
      getSession: vi.fn(),
      signOut: vi.fn(),
    },
    from: vi.fn(),
  },
}))

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

  describe('enableSync', () => {
    it('should return success with userId when sync enabled', async () => {
      vi.mocked(supabase.auth.signInAnonymously).mockResolvedValue({
        data: { user: { id: 'test-user-id' } },
        error: null,
      } as any)
      
      vi.mocked(supabase.from).mockReturnValue({
        insert: vi.fn().mockResolvedValue({ error: null }),
      } as any)

      const profileData = {
        name: 'Test User',
        fitnessLevel: 'intermediate' as const,
        goal: 'strength' as const,
        weeklyFrequency: 3 as const,
        barriers: ['no-time'],
        onboardingCompletedAt: new Date().toISOString(),
      }

      const workoutHistory: Array<{
        workoutId: string
        completedAt: string
        durationSeconds: number
        caloriesBurned: number
      }> = []

      const result = await enableSync(profileData, workoutHistory)

      expect(result.success).toBe(true)
      expect(result.userId).toBe('test-user-id')
    })

    it('should return error when auth fails', async () => {
      vi.mocked(supabase.auth.signInAnonymously).mockResolvedValue({
        data: { user: null },
        error: { message: 'Auth failed' },
      } as any)

      const result = await enableSync({
        name: 'Test',
        fitnessLevel: 'beginner',
        goal: 'cardio',
        weeklyFrequency: 3,
        barriers: [],
        onboardingCompletedAt: new Date().toISOString(),
      }, [])

      expect(result.success).toBe(false)
      expect(result.error).toBeDefined()
    })

    it('should sync workout history when provided', async () => {
      vi.mocked(supabase.auth.signInAnonymously).mockResolvedValue({
        data: { user: { id: 'test-user-id' } },
        error: null,
      } as any)

      const mockInsert = vi.fn().mockResolvedValue({ error: null })
      vi.mocked(supabase.from).mockReturnValue({
        insert: mockInsert,
      } as any)

      const profileData = {
        name: 'Test User',
        fitnessLevel: 'intermediate' as const,
        goal: 'strength' as const,
        weeklyFrequency: 3 as const,
        barriers: ['no-time'],
        onboardingCompletedAt: new Date().toISOString(),
      }

      const workoutHistory = [
        {
          workoutId: 'workout-1',
          completedAt: new Date().toISOString(),
          durationSeconds: 240,
          caloriesBurned: 45,
        },
      ]

      await enableSync(profileData, workoutHistory)

      expect(mockInsert).toHaveBeenCalled()
    })
  })

  describe('syncWorkoutSession', () => {
    it('should return error when no authenticated user', async () => {
      vi.mocked(supabase.auth.getUser).mockResolvedValue({
        data: { user: null },
      } as any)

      const result = await syncWorkoutSession({
        workoutId: 'workout-1',
        completedAt: new Date().toISOString(),
        durationSeconds: 240,
        caloriesBurned: 45,
      })

      expect(result.success).toBe(false)
      expect(result.error).toBe('No authenticated user')
    })

    it('should sync session when user is authenticated', async () => {
      vi.mocked(supabase.auth.getUser).mockResolvedValue({
        data: { user: { id: 'test-user-id' } },
      } as any)

      vi.mocked(supabase.from).mockReturnValue({
        insert: vi.fn().mockResolvedValue({ error: null }),
      } as any)

      const result = await syncWorkoutSession({
        workoutId: 'workout-1',
        completedAt: new Date().toISOString(),
        durationSeconds: 240,
        caloriesBurned: 45,
      })

      expect(result.success).toBe(true)
    })
  })

  describe('deleteSyncedData', () => {
    it('should return error when no authenticated user', async () => {
      vi.mocked(supabase.auth.getUser).mockResolvedValue({
        data: { user: null },
      } as any)

      const result = await deleteSyncedData()

      expect(result.success).toBe(false)
      expect(result.error).toBe('No authenticated user')
    })

    it('should delete all user data when authenticated', async () => {
      vi.mocked(supabase.auth.getUser).mockResolvedValue({
        data: { user: { id: 'test-user-id' } },
      } as any)

      const mockDelete = vi.fn().mockReturnThis()
      const mockEq = vi.fn().mockResolvedValue({ error: null })
      
      vi.mocked(supabase.from).mockReturnValue({
        delete: mockDelete,
        eq: mockEq,
      } as any)

      vi.mocked(supabase.auth.signOut).mockResolvedValue({ error: null } as any)

      const result = await deleteSyncedData()

      expect(result.success).toBe(true)
      expect(mockDelete).toHaveBeenCalledTimes(3)
    })
  })

  describe('getSyncState', () => {
    it('should return never-synced when no session', async () => {
      vi.mocked(supabase.auth.getSession).mockResolvedValue({
        data: { session: null },
      } as any)

      const state = await getSyncState()

      expect(state.status).toBe('never-synced')
      expect(state.userId).toBeNull()
    })

    it('should return synced state with user info', async () => {
      vi.mocked(supabase.auth.getSession).mockResolvedValue({
        data: { session: { user: { id: 'test-user-id' } } },
      } as any)

      vi.mocked(supabase.from).mockReturnValue({
        select: vi.fn().mockReturnThis(),
        eq: vi.fn().mockReturnThis(),
        order: vi.fn().mockReturnThis(),
        limit: vi.fn().mockResolvedValue({ data: [] }),
      } as any)

      const state = await getSyncState()

      expect(state.status).toBe('synced')
      expect(state.userId).toBe('test-user-id')
    })
  })

  describe('hasSyncedData', () => {
    it('should return false when no session', async () => {
      vi.mocked(supabase.auth.getSession).mockResolvedValue({
        data: { session: null },
      } as any)

      const result = await hasSyncedData()

      expect(result).toBe(false)
    })

    it('should return true when session exists', async () => {
      vi.mocked(supabase.auth.getSession).mockResolvedValue({
        data: { session: { user: { id: 'test-user-id' } } },
      } as any)

      const result = await hasSyncedData()

      expect(result).toBe(true)
    })
  })

  describe('isAuthenticated', () => {
    it('should return false when no session', async () => {
      vi.mocked(supabase.auth.getSession).mockResolvedValue({
        data: { session: null },
      } as any)

      const result = await isAuthenticated()

      expect(result).toBe(false)
    })

    it('should return true when session exists', async () => {
      vi.mocked(supabase.auth.getSession).mockResolvedValue({
        data: { session: { user: { id: 'test-user-id' } } },
      } as any)

      const result = await isAuthenticated()

      expect(result).toBe(true)
    })
  })
})
