import { describe, it, expect, beforeEach, vi } from 'vitest'
import { useActivityStore, getWeeklyActivity } from '../../shared/stores/activityStore'
import type { WorkoutResult } from '../../shared/types'

const createWorkoutResult = (daysAgo: number, overrides?: Partial<WorkoutResult>): WorkoutResult => ({
  id: `result-${Date.now()}-${Math.random()}`,
  workoutId: 'test-workout',
  durationMinutes: 4,
  calories: 45,
  rounds: 8,
  completionRate: 1,
  completedAt: Date.now() - daysAgo * 86400000,
  ...overrides,
})

describe('activityStore', () => {
  beforeEach(async () => {
    await useActivityStore.persist.clearStorage()
    useActivityStore.setState({ history: [], streak: { current: 0, longest: 0 } })
    vi.clearAllMocks()
  })

  describe('initial state', () => {
    it('should have empty history and zero streak', () => {
      const state = useActivityStore.getState()
      
      expect(state.history).toEqual([])
      expect(state.streak).toEqual({ current: 0, longest: 0 })
    })
  })

  describe('addWorkoutResult', () => {
    it('should add workout to beginning of history', async () => {
      const store = useActivityStore.getState()
      const result1 = createWorkoutResult(2)
      const result2 = createWorkoutResult(1)
      
      await store.addWorkoutResult(result1)
      await store.addWorkoutResult(result2)
      
      const history = useActivityStore.getState().history
      expect(history).toHaveLength(2)
      expect(history[0].completedAt).toBeGreaterThan(history[1].completedAt)
    })

    it('should calculate streak after adding workout', async () => {
      const store = useActivityStore.getState()
      const todayResult = createWorkoutResult(0)
      
      await store.addWorkoutResult(todayResult)
      
      const streak = useActivityStore.getState().streak
      expect(streak.current).toBe(1)
      expect(streak.longest).toBe(1)
    })

    it('should build consecutive streak', async () => {
      const store = useActivityStore.getState()
      
      await store.addWorkoutResult(createWorkoutResult(2))
      await store.addWorkoutResult(createWorkoutResult(1))
      await store.addWorkoutResult(createWorkoutResult(0))
      
      const streak = useActivityStore.getState().streak
      expect(streak.current).toBe(3)
      expect(streak.longest).toBe(3)
    })
  })

  describe('getWorkoutHistory', () => {
    it('should return all workouts', async () => {
      const store = useActivityStore.getState()
      
      await store.addWorkoutResult(createWorkoutResult(2))
      await store.addWorkoutResult(createWorkoutResult(1))
      
      const history = store.getWorkoutHistory()
      expect(history).toHaveLength(2)
    })
  })
})

describe('streak calculation', () => {
  beforeEach(async () => {
    await useActivityStore.persist.clearStorage()
    useActivityStore.setState({ history: [], streak: { current: 0, longest: 0 } })
  })

  describe('empty history', () => {
    it('should return 0 streak for empty history', async () => {
      const store = useActivityStore.getState()
      
      expect(store.history).toEqual([])
      expect(store.streak.current).toBe(0)
      expect(store.streak.longest).toBe(0)
    })
  })

  describe('single workout', () => {
    it('should return streak 1 for single workout today', async () => {
      const store = useActivityStore.getState()
      
      await store.addWorkoutResult(createWorkoutResult(0))
      
      expect(useActivityStore.getState().streak.current).toBe(1)
    })

    it('should return streak 1 for single workout yesterday', async () => {
      const store = useActivityStore.getState()
      
      await store.addWorkoutResult(createWorkoutResult(1))
      
      expect(useActivityStore.getState().streak.current).toBe(1)
    })
  })

  describe('consecutive days', () => {
    it('should count 3-day streak correctly', async () => {
      const store = useActivityStore.getState()
      
      await store.addWorkoutResult(createWorkoutResult(2))
      await store.addWorkoutResult(createWorkoutResult(1))
      await store.addWorkoutResult(createWorkoutResult(0))
      
      const streak = useActivityStore.getState().streak
      expect(streak.current).toBe(3)
    })

    it('should count 7-day streak correctly', async () => {
      const store = useActivityStore.getState()
      
      for (let i = 6; i >= 0; i--) {
        await store.addWorkoutResult(createWorkoutResult(i))
      }
      
      const streak = useActivityStore.getState().streak
      expect(streak.current).toBe(7)
    })
  })

  describe('streak breaks', () => {
    beforeEach(async () => {
      await useActivityStore.persist.clearStorage()
      useActivityStore.setState({ history: [], streak: { current: 0, longest: 0 } })
    })

    it('should reset streak when gap exists', async () => {
      const store = useActivityStore.getState()
      
      await store.addWorkoutResult(createWorkoutResult(5))
      await store.addWorkoutResult(createWorkoutResult(4))
      await store.addWorkoutResult(createWorkoutResult(1))
      await store.addWorkoutResult(createWorkoutResult(0))
      
      const streak = useActivityStore.getState().streak
      expect(streak.current).toBe(2)
      expect(streak.longest).toBe(2)
    })

    it('should maintain longest streak even after current streak breaks', async () => {
      const store = useActivityStore.getState()
      
      await store.addWorkoutResult(createWorkoutResult(10))
      await store.addWorkoutResult(createWorkoutResult(9))
      await store.addWorkoutResult(createWorkoutResult(8))
      await store.addWorkoutResult(createWorkoutResult(5))
      await store.addWorkoutResult(createWorkoutResult(4))
      
      const streak = useActivityStore.getState().streak
      expect(streak.current).toBe(0)
      expect(streak.longest).toBe(3)
    })
  })

  describe('multiple workouts same day', () => {
    beforeEach(async () => {
      await useActivityStore.persist.clearStorage()
      useActivityStore.setState({ history: [], streak: { current: 0, longest: 0 } })
    })

    it('should count same-day workouts as 1 day', async () => {
      const store = useActivityStore.getState()
      const now = Date.now()
      
      // Add 3 workouts on the same day (1 hour apart)
      await store.addWorkoutResult({ ...createWorkoutResult(0), completedAt: now })
      await store.addWorkoutResult({ ...createWorkoutResult(0), completedAt: now + 3600000 })
      await store.addWorkoutResult({ ...createWorkoutResult(0), completedAt: now + 7200000 })
      
      const state = useActivityStore.getState()
      // All 3 workout entries are stored in history
      expect(state.history).toHaveLength(3)
      // But they count as 1 unique day for streak purposes
      expect(state.streak.current).toBe(1)
      expect(state.streak.longest).toBe(1)
    })
  })

  describe('streak expiration', () => {
    beforeEach(async () => {
      await useActivityStore.persist.clearStorage()
      useActivityStore.setState({ history: [], streak: { current: 0, longest: 0 } })
    })

    it('should reset streak if last workout was 2+ days ago', async () => {
      const store = useActivityStore.getState()
      
      await store.addWorkoutResult(createWorkoutResult(5))
      await store.addWorkoutResult(createWorkoutResult(4))
      await store.addWorkoutResult(createWorkoutResult(3))
      
      const streak = useActivityStore.getState().streak
      expect(streak.current).toBe(0)
      expect(streak.longest).toBe(3)
    })
  })
})

describe('getWeeklyActivity', () => {
  it('should return 7 days of activity', () => {
    const history: WorkoutResult[] = []
    const activity = getWeeklyActivity(history)
    
    expect(activity).toHaveLength(7)
    activity.forEach((day) => {
      expect(day).toHaveProperty('date')
      expect(day).toHaveProperty('completed')
      expect(day).toHaveProperty('workoutCount')
    })
  })

  it('should mark days with workouts as completed', () => {
    const today = Date.now()
    const history: WorkoutResult[] = [
      createWorkoutResult(0),
      createWorkoutResult(2),
    ]
    
    const activity = getWeeklyActivity(history)
    const completedDays = activity.filter((d) => d.completed)
    
    expect(completedDays.length).toBeGreaterThanOrEqual(1)
  })

  it('should count multiple workouts per day', () => {
    const today = Date.now()
    const history: WorkoutResult[] = [
      { ...createWorkoutResult(0), completedAt: today },
      { ...createWorkoutResult(0), completedAt: today + 3600000 },
    ]
    
    const activity = getWeeklyActivity(history)
    const todayActivity = activity.find((d) => d.workoutCount > 1)
    
    expect(todayActivity?.workoutCount).toBe(2)
  })

  it('should return all days with zero workouts for empty history', () => {
    const history: WorkoutResult[] = []
    const activity = getWeeklyActivity(history)
    
    activity.forEach((day) => {
      expect(day.completed).toBe(false)
      expect(day.workoutCount).toBe(0)
    })
  })
})
