/**
 * Tabata Program Store
 * Handles program selection, progression tracking, and sequential unlocking
 * for the 4 tabata programs (debutant, intermediaire, avance, bureau)
 */

import { create } from 'zustand'
import { persist } from 'zustand/middleware'
import AsyncStorage from '@react-native-async-storage/async-storage'
import type {
  TabataProgramId,
  TabataProgramProgress,
} from '../types/program'
import type { TabataProgram, TabataSession } from '../types/program'
import { getTabataProgramById, getAllTabataPrograms, getTabataSessionById } from '../data/tabata'

// ─── Types ─────────────────────────────────────────────────────

interface TabataProgramState {
  // Program selection
  selectedProgramId: TabataProgramId | null

  // Progress tracking for each program
  programsProgress: Record<TabataProgramId, TabataProgramProgress>

  // Actions
  selectProgram: (programId: TabataProgramId) => void
  completeSession: (programId: TabataProgramId, sessionId: string) => void
  resetProgram: (programId: TabataProgramId) => void
  changeProgram: (programId: TabataProgramId) => void

  // Getters
  getCurrentSession: (programId: TabataProgramId) => TabataSession | null
  getNextSession: (programId: TabataProgramId) => TabataSession | null
  isWeekUnlocked: (programId: TabataProgramId, weekNumber: number) => boolean
  isSessionUnlocked: (programId: TabataProgramId, sessionId: string) => boolean
  getProgramCompletion: (programId: TabataProgramId) => number
  getTotalSessionsCompleted: () => number
  getProgramStatus: (programId: TabataProgramId) => 'not-started' | 'in-progress' | 'completed'
  getRecommendedNext: () => { programId: TabataProgramId; session: TabataSession } | null
  getProgram: (programId: TabataProgramId) => TabataProgram | undefined
}

// ─── Helpers ───────────────────────────────────────────────────

const PROGRAM_IDS: TabataProgramId[] = ['debutant', 'intermediaire', 'avance', 'bureau']

const createInitialProgress = (programId: TabataProgramId): TabataProgramProgress => ({
  programId,
  currentWeek: 1,
  currentSessionIndex: 0,
  completedSessionIds: [],
  isProgramCompleted: false,
  startDate: undefined,
  lastSessionDate: undefined,
})

const createAllProgress = (): Record<TabataProgramId, TabataProgramProgress> => {
  const result = {} as Record<TabataProgramId, TabataProgramProgress>
  for (const id of PROGRAM_IDS) {
    result[id] = createInitialProgress(id)
  }
  return result
}

/** Get sessions for a specific week of a program */
const getWeekSessions = (program: TabataProgram, weekNumber: number): TabataSession[] => {
  const week = program.weeks.find(w => w.weekNumber === weekNumber)
  return week?.sessions ?? []
}

// ─── Store ─────────────────────────────────────────────────────

export const useTabataProgramStore = create<TabataProgramState>()(
  persist(
    (set, get) => ({
      // Initial state
      selectedProgramId: null,
      programsProgress: createAllProgress(),

      // Select a program to start
      selectProgram: (programId) => {
        const state = get()
        const progress = state.programsProgress[programId]

        if (progress.completedSessionIds.length === 0 && !progress.startDate) {
          set((state) => ({
            selectedProgramId: programId,
            programsProgress: {
              ...state.programsProgress,
              [programId]: {
                ...progress,
                startDate: new Date().toISOString(),
              },
            },
          }))
        } else {
          set({ selectedProgramId: programId })
        }
      },

      // Complete a session and advance progress
      completeSession: (programId, sessionId) => {
        const state = get()
        const progress = state.programsProgress[programId]
        const program = getTabataProgramById(programId)

        if (!program) return

        // Avoid duplicate completions
        if (progress.completedSessionIds.includes(sessionId)) return

        const newCompletedIds = [...progress.completedSessionIds, sessionId]

        // Find current session position
        let newWeek = progress.currentWeek
        let newSessionIndex = progress.currentSessionIndex

        const currentWeekSessions = getWeekSessions(program, progress.currentWeek)
        const isLastSessionOfWeek = progress.currentSessionIndex >= currentWeekSessions.length - 1

        if (isLastSessionOfWeek && progress.currentWeek < program.durationWeeks) {
          // Move to next week
          newWeek = progress.currentWeek + 1
          newSessionIndex = 0
        } else if (!isLastSessionOfWeek) {
          // Move to next session in same week
          newSessionIndex = progress.currentSessionIndex + 1
        }

        // Check if program is completed
        const isProgramCompleted = newCompletedIds.length >= program.totalSessions

        set((state) => ({
          programsProgress: {
            ...state.programsProgress,
            [programId]: {
              ...progress,
              completedSessionIds: newCompletedIds,
              currentWeek: newWeek,
              currentSessionIndex: newSessionIndex,
              isProgramCompleted,
              lastSessionDate: new Date().toISOString(),
            },
          },
        }))
      },

      // Reset a program to start over
      resetProgram: (programId) => {
        set((state) => ({
          programsProgress: {
            ...state.programsProgress,
            [programId]: createInitialProgress(programId),
          },
          selectedProgramId: state.selectedProgramId === programId ? null : state.selectedProgramId,
        }))
      },

      // Change to a different program
      changeProgram: (programId) => {
        set({ selectedProgramId: programId })
      },

      // Get the current session for a program
      getCurrentSession: (programId) => {
        const state = get()
        const progress = state.programsProgress[programId]
        const program = getTabataProgramById(programId)

        if (!program || !progress) return null

        const week = program.weeks.find(w => w.weekNumber === progress.currentWeek)
        if (!week) return null

        return week.sessions[progress.currentSessionIndex] ?? null
      },

      // Get the next session
      getNextSession: (programId) => {
        const state = get()
        const progress = state.programsProgress[programId]
        const program = getTabataProgramById(programId)

        if (!program || !progress) return null

        const currentWeek = program.weeks.find(w => w.weekNumber === progress.currentWeek)
        if (!currentWeek) return null

        if (progress.currentSessionIndex < currentWeek.sessions.length - 1) {
          return currentWeek.sessions[progress.currentSessionIndex + 1]
        }

        if (progress.currentWeek < program.durationWeeks) {
          const nextWeek = program.weeks.find(w => w.weekNumber === progress.currentWeek + 1)
          return nextWeek?.sessions[0] ?? null
        }

        return null
      },

      // Check if a week is unlocked
      isWeekUnlocked: (programId, weekNumber) => {
        if (weekNumber === 1) return true

        const state = get()
        const progress = state.programsProgress[programId]
        const program = getTabataProgramById(programId)

        if (!program) return false

        const previousWeek = program.weeks.find(w => w.weekNumber === weekNumber - 1)
        if (!previousWeek) return false

        const previousWeekSessionIds = previousWeek.sessions.map(s => s.id)
        return previousWeekSessionIds.every(id => progress.completedSessionIds.includes(id))
      },

      // Check if a specific session is unlocked
      isSessionUnlocked: (programId, sessionId) => {
        const state = get()
        const progress = state.programsProgress[programId]
        const program = getTabataProgramById(programId)

        if (!program || !progress) return false

        for (const week of program.weeks) {
          const sessionIndex = week.sessions.findIndex(s => s.id === sessionId)
          if (sessionIndex === -1) continue

          // Week must be unlocked
          if (!get().isWeekUnlocked(programId, week.weekNumber)) return false

          // If completed, always accessible
          if (progress.completedSessionIds.includes(sessionId)) return true

          // Must be at or before current position
          if (week.weekNumber === progress.currentWeek) {
            return sessionIndex <= progress.currentSessionIndex
          }

          if (week.weekNumber < progress.currentWeek) return true
        }

        return false
      },

      // Get completion percentage
      getProgramCompletion: (programId) => {
        const state = get()
        const progress = state.programsProgress[programId]
        const program = getTabataProgramById(programId)

        if (!progress || !program) return 0

        return Math.round((progress.completedSessionIds.length / program.totalSessions) * 100)
      },

      // Get total sessions completed across all programs
      getTotalSessionsCompleted: () => {
        const state = get()
        return Object.values(state.programsProgress).reduce(
          (total, progress) => total + progress.completedSessionIds.length,
          0,
        )
      },

      // Get program status
      getProgramStatus: (programId) => {
        const state = get()
        const progress = state.programsProgress[programId]

        if (!progress || progress.completedSessionIds.length === 0) return 'not-started'
        if (progress.isProgramCompleted) return 'completed'
        return 'in-progress'
      },

      // Get recommended next session
      getRecommendedNext: () => {
        const state = get()

        // If a program is selected, recommend current session
        if (state.selectedProgramId) {
          const session = get().getCurrentSession(state.selectedProgramId)
          if (session) {
            return { programId: state.selectedProgramId, session }
          }
        }

        // Find any in-progress program
        for (const programId of PROGRAM_IDS) {
          const status = get().getProgramStatus(programId)
          if (status === 'in-progress') {
            const session = get().getCurrentSession(programId)
            if (session) return { programId, session }
          }
        }

        return null
      },

      // Get program data
      getProgram: (programId) => {
        return getTabataProgramById(programId)
      },
    }),
    {
      name: 'tabatafit-kine-program-storage',
      version: 2,
      storage: {
        getItem: async (name) => {
          const value = await AsyncStorage.getItem(name)
          return value ? JSON.parse(value) : null
        },
        setItem: async (name, value) => {
          await AsyncStorage.setItem(name, JSON.stringify(value))
        },
        removeItem: async (name) => {
          await AsyncStorage.removeItem(name)
        },
      },
      migrate: (persisted, version): TabataProgramState => {
        // Migrate from v1 (old 3-program model) to v2 (tabata 4-program model)
        if (version === undefined || version < 2) {
          // Old format had upper-body/lower-body/full-body keys — reset entirely
          return {
            selectedProgramId: null,
            programsProgress: createAllProgress(),
          } as TabataProgramState
        }
        return persisted as TabataProgramState
      },
    },
  )
)
