import { defineStore } from 'pinia'
import { ref, computed, watch } from 'vue'
import { invoke } from '@tauri-apps/api/core'
import { emit, listen } from '@tauri-apps/api/event'
import { useSettingsStore } from './settings'
import { useProjectsStore } from './projects'
import { useAnnouncer } from '../composables/useAnnouncer'
import { audioEngine } from '../utils/audio'
import { handleInvokeError } from '../utils/errorHandler'
import type { TimeEntry } from './entries'

export type TimerState = 'STOPPED' | 'RUNNING' | 'PAUSED_IDLE' | 'PAUSED_APP' | 'PAUSED_MANUAL'

export interface TrackedApp {
  id?: number
  project_id: number
  exe_name: string
  exe_path?: string
  display_name?: string
}

export const useTimerStore = defineStore('timer', () => {
  // Core state
  const timerState = ref<TimerState>('STOPPED')
  const startTime = ref<Date | null>(null)
  const currentEntry = ref<TimeEntry | null>(null)
  const selectedProjectId = ref<number | null>(null)
  const selectedTaskId = ref<number | null>(null)
  const description = ref('')
  const billable = ref(1)
  const elapsedSeconds = ref(0)

  // Pause tracking
  const pausedAt = ref<number | null>(null)
  const totalPausedMs = ref(0)
  const idleStartedAt = ref<number | null>(null)

  // Tracked apps for current project
  const trackedApps = ref<TrackedApp[]>([])

  // Prompt visibility flags (consumed by Timer.vue)
  const showIdlePrompt = ref(false)
  const showAppPrompt = ref(false)
  const idleDurationSeconds = ref(0)

  // Save dialog state (safety net for no-project / long-timer)
  const showSaveDialog = ref(false)
  const saveDialogMode = ref<'no-project' | 'long-timer'>('no-project')
  const pendingStopDuration = ref(0)

  const { announce } = useAnnouncer()

  let displayIntervalId: number | null = null
  let monitorIntervalId: number | null = null
  let goalReachedFired = false
  let lastBreakReminderAt: number | null = null

  // Timeline recording state
  let currentTimelineEventId: number | null = null
  let lastTimelineExe: string = ''
  let lastTimelineTitle: string = ''
  let lastTimelineStartMs: number = 0

  function isTimelineEnabled(): boolean {
    const settingsStore = useSettingsStore()
    const globalEnabled = settingsStore.settings.timeline_recording === 'on'
    if (selectedProjectId.value) {
      const projectsStore = useProjectsStore()
      const project = projectsStore.projects.find(p => p.id === selectedProjectId.value)
      if (project?.timeline_override === 'on') return true
      if (project?.timeline_override === 'off') return false
    }
    return globalEnabled
  }

  async function closeTimelineEvent() {
    if (!currentTimelineEventId) return
    const now = new Date().toISOString()
    const duration = Math.floor((Date.now() - lastTimelineStartMs) / 1000)
    try {
      await invoke('update_timeline_event_ended', {
        id: currentTimelineEventId,
        endedAt: now,
        duration,
      })
    } catch (e) {
      console.error('Failed to close timeline event:', e)
    }
    currentTimelineEventId = null
  }

  function resetTimelineState() {
    currentTimelineEventId = null
    lastTimelineExe = ''
    lastTimelineTitle = ''
    lastTimelineStartMs = 0
  }

  // Derived
  const isRunning = computed(() => timerState.value === 'RUNNING')
  const isPaused = computed(() => timerState.value === 'PAUSED_IDLE' || timerState.value === 'PAUSED_APP' || timerState.value === 'PAUSED_MANUAL')
  const isStopped = computed(() => timerState.value === 'STOPPED')

  const formattedTime = computed(() => {
    const hours = Math.floor(elapsedSeconds.value / 3600)
    const minutes = Math.floor((elapsedSeconds.value % 3600) / 60)
    const seconds = elapsedSeconds.value % 60
    return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
  })

  function updateElapsed() {
    if (!startTime.value) return
    const now = Date.now()
    let paused = totalPausedMs.value
    if (pausedAt.value) {
      paused += now - pausedAt.value
    }
    elapsedSeconds.value = Math.floor((now - startTime.value.getTime() - paused) / 1000)
    emitTimerSync()

    if (timerState.value === 'RUNNING') {
      const settingsStore = useSettingsStore()

      // Goal reached check
      if (!goalReachedFired && settingsStore.settings.goals_enabled === 'true') {
        const dailyGoalHours = parseFloat(settingsStore.settings.daily_goal_hours) || 8
        const elapsedHours = elapsedSeconds.value / 3600
        if (elapsedHours >= dailyGoalHours) {
          goalReachedFired = true
          audioEngine.play('goal_reached')
          announce('Daily goal reached')
        }
      }

      // Break reminder check
      const reminderMin = parseInt(settingsStore.settings.reminder_interval) || 30
      if (reminderMin > 0) {
        const nowMs = Date.now()
        if (!lastBreakReminderAt) {
          lastBreakReminderAt = nowMs
        } else if (nowMs - lastBreakReminderAt >= reminderMin * 60000) {
          lastBreakReminderAt = nowMs
          audioEngine.play('break_reminder')
        }
      }
    }
  }

  function emitTimerSync() {
    emit('timer-sync', {
      projectId: selectedProjectId.value,
      timerState: timerState.value,
      elapsedSeconds: elapsedSeconds.value,
    }).catch(() => {})
  }

  // Fetch tracked apps for the selected project
  async function fetchTrackedApps() {
    if (!selectedProjectId.value) {
      trackedApps.value = []
      return
    }
    try {
      trackedApps.value = await invoke<TrackedApp[]>('get_tracked_apps', { projectId: selectedProjectId.value })
    } catch (e) {
      console.error('Failed to fetch tracked apps:', e)
      trackedApps.value = []
    }
  }

  // Watch project changes to reload tracked apps
  watch(selectedProjectId, () => {
    fetchTrackedApps()
  })

  function startDisplayInterval() {
    if (displayIntervalId) return
    displayIntervalId = window.setInterval(updateElapsed, 1000)
  }

  function stopDisplayInterval() {
    if (displayIntervalId) {
      clearInterval(displayIntervalId)
      displayIntervalId = null
    }
  }

  function startMonitorInterval() {
    if (monitorIntervalId) return
    const settingsStore = useSettingsStore()
    const intervalSec = parseInt(settingsStore.settings.app_check_interval) || 5
    monitorIntervalId = window.setInterval(() => monitorTick(), intervalSec * 1000)
  }

  function stopMonitorInterval() {
    if (monitorIntervalId) {
      clearInterval(monitorIntervalId)
      monitorIntervalId = null
    }
  }

  async function monitorTick() {
    const settingsStore = useSettingsStore()

    if (timerState.value === 'RUNNING') {
      // 1. Check idle first (priority)
      const idleEnabled = settingsStore.settings.idle_detection !== 'false'
      if (idleEnabled) {
        const idleTimeoutMin = parseInt(settingsStore.settings.idle_timeout) || 5
        try {
          const idleSec = await invoke<number>('get_idle_seconds')
          if (idleSec >= idleTimeoutMin * 60) {
            pauseForIdle(idleSec)
            return
          }
        } catch (e) {
          console.error('Idle check failed:', e)
        }
      }

      // Fetch visible windows once for both app tracking and timeline
      const needWindows = trackedApps.value.length > 0 || isTimelineEnabled()
      let visibleWindows: Array<{ exe_name: string; exe_path: string; title: string }> | null = null
      if (needWindows) {
        try {
          visibleWindows = await invoke<Array<{ exe_name: string; exe_path: string; title: string }>>('get_visible_windows')
        } catch (e) {
          console.error('Window check failed:', e)
        }
      }

      // 2. Check app visibility
      if (trackedApps.value.length > 0 && visibleWindows) {
        const trackedPaths = trackedApps.value.map(a => (a.exe_path || '').toLowerCase())
        const anyVisible = visibleWindows.some(w => trackedPaths.includes(w.exe_path.toLowerCase()))
        if (!anyVisible) {
          pauseForApp()
          return
        }
      }

      // 3. Timeline recording
      if (isTimelineEnabled() && visibleWindows && visibleWindows.length > 0) {
        try {
          const fg = visibleWindows[0]
          const fgExe = fg.exe_name || ''
          const fgTitle = fg.title || ''
          if (fgExe !== lastTimelineExe || fgTitle !== lastTimelineTitle) {
            // Window changed - close previous event
            await closeTimelineEvent()
            // Create new event
            const projectId = selectedProjectId.value
            if (projectId) {
              const event = {
                project_id: projectId,
                exe_name: fgExe || null,
                exe_path: fg.exe_path || null,
                window_title: fgTitle || null,
                started_at: new Date().toISOString(),
              }
              try {
                currentTimelineEventId = await invoke<number>('create_timeline_event', { event })
                lastTimelineExe = fgExe
                lastTimelineTitle = fgTitle
                lastTimelineStartMs = Date.now()
              } catch (e) {
                console.error('Failed to create timeline event:', e)
              }
            }
          }
        } catch (e) {
          console.error('Timeline recording failed:', e)
        }
      }
    } else if (timerState.value === 'PAUSED_APP') {
      // Auto-resume check: see if tracked app came back
      const mode = settingsStore.settings.app_tracking_mode || 'auto'
      if (mode === 'auto' || mode === 'notify') {
        try {
          const visibleWindows = await invoke<Array<{ exe_path: string }>>('get_visible_windows')
          const trackedPaths = trackedApps.value.map(a => (a.exe_path || '').toLowerCase())
          const anyVisible = visibleWindows.some(w => trackedPaths.includes(w.exe_path.toLowerCase()))
          if (anyVisible) {
            resumeFromPause()
          }
        } catch (e) {
          console.error('Visibility resume check failed:', e)
        }
      }
    }
    // PAUSED_IDLE: no auto-resume, wait for user dialog action
  }

  function pauseForIdle(idleSec: number) {
    if (timerState.value !== 'RUNNING') return
    const now = Date.now()
    idleStartedAt.value = now - (idleSec * 1000)
    pausedAt.value = idleStartedAt.value
    timerState.value = 'PAUSED_IDLE'
    idleDurationSeconds.value = idleSec
    updateElapsed()
    announce('Idle detected - timer paused')
    audioEngine.play('idle_alert')

    // Show prompt (bring window to front handled by Timer.vue)
    showIdlePrompt.value = true
  }

  function pauseForApp() {
    if (timerState.value !== 'RUNNING') return
    pausedAt.value = Date.now()
    timerState.value = 'PAUSED_APP'
    updateElapsed()
    announce('App not visible - timer paused')
    audioEngine.play('timer_pause')

    const settingsStore = useSettingsStore()
    const mode = settingsStore.settings.app_tracking_mode || 'auto'
    if (mode === 'prompt') {
      showAppPrompt.value = true
    } else if (mode === 'notify') {
      // notification handled by Timer.vue
    }
    // 'auto' mode: silent, auto-resume check in monitorTick
  }

  function resumeFromPause() {
    if (!pausedAt.value) return
    totalPausedMs.value += Date.now() - pausedAt.value
    pausedAt.value = null
    idleStartedAt.value = null
    timerState.value = 'RUNNING'
    showIdlePrompt.value = false
    showAppPrompt.value = false
    updateElapsed()
    announce('Timer resumed')
    audioEngine.play('timer_resume')
    persistState()
  }

  function resumeKeepingIdleTime() {
    // Idle period counts as work: don't add to paused time
    pausedAt.value = null
    idleStartedAt.value = null
    timerState.value = 'RUNNING'
    showIdlePrompt.value = false
    updateElapsed()
    announce('Timer resumed')
    audioEngine.play('timer_resume')
    persistState()
  }

  function pauseManual() {
    if (timerState.value !== 'RUNNING') return
    pausedAt.value = Date.now()
    timerState.value = 'PAUSED_MANUAL'
    updateElapsed()
    announce('Timer paused')
    audioEngine.play('timer_pause')
    persistState()
  }

  // Start timer
  function start() {
    if (timerState.value !== 'STOPPED') return

    startTime.value = new Date()
    totalPausedMs.value = 0
    pausedAt.value = null
    idleStartedAt.value = null
    elapsedSeconds.value = 0
    timerState.value = 'RUNNING'
    showIdlePrompt.value = false
    showAppPrompt.value = false
    goalReachedFired = false
    lastBreakReminderAt = Date.now()
    resetTimelineState()

    startDisplayInterval()
    startMonitorInterval()
    emitTimerSync()
    announce('Timer started')
    audioEngine.play('timer_start')
    persistState()
  }

  // Stop timer and save entry
  async function stop(options?: { subtractIdleTime?: boolean; confirmed?: boolean }) {
    if (timerState.value === 'STOPPED') return

    const endTime = new Date()
    let duration: number

    if (options?.subtractIdleTime && idleStartedAt.value) {
      // Duration up to when idle started
      duration = Math.floor((idleStartedAt.value - startTime.value!.getTime() - totalPausedMs.value) / 1000)
    } else if (pausedAt.value) {
      // Include paused time in calculation
      const finalPaused = totalPausedMs.value + (Date.now() - pausedAt.value)
      duration = Math.floor((endTime.getTime() - startTime.value!.getTime() - finalPaused) / 1000)
    } else {
      duration = Math.floor((endTime.getTime() - startTime.value!.getTime() - totalPausedMs.value) / 1000)
    }

    if (duration < 0) duration = 0

    // Safety net: if no project and there's tracked time, show save dialog
    if (!selectedProjectId.value && duration > 0 && !options?.confirmed) {
      pendingStopDuration.value = duration
      saveDialogMode.value = 'no-project'
      showSaveDialog.value = true
      return
    }

    // Safety net: long timer confirmation (8+ hours)
    const LONG_TIMER_THRESHOLD = 8 * 3600
    if (duration > LONG_TIMER_THRESHOLD && !options?.confirmed) {
      pendingStopDuration.value = duration
      saveDialogMode.value = 'long-timer'
      showSaveDialog.value = true
      return
    }

    // Now actually stop everything
    stopDisplayInterval()
    stopMonitorInterval()
    await closeTimelineEvent()
    resetTimelineState()

    if (selectedProjectId.value) {
      const entry: TimeEntry = {
        project_id: selectedProjectId.value,
        task_id: selectedTaskId.value || undefined,
        description: description.value || undefined,
        start_time: startTime.value!.toISOString(),
        end_time: endTime.toISOString(),
        duration,
        billable: billable.value,
      }

      try {
        const id = await invoke<number>('create_time_entry', { entry })
        currentEntry.value = { ...entry, id: Number(id) }
      } catch (error) {
        handleInvokeError(error, 'Failed to save time entry')
      }
    }

    const h = Math.floor(elapsedSeconds.value / 3600)
    const m = Math.floor((elapsedSeconds.value % 3600) / 60)
    const parts = []
    if (h > 0) parts.push(`${h} hour${h !== 1 ? 's' : ''}`)
    if (m > 0) parts.push(`${m} minute${m !== 1 ? 's' : ''}`)
    announce(`Timer stopped - ${parts.join(' ') || 'less than a minute'} logged`)
    audioEngine.play('timer_stop')

    timerState.value = 'STOPPED'
    startTime.value = null
    pausedAt.value = null
    totalPausedMs.value = 0
    idleStartedAt.value = null
    elapsedSeconds.value = 0
    showIdlePrompt.value = false
    showAppPrompt.value = false
    emitTimerSync()

    const settingsStore = useSettingsStore()
    settingsStore.updateSetting('timer_state_backup', '')
  }

  // Idle prompt handlers
  function handleIdleContinueKeep() {
    resumeKeepingIdleTime()
  }

  function handleIdleContinueSubtract() {
    resumeFromPause()
  }

  async function handleIdleStop() {
    await stop({ subtractIdleTime: true })
  }

  // App prompt handlers
  function handleAppContinue() {
    resumeFromPause()
  }

  async function handleAppStop() {
    await stop()
  }

  // Save dialog handlers (safety net)
  async function handleSaveDialogSave(projectId: number, desc: string) {
    showSaveDialog.value = false
    const oldProjectId = selectedProjectId.value
    const oldDescription = description.value
    selectedProjectId.value = projectId
    description.value = desc
    await stop({ confirmed: true })
    selectedProjectId.value = oldProjectId
    description.value = oldDescription
  }

  function handleSaveDialogDiscard() {
    showSaveDialog.value = false
    stopDisplayInterval()
    stopMonitorInterval()
    timerState.value = 'STOPPED'
    startTime.value = null
    pausedAt.value = null
    totalPausedMs.value = 0
    idleStartedAt.value = null
    elapsedSeconds.value = 0
    showIdlePrompt.value = false
    showAppPrompt.value = false
    emitTimerSync()
    announce('Timer stopped - entry discarded')
    audioEngine.play('timer_stop')
    const settingsStore = useSettingsStore()
    settingsStore.updateSetting('timer_state_backup', '')
  }

  function handleSaveDialogCancel() {
    showSaveDialog.value = false
  }

  function setProject(projectId: number | null) {
    selectedProjectId.value = projectId
    if (timerState.value !== 'STOPPED') persistState()
  }

  function setTask(taskId: number | null) {
    selectedTaskId.value = taskId
    if (timerState.value !== 'STOPPED') persistState()
  }

  function setDescription(desc: string) {
    description.value = desc
    if (timerState.value !== 'STOPPED') persistState()
  }

  function setBillable(val: number) {
    billable.value = val
    if (timerState.value !== 'STOPPED') persistState()
  }

  function reset() {
    stopDisplayInterval()
    stopMonitorInterval()
    timerState.value = 'STOPPED'
    startTime.value = null
    currentEntry.value = null
    selectedProjectId.value = null
    selectedTaskId.value = null
    description.value = ''
    billable.value = 1
    elapsedSeconds.value = 0
    totalPausedMs.value = 0
    pausedAt.value = null
    idleStartedAt.value = null
    showIdlePrompt.value = false
    showAppPrompt.value = false
    trackedApps.value = []
    goalReachedFired = false
    lastBreakReminderAt = null
  }

  // Timer state persistence
  function persistState() {
    try {
      const settingsStore = useSettingsStore()
      const data = {
        timerState: timerState.value,
        startTime: startTime.value ? startTime.value.toISOString() : null,
        selectedProjectId: selectedProjectId.value,
        selectedTaskId: selectedTaskId.value,
        description: description.value,
        billable: billable.value,
        totalPausedMs: totalPausedMs.value,
        pausedAt: pausedAt.value,
      }
      settingsStore.updateSetting('timer_state_backup', JSON.stringify(data))
        .catch(e => handleInvokeError(e, 'Failed to save timer state'))
    } catch (e) {
      handleInvokeError(e, 'Failed to save timer state')
    }
  }

  async function restoreState() {
    try {
      const settingsStore = useSettingsStore()
      const raw = settingsStore.settings.timer_state_backup
      if (!raw) return

      const data = JSON.parse(raw)
      if (!data.timerState || data.timerState === 'STOPPED') return

      // Validate project still exists
      const projectsStore = useProjectsStore()
      await projectsStore.fetchProjects()
      const project = projectsStore.projects.find(p => p.id === data.selectedProjectId)
      if (!project) {
        announce('Previous timer session could not be restored')
        settingsStore.updateSetting('timer_state_backup', '')
        return
      }

      // Restore common state
      selectedProjectId.value = data.selectedProjectId
      selectedTaskId.value = data.selectedTaskId
      description.value = data.description || ''
      billable.value = data.billable ?? 1
      totalPausedMs.value = data.totalPausedMs || 0

      if (data.timerState === 'RUNNING') {
        startTime.value = new Date(data.startTime)
        timerState.value = 'RUNNING'
        const now = Date.now()
        elapsedSeconds.value = Math.floor((now - startTime.value.getTime() - totalPausedMs.value) / 1000)
        if (elapsedSeconds.value < 0) elapsedSeconds.value = 0
        startDisplayInterval()
        startMonitorInterval()
      } else if (data.timerState === 'PAUSED_MANUAL' || data.timerState === 'PAUSED_IDLE' || data.timerState === 'PAUSED_APP') {
        startTime.value = new Date(data.startTime)
        pausedAt.value = data.pausedAt
        timerState.value = data.timerState as TimerState
        if (pausedAt.value && startTime.value) {
          elapsedSeconds.value = Math.floor((pausedAt.value - startTime.value.getTime() - totalPausedMs.value) / 1000)
          if (elapsedSeconds.value < 0) elapsedSeconds.value = 0
        }
        // For app-paused state, start monitor so auto-resume can detect app return
        if (data.timerState === 'PAUSED_APP') {
          startMonitorInterval()
        }
      }

      const h = Math.floor(elapsedSeconds.value / 3600)
      const m = Math.floor((elapsedSeconds.value % 3600) / 60)
      const parts = []
      if (h > 0) parts.push(`${h}h`)
      if (m > 0) parts.push(`${m}m`)
      announce(`Timer restored: ${project.name}, ${parts.join(' ') || 'less than a minute'}`)
    } catch (e) {
      handleInvokeError(e, 'Failed to restore timer state')
    }
  }

  // Listen for mini-timer events
  listen('mini-timer-ready', () => {
    emitTimerSync()
  }).catch(() => {})

  listen('mini-timer-stop', () => {
    stop()
  }).catch(() => {})

  listen('mini-timer-pause', () => {
    pauseManual()
  }).catch(() => {})

  listen('mini-timer-resume', () => {
    resumeFromPause()
  }).catch(() => {})

  return {
    // State
    timerState,
    isRunning,
    isPaused,
    isStopped,
    startTime,
    currentEntry,
    selectedProjectId,
    selectedTaskId,
    description,
    billable,
    elapsedSeconds,
    formattedTime,
    trackedApps,
    showIdlePrompt,
    showAppPrompt,
    idleDurationSeconds,
    // Actions
    start,
    stop,
    pauseManual,
    resumeFromPause,
    setProject,
    setTask,
    setDescription,
    setBillable,
    reset,
    fetchTrackedApps,
    handleIdleContinueKeep,
    handleIdleContinueSubtract,
    handleIdleStop,
    handleAppContinue,
    handleAppStop,
    showSaveDialog,
    saveDialogMode,
    pendingStopDuration,
    handleSaveDialogSave,
    handleSaveDialogDiscard,
    handleSaveDialogCancel,
    restoreState,
  }
})
