import { ref, computed } from 'vue'
import i18next from 'i18next'
import { sessionRepository } from '../db/repositories/sessionRepository.js'
import { comboRepository } from '../db/repositories/comboRepository.js'
import { exerciseRepository } from '../db/repositories/exerciseRepository.js'
import { executionLogRepository } from '../db/repositories/executionLogRepository.js'
import { settingsRepository } from '../db/repositories/settingsRepository.js'
import { useTimer } from './useTimer.js'
import { useAudioCues } from './useAudioCues.js'
import { useSpeech } from './useSpeech.js'

export function useSessionExecution(sessionId) {
  const sessionTitle = ref('')
  const queue = ref([])
  const currentIndex = ref(0)
  const logId = ref(null)
  const showFinalRound = ref(false)
  const sessionDone = ref(false)
  const loading = ref(true)
  const error = ref(null)

  const currentReps = ref(0)
  const currentWeight = ref(0.0)
  const currentSide = ref('left')

  // Timer state
  const timerPhase = ref(null) // 'work' | 'rest' | 'amrap' | null
  const isAmrapTimeUp = ref(false)
  const currentAmrapComboId = ref(null)

  const {
    remaining: timerRemaining,
    isPaused: timerPaused,
    start: startTimer,
    pause: pauseTimer,
    resume: resumeTimer,
    stop: stopTimer,
  } = useTimer()
  const { playBip, playBIP } = useAudioCues()
  const { init: initSpeech, speak, cancel: cancelSpeech } = useSpeech()
  const audioVolume = ref(0.5)
  let preAnnouncedIndex = null

  const currentStep = computed(() => queue.value[currentIndex.value] || null)
  const nextStep = computed(() => queue.value[currentIndex.value + 1] || null)
  const totalSteps = computed(() => queue.value.length)
  const timerActive = computed(() => timerPhase.value !== null && !isAmrapTimeUp.value)

  function _announceStep(step) {
    speak(step.exerciseTitle, { volume: audioVolume.value })
  }

  function _preAnnounceNext() {
    const next = nextStep.value
    if (!next) return
    speak(i18next.t('execution.voice.next', { title: next.exerciseTitle }), {
      volume: audioVolume.value,
    })
    preAnnouncedIndex = currentIndex.value + 1
  }

  function loadStepState() {
    const step = currentStep.value
    if (!step) return
    currentReps.value = step.prefillReps
    currentWeight.value = step.prefillWeight
    currentSide.value = step.prefillSide
    if (preAnnouncedIndex === currentIndex.value) {
      preAnnouncedIndex = null
    } else {
      _announceStep(step)
    }
    _startStepTimer(step)
  }

  function _emomSounds(remaining) {
    if (remaining === 30 || remaining === 2 || remaining === 1) playBip(audioVolume.value)
    if (remaining === 10) _preAnnounceNext()
  }

  function _amrapLastMinuteSounds(remaining) {
    if (remaining === 30 || remaining === 2 || remaining === 1) playBip(audioVolume.value)
  }

  function _tabataCountdown(remaining, totalTime) {
    const half = Math.floor(totalTime / 2)
    if (remaining === half) playBip(audioVolume.value)
    if (remaining === 2 || remaining === 1) playBip(audioVolume.value)
  }

  function _startStepTimer(step) {
    if (step.comboType === 'EMOM') {
      timerPhase.value = 'work'
      startTimer(60, {
        onTick: _emomSounds,
        onExpire: _onEmomExpire,
      })
    } else if (step.comboType === 'TABATA') {
      _startWorkPhase(step)
    } else if (step.comboType === 'AMRAP') {
      _maybeStartAmrapTimer(step)
    } else {
      timerPhase.value = null
    }
  }

  function _onEmomExpire() {
    playBIP(audioVolume.value)
    const step = currentStep.value
    if (!step) return
    timerPhase.value = null
    _saveStepToLog(step).then(() => _advance())
  }

  function _startWorkPhase(step) {
    timerPhase.value = 'work'
    playBIP(audioVolume.value)
    startTimer(step.workTime, {
      onTick: (r) => _tabataCountdown(r, step.workTime),
      onExpire: () => _startRestPhase(step),
    })
  }

  function _startRestPhase(step) {
    timerPhase.value = 'rest'
    playBIP(audioVolume.value)
    _preAnnounceNext()
    startTimer(step.restTime, {
      onTick: (r) => _tabataCountdown(r, step.restTime),
      onExpire: async () => {
        timerPhase.value = null
        if (step.isFinalComboStep) {
          showFinalRound.value = true
        } else {
          await _advance()
        }
      },
    })
  }

  function _maybeStartAmrapTimer(step) {
    if (step.comboId === currentAmrapComboId.value) return
    currentAmrapComboId.value = step.comboId
    isAmrapTimeUp.value = false
    timerPhase.value = 'amrap'

    const durationSec = step.amrapMinutes * 60
    const halfDuration = Math.round(durationSec / 2)
    let lastElapsedMin = 0

    startTimer(durationSec, {
      onTick: (r) => {
        if (r <= 60) {
          _amrapLastMinuteSounds(r)
        } else {
          const elapsed = Math.floor((durationSec - r) / 60)
          const isNewMinute = elapsed > lastElapsedMin
          if (isNewMinute) lastElapsedMin = elapsed
          if (r === halfDuration) {
            playBIP(audioVolume.value)
          } else if (isNewMinute) {
            playBip(audioVolume.value)
          }
        }
      },
      onExpire: () => {
        playBIP(audioVolume.value)
        timerPhase.value = null
        isAmrapTimeUp.value = true
      },
    })
  }

  async function init() {
    loading.value = true
    error.value = null
    try {
      const volumeStr = await settingsRepository.get('audio_volume')
      const parsed = parseInt(volumeStr, 10)
      audioVolume.value = isNaN(parsed) ? 0.5 : parsed / 100
      await initSpeech()

      const session = await sessionRepository.getById(sessionId)
      if (!session) throw new Error('Session not found')
      sessionTitle.value = session.title

      const prefillMap = await executionLogRepository.getLastItemsBySession(sessionId)

      const steps = []
      for (const item of session.items) {
        if (item.item_type === 'exercise') {
          const ex = await exerciseRepository.getById(item.item_id)
          const pf = prefillMap[item.item_id] || {}
          steps.push({
            exerciseId: item.item_id,
            exerciseTitle: item.title,
            prefillReps: pf.reps_done ?? item.repetitions,
            prefillWeight: pf.weight_used ?? item.weight,
            prefillSide: pf.side || 'left',
            comboId: null,
            comboTitle: null,
            comboType: null,
            workTime: null,
            restTime: null,
            amrapMinutes: null,
            roundNumber: null,
            totalRounds: null,
            asymmetric: ex?.asymmetric || false,
            alternate: ex?.alternate || false,
            isFinalComboStep: false,
            comboExercises: null,
          })
        } else if (item.item_type === 'combo') {
          const combo = await comboRepository.getById(item.item_id)
          if (!combo || !combo.exercises.length) continue

          const comboType = combo.type || 'NONE'
          const timerCfg = combo.timer_config || {}
          const workTime = timerCfg.workTime ?? 20
          const restTime = timerCfg.restTime ?? 10
          const amrapMinutes = item.repetitions || 1

          // AMRAP: treat repetitions as minutes, unroll just 1 round in queue
          // EMOM: repetitions = total duration in minutes; rounds = floor(minutes / exercises)
          // TABATA/NONE: repetitions = number of full combo rounds
          const totalRounds =
            comboType === 'AMRAP'
              ? 1
              : comboType === 'EMOM' && combo.exercises.length > 0
                ? Math.max(1, Math.floor((item.repetitions || 1) / combo.exercises.length))
                : item.repetitions || 1

          for (let round = 1; round <= totalRounds; round++) {
            for (let ei = 0; ei < combo.exercises.length; ei++) {
              const ex = combo.exercises[ei]
              const pf = prefillMap[ex.exercise_id] || {}
              const isLast = ei === combo.exercises.length - 1 && round === totalRounds
              steps.push({
                exerciseId: ex.exercise_id,
                exerciseTitle: ex.title,
                prefillReps: pf.reps_done ?? ex.default_reps,
                prefillWeight: pf.weight_used ?? ex.default_weight,
                prefillSide: pf.side || 'left',
                comboId: item.item_id,
                comboTitle: combo.title,
                comboType,
                workTime,
                restTime,
                amrapMinutes,
                roundNumber: round,
                totalRounds,
                asymmetric: ex.asymmetric || false,
                alternate: ex.alternate || false,
                isFinalComboStep: isLast,
                comboExercises: combo.exercises,
              })
            }
          }
        }
      }
      queue.value = steps

      logId.value = await executionLogRepository.create({
        session_id: sessionId,
        started_at: new Date().toISOString(),
        finished_at: null,
      })

      loadStepState()
    } catch (e) {
      error.value = e.message
    } finally {
      loading.value = false
    }
  }

  async function _saveStepToLog(step) {
    await executionLogRepository.addItem({
      log_id: logId.value,
      exercise_id: step.exerciseId,
      combo_id: step.comboId || null,
      round_number: step.roundNumber || null,
      reps_done: currentReps.value,
      weight_used: currentWeight.value,
      side: step.asymmetric ? currentSide.value : null,
      completed: true,
      timestamp: new Date().toISOString(),
    })
    step.actualReps = currentReps.value
    step.actualWeight = currentWeight.value
    step.actualSide = currentSide.value
  }

  async function markDone() {
    const step = currentStep.value
    if (!step || sessionDone.value) return

    // Stop running step timer (EMOM or TABATA work phase)
    if (step.comboType === 'EMOM' || (step.comboType === 'TABATA' && timerPhase.value === 'work')) {
      stopTimer()
      timerPhase.value = null
    }

    await _saveStepToLog(step)

    if (step.comboType === 'TABATA') {
      _startRestPhase(step)
    } else if (step.comboType === 'EMOM') {
      // EMOM always auto-advances, no final-round screen
      await _advance()
    } else if (step.comboType === 'AMRAP') {
      if (step.isFinalComboStep) {
        if (isAmrapTimeUp.value) {
          await _advance()
        } else {
          // Auto-cycle: add one more round silently
          _addAmrapRound()
        }
      } else {
        await _advance()
      }
    } else {
      if (step.isFinalComboStep) {
        showFinalRound.value = true
      } else {
        await _advance()
      }
    }
  }

  function _appendRound({ resetFinalRound = false } = {}) {
    const step = currentStep.value
    if (!step?.comboId || !step?.comboExercises) return

    const newRound = step.roundNumber + 1
    const exercises = step.comboExercises
    const lastRoundSteps = queue.value.filter(
      (s) => s.comboId === step.comboId && s.roundNumber === step.roundNumber,
    )

    const newSteps = exercises.map((ex, i) => {
      const prev = lastRoundSteps.find((s) => s.exerciseId === ex.exercise_id)
      return {
        exerciseId: ex.exercise_id,
        exerciseTitle: ex.title,
        prefillReps: prev?.actualReps ?? prev?.prefillReps ?? ex.default_reps,
        prefillWeight: prev?.actualWeight ?? prev?.prefillWeight ?? ex.default_weight,
        prefillSide: prev?.actualSide || 'left',
        comboId: step.comboId,
        comboTitle: step.comboTitle,
        comboType: step.comboType,
        workTime: step.workTime,
        restTime: step.restTime,
        amrapMinutes: step.amrapMinutes,
        roundNumber: newRound,
        totalRounds: newRound,
        asymmetric: ex.asymmetric || false,
        alternate: ex.alternate || false,
        isFinalComboStep: i === exercises.length - 1,
        comboExercises: exercises,
      }
    })

    queue.value.splice(currentIndex.value + 1, 0, ...newSteps)
    if (resetFinalRound) showFinalRound.value = false
    currentIndex.value++
    loadStepState()
  }

  function _addAmrapRound() {
    _appendRound()
  }

  async function continueAfterCombo() {
    showFinalRound.value = false
    await _advance()
  }

  async function continueAfterAmrap() {
    isAmrapTimeUp.value = false
    currentAmrapComboId.value = null
    stopTimer()
    // Skip any remaining steps belonging to this AMRAP combo
    const comboId = currentStep.value?.comboId
    while (
      currentIndex.value < queue.value.length &&
      queue.value[currentIndex.value]?.comboId === comboId
    ) {
      currentIndex.value++
    }
    if (currentIndex.value >= queue.value.length) {
      await _finish()
    } else {
      loadStepState()
    }
  }

  function addOneMoreRound() {
    _appendRound({ resetFinalRound: true })
  }

  function togglePause() {
    if (timerPaused.value) {
      resumeTimer()
    } else {
      pauseTimer()
    }
  }

  function goBack() {
    if (sessionDone.value || currentIndex.value <= 0) return
    stopTimer()
    timerPhase.value = null
    showFinalRound.value = false
    isAmrapTimeUp.value = false
    currentAmrapComboId.value = null
    currentIndex.value--
    loadStepState()
  }

  async function _advance() {
    currentIndex.value++
    if (currentIndex.value >= queue.value.length) {
      stopTimer()
      timerPhase.value = null
      await _finish()
    } else {
      // If the new step is a different AMRAP combo, stop the current AMRAP timer
      const nextStep = currentStep.value
      if (nextStep?.comboType !== 'AMRAP' || nextStep?.comboId !== currentAmrapComboId.value) {
        if (currentAmrapComboId.value) {
          stopTimer()
          currentAmrapComboId.value = null
          timerPhase.value = null
        }
      }
      loadStepState()
    }
  }

  async function _finish() {
    cancelSpeech()
    await executionLogRepository.update(logId.value, {
      finished_at: new Date().toISOString(),
      status: 'completed',
    })
    sessionDone.value = true
  }

  async function exit() {
    if (sessionDone.value) return
    stopTimer()
    timerPhase.value = null
    cancelSpeech()
    await executionLogRepository.update(logId.value, {
      finished_at: new Date().toISOString(),
      status: 'aborted',
    })
    sessionDone.value = true
  }

  return {
    sessionTitle,
    queue,
    currentIndex,
    currentStep,
    nextStep,
    totalSteps,
    showFinalRound,
    sessionDone,
    loading,
    error,
    currentReps,
    currentWeight,
    currentSide,
    timerPhase,
    timerRemaining,
    timerPaused,
    timerActive,
    isAmrapTimeUp,
    init,
    markDone,
    continueAfterCombo,
    continueAfterAmrap,
    addOneMoreRound,
    togglePause,
    goBack,
    exit,
  }
}
