<template>
  <div class="p-6 h-full flex flex-col">
    <!-- Aria-live region for announcements -->
    <div class="sr-only" aria-live="assertive">{{ liveAnnouncement }}</div>

    <!-- Running timer indicator -->
    <div
      v-if="timerStore.timerState === 'RUNNING'"
      class="flex items-center gap-3 px-4 py-2 mb-4 bg-status-running/10 border border-status-running/20 rounded-lg"
      role="status"
      aria-live="polite"
    >
      <span class="relative flex h-2 w-2 shrink-0" aria-hidden="true">
        <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-status-running opacity-75" />
        <span class="relative inline-flex rounded-full h-2 w-2 bg-status-running" />
      </span>
      <span class="text-[0.75rem] font-medium text-text-primary">Currently tracking</span>
      <div
        v-if="timerStore.selectedProjectId"
        class="w-2 h-2 rounded-full shrink-0"
        :style="{ backgroundColor: projectsStore.projects.find(p => p.id === timerStore.selectedProjectId)?.color || '#6B7280' }"
        aria-hidden="true"
      />
      <span class="text-[0.75rem] text-text-secondary truncate">{{ projectsStore.projects.find(p => p.id === timerStore.selectedProjectId)?.name || '' }}</span>
      <span class="text-[0.75rem] font-mono text-status-running ml-auto shrink-0">{{ formatTimerDuration(timerStore.elapsedSeconds) }}</span>
    </div>

    <!-- Header -->
    <div class="flex items-center justify-between mb-6">
      <h1 class="text-[1.75rem] font-bold font-[family-name:var(--font-heading)] tracking-tight text-text-primary">Calendar</h1>

      <div class="flex items-center gap-3">
        <button
          @click="goToToday"
          class="px-3 py-1.5 text-[0.75rem] font-medium border border-border-subtle text-text-secondary rounded-lg hover:bg-bg-elevated transition-colors duration-150 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-accent"
        >
          Today
        </button>

        <!-- View toggle (Task 29) -->
        <div role="tablist" aria-label="Calendar view" class="flex items-center border border-border-subtle rounded-lg overflow-hidden">
          <button
            v-for="view in (['day', 'week', 'month'] as const)"
            :key="view"
            @click="currentView = view"
            role="tab"
            :aria-selected="currentView === view"
            :tabindex="currentView === view ? 0 : -1"
            class="px-3 py-1.5 text-[0.6875rem] font-medium capitalize transition-colors duration-150 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-accent"
            :class="currentView === view
              ? 'bg-accent text-bg-base'
              : 'text-text-secondary hover:text-text-primary hover:bg-bg-elevated'"
          >
            {{ view }}
          </button>
        </div>

        <div class="flex items-center gap-1">
          <button
            @click="navigatePrev"
            v-tooltip="'Previous ' + currentView"
            class="p-1.5 text-text-tertiary hover:text-text-secondary rounded-lg hover:bg-bg-elevated transition-colors duration-150 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-accent"
            :aria-label="'Previous ' + currentView"
          >
            <ChevronLeft class="w-4 h-4" :stroke-width="2" aria-hidden="true" />
          </button>
          <button
            @click="navigateNext"
            v-tooltip="'Next ' + currentView"
            class="p-1.5 text-text-tertiary hover:text-text-secondary rounded-lg hover:bg-bg-elevated transition-colors duration-150 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-accent"
            :aria-label="'Next ' + currentView"
          >
            <ChevronRight class="w-4 h-4" :stroke-width="2" aria-hidden="true" />
          </button>
        </div>
        <span class="text-[0.75rem] text-text-secondary font-medium min-w-[10rem]" role="status" aria-live="polite">
          {{ viewRangeLabel }}
        </span>
      </div>
    </div>

    <!-- Empty state (week view only) -->
    <div v-if="currentView === 'week' && weekIsEmpty" class="flex-1 flex flex-col items-center justify-center py-16">
      <CalendarX class="w-12 h-12 text-text-tertiary animate-float" :stroke-width="1.5" aria-hidden="true" />
      <p class="text-sm text-text-secondary mt-4">No events this week</p>
      <p class="text-xs text-text-tertiary mt-2 max-w-xs text-center">Track time or import a calendar to see events here.</p>
      <router-link to="/timer" class="mt-4 px-4 py-2 bg-accent text-bg-base text-xs font-medium rounded-lg hover:bg-accent-hover transition-colors">
        Go to Timer
      </router-link>
      <button
        v-if="onboardingStore.isVisible"
        @click="tourStore.start(TOURS.timer)"
        class="mt-2 text-[0.6875rem] text-accent-text hover:underline"
      >
        Show me how
      </button>
    </div>

    <!-- Empty state (day view) -->
    <div v-else-if="currentView === 'day' && dayIsEmpty" class="flex-1 flex flex-col items-center justify-center py-16">
      <CalendarX class="w-12 h-12 text-text-tertiary animate-float" :stroke-width="1.5" aria-hidden="true" />
      <p class="text-sm text-text-secondary mt-4">No events this day</p>
      <p class="text-xs text-text-tertiary mt-2 max-w-xs text-center">Track time or import a calendar to see events here.</p>
      <router-link to="/timer" class="mt-4 px-4 py-2 bg-accent text-bg-base text-xs font-medium rounded-lg hover:bg-accent-hover transition-colors">
        Go to Timer
      </router-link>
    </div>

    <!-- Week View -->
    <Transition v-else-if="currentView === 'week'" name="fade" mode="out-in">
    <div :key="weekStart.getTime()" class="flex-1 bg-bg-surface rounded-lg border border-border-subtle overflow-hidden flex flex-col min-h-0">
      <div class="sr-only">Navigate to entries with Tab. Press Enter to edit. Use the quick entry shortcut to create new entries.</div>
      <!-- Scrollable hour rows -->
      <div ref="scrollContainer" class="flex-1 overflow-y-auto min-h-0">
        <!-- Day column headers (sticky) -->
        <div class="grid shrink-0 border-b border-border-subtle sticky top-0 z-10 bg-bg-surface" :style="weekGridStyle">
          <!-- Top-left corner (hour gutter) -->
          <div class="w-14 shrink-0 border-r border-border-subtle" />
          <!-- Day headers -->
          <div
            v-for="(day, index) in weekDays"
            :key="index"
            role="columnheader"
            class="px-2 py-2.5 text-center border-r border-border-subtle last:border-r-0"
            :class="isToday(day) ? 'bg-accent/5' : ''"
          >
            <span
              class="text-[0.6875rem] uppercase tracking-[0.08em] font-medium"
              :class="isToday(day) ? 'text-accent-text' : 'text-text-tertiary'"
            >
              {{ formatDayHeader(day) }}
            </span>
          </div>
        </div>
        <div class="grid relative" :style="weekGridStyle">
          <!-- Hour labels column -->
          <div class="w-14 shrink-0 border-r border-border-subtle">
            <div
              v-for="hour in hours"
              :key="hour"
              class="h-12 flex items-start justify-end pr-2 pt-0.5"
            >
              <span class="text-[0.6875rem] text-text-tertiary font-mono leading-none -translate-y-1/2">
                {{ formatHourLabel(hour) }}
              </span>
            </div>
          </div>

          <!-- Day columns -->
          <div
            v-for="(day, dayIndex) in weekDays"
            :key="dayIndex"
            class="relative border-r border-border-subtle last:border-r-0"
            :class="isToday(day) ? 'bg-accent/[0.02]' : ''"
            @mousedown="onGridMouseDown($event, dayIndex, 'week')"
            @mousemove="onGridMouseMove($event, dayIndex, 'week')"
            @mouseup="onGridMouseUp"
          >
            <!-- Hour grid lines -->
            <div
              v-for="hour in hours"
              :key="hour"
              class="h-12 border-b border-border-subtle"
            />

            <!-- Drag preview rectangle (Task 32) -->
            <div
              v-if="isDragging && dragStart && dragEnd && getDragDayIndex() === dayIndex"
              class="absolute left-0.5 right-0.5 rounded pointer-events-none z-20"
              :style="getDragPreviewStyle()"
              aria-hidden="true"
            />

            <!-- Time entry blocks -->
            <div
              v-for="entry in getEntriesForDay(day)"
              :key="entry.id"
              class="absolute left-0.5 right-0.5 rounded px-1.5 py-1 overflow-hidden border-l-[3px] min-h-[1.25rem] group focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-accent"
              :class="[
                movingEntryId === entry.id ? 'cursor-grabbing opacity-70 z-30' : 'cursor-grab',
                resizingEntryId === entry.id ? 'z-30' : ''
              ]"
              :style="getEntryStyle(entry)"
              :aria-label="getEntryTooltip(entry)"
              tabindex="0"
              @mousedown.stop="startMoveEntry($event, entry, dayIndex)"
              @keydown="onEntryKeydown($event, entry)"
            >
              <p class="text-[0.625rem] font-medium leading-tight truncate" :style="{ color: getProjectColor(entry.project_id) }">
                {{ getProjectName(entry.project_id) }}
              </p>
              <p
                v-if="entry.description && entry.duration >= 900"
                class="text-[0.5625rem] text-text-tertiary leading-tight truncate mt-0.5"
              >
                {{ entry.description }}
              </p>
              <p
                v-if="entry.duration >= 1800"
                class="text-[0.5625rem] text-text-tertiary leading-tight truncate mt-0.5"
              >
                {{ formatDuration(entry.duration) }}
              </p>
              <!-- Replay button -->
              <button
                @click.stop="replayEntry(entry)"
                @mousedown.stop
                class="absolute top-0.5 right-0.5 p-0.5 text-text-tertiary hover:text-status-running transition-colors opacity-0 group-hover:opacity-100 focus:opacity-100 z-10 bg-bg-surface/80 rounded"
                aria-label="Start timer with this entry"
              >
                <Play class="h-2.5 w-2.5" :stroke-width="2" fill="currentColor" aria-hidden="true" />
              </button>
              <!-- Resize handle (Task 33) -->
              <div
                @mousedown.stop="startResize($event, entry)"
                role="separator"
                aria-orientation="horizontal"
                :aria-label="'Resize ' + (entry.description || 'entry')"
                class="absolute bottom-0 left-0 right-0 h-1.5 cursor-ns-resize hover:bg-accent/30 transition-colors opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 focus:opacity-100"
                tabindex="0"
              />
            </div>

            <!-- Calendar event blocks -->
            <div
              v-for="evt in getCalendarEventsForDay(day)"
              :key="'cal-' + (evt.id || evt.uid)"
              class="absolute left-0.5 right-0.5 rounded px-1.5 py-1 overflow-hidden border-l-[3px] border-dashed min-h-[1.25rem] pointer-events-none"
              :style="getCalendarEventStyle(evt)"
              :aria-label="(evt.summary || 'Calendar event') + ' at ' + formatCalendarEventTime(evt)"
            >
              <p class="text-[0.625rem] font-medium leading-tight truncate text-[#8B5CF6]">
                {{ evt.summary || 'Calendar Event' }}
              </p>
              <p class="text-[0.5625rem] text-text-tertiary leading-tight truncate mt-0.5">
                {{ formatCalendarEventTime(evt) }}
                <span v-if="evt.location"> - {{ evt.location }}</span>
              </p>
            </div>

            <!-- Current time indicator -->
            <div
              v-if="isToday(day) && currentTimeOffset !== null"
              class="absolute left-0 right-0 z-10 pointer-events-none"
              :style="{ top: currentTimeOffset + 'px' }"
              aria-hidden="true"
            >
              <div class="relative">
                <div class="absolute -left-[3px] -top-[3px] w-[7px] h-[7px] rounded-full bg-status-error" />
                <div class="h-[1.5px] bg-status-error w-full" />
              </div>
            </div>
          </div>
        </div>
      </div>

      <!-- SR-only help text for drag-to-create (Task 32) -->
      <div class="sr-only">Use Enter to create an entry at the selected time.</div>
    </div>
    </Transition>

    <!-- Day View (Task 30) -->
    <Transition v-else-if="currentView === 'day'" name="fade" mode="out-in">
    <div :key="currentDay" class="flex-1 bg-bg-surface rounded-lg border border-border-subtle overflow-hidden flex flex-col min-h-0">
      <div class="sr-only">Navigate to entries with Tab. Press Enter to edit. Use the quick entry shortcut to create new entries.</div>
      <div ref="dayScrollContainer" class="flex-1 overflow-y-auto min-h-0">
        <!-- Day header (sticky) -->
        <div class="grid shrink-0 border-b border-border-subtle sticky top-0 z-10 bg-bg-surface" :style="dayGridStyle">
          <div class="w-14 shrink-0 border-r border-border-subtle" />
          <div
            role="columnheader"
            class="px-2 py-2.5 text-center"
            :class="isToday(currentDayDate) ? 'bg-accent/5' : ''"
          >
            <span
              class="text-[0.6875rem] uppercase tracking-[0.08em] font-medium"
              :class="isToday(currentDayDate) ? 'text-accent-text' : 'text-text-tertiary'"
            >
              {{ formatDayHeaderLong(currentDayDate) }}
            </span>
          </div>
        </div>
        <div class="grid relative" :style="dayGridStyle">
          <!-- Hour labels column -->
          <div class="w-14 shrink-0 border-r border-border-subtle">
            <div
              v-for="hour in hours"
              :key="hour"
              class="h-12 flex items-start justify-end pr-2 pt-0.5"
            >
              <span class="text-[0.6875rem] text-text-tertiary font-mono leading-none -translate-y-1/2">
                {{ formatHourLabel(hour) }}
              </span>
            </div>
          </div>

          <!-- Single day column -->
          <div
            class="relative"
            :class="isToday(currentDayDate) ? 'bg-accent/[0.02]' : ''"
            @mousedown="onGridMouseDown($event, 0, 'day')"
            @mousemove="onGridMouseMove($event, 0, 'day')"
            @mouseup="onGridMouseUp"
          >
            <!-- Hour grid lines -->
            <div
              v-for="hour in hours"
              :key="hour"
              class="h-12 border-b border-border-subtle"
            />

            <!-- Drag preview rectangle -->
            <div
              v-if="isDragging && dragStart && dragEnd && getDragDayIndex() === 0"
              class="absolute left-16 right-2 rounded pointer-events-none z-20"
              :style="getDragPreviewStyle()"
              aria-hidden="true"
            />

            <!-- Time entry blocks (wider in day view) -->
            <div
              v-for="entry in dayEntries"
              :key="entry.id"
              class="absolute left-16 right-2 rounded px-1.5 py-1 overflow-hidden border-l-[3px] min-h-[1.25rem] group focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-accent"
              :class="[
                movingEntryId === entry.id ? 'cursor-grabbing opacity-70 z-30' : 'cursor-grab',
                resizingEntryId === entry.id ? 'z-30' : ''
              ]"
              :style="getEntryStyle(entry)"
              :aria-label="getEntryTooltip(entry)"
              tabindex="0"
              @mousedown.stop="startMoveEntry($event, entry, 0)"
              @keydown="onEntryKeydown($event, entry)"
            >
              <p class="text-[0.625rem] font-medium leading-tight truncate" :style="{ color: getProjectColor(entry.project_id) }">
                {{ getProjectName(entry.project_id) }}
              </p>
              <p
                v-if="entry.description"
                class="text-[0.5625rem] text-text-tertiary leading-tight truncate mt-0.5"
              >
                {{ entry.description }}
              </p>
              <p
                v-if="entry.duration >= 1800"
                class="text-[0.5625rem] text-text-tertiary leading-tight truncate mt-0.5"
              >
                {{ formatDuration(entry.duration) }}
              </p>
              <!-- Resize handle -->
              <div
                @mousedown.stop="startResize($event, entry)"
                role="separator"
                aria-orientation="horizontal"
                :aria-label="'Resize ' + (entry.description || 'entry')"
                class="absolute bottom-0 left-0 right-0 h-1.5 cursor-ns-resize hover:bg-accent/30 transition-colors opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 focus:opacity-100"
                tabindex="0"
              />
            </div>

            <!-- Calendar event blocks -->
            <div
              v-for="evt in dayCalendarEvents"
              :key="'cal-' + (evt.id || evt.uid)"
              class="absolute left-16 right-2 rounded px-1.5 py-1 overflow-hidden border-l-[3px] border-dashed min-h-[1.25rem] pointer-events-none"
              :style="getCalendarEventStyle(evt)"
              :aria-label="(evt.summary || 'Calendar event') + ' at ' + formatCalendarEventTime(evt)"
            >
              <p class="text-[0.625rem] font-medium leading-tight truncate text-[#8B5CF6]">
                {{ evt.summary || 'Calendar Event' }}
              </p>
              <p class="text-[0.5625rem] text-text-tertiary leading-tight truncate mt-0.5">
                {{ formatCalendarEventTime(evt) }}
                <span v-if="evt.location"> - {{ evt.location }}</span>
              </p>
            </div>

            <!-- Current time indicator -->
            <div
              v-if="isToday(currentDayDate) && currentTimeOffset !== null"
              class="absolute left-0 right-0 z-10 pointer-events-none"
              :style="{ top: currentTimeOffset + 'px' }"
              aria-hidden="true"
            >
              <div class="relative">
                <div class="absolute -left-[3px] -top-[3px] w-[7px] h-[7px] rounded-full bg-status-error" />
                <div class="h-[1.5px] bg-status-error w-full" />
              </div>
            </div>
          </div>
        </div>
      </div>

      <div class="sr-only">Use Enter to create an entry at the selected time.</div>
    </div>
    </Transition>

    <!-- Month View (Task 31) -->
    <Transition v-else-if="currentView === 'month'" name="fade" mode="out-in">
    <div :key="currentMonth.year + '-' + currentMonth.month" class="flex-1 bg-bg-surface rounded-lg border border-border-subtle overflow-hidden flex flex-col min-h-0">
      <!-- Weekday headers -->
      <div class="grid grid-cols-7 border-b border-border-subtle">
        <div
          v-for="dayName in ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']"
          :key="dayName"
          class="px-2 py-2 text-center text-[0.6875rem] uppercase tracking-[0.08em] font-medium text-text-tertiary border-r border-border-subtle last:border-r-0"
        >
          {{ dayName }}
        </div>
      </div>
      <!-- Month grid -->
      <div class="flex-1 overflow-y-auto min-h-0">
        <div
          v-for="(week, weekIndex) in monthGrid"
          :key="weekIndex"
          class="grid grid-cols-7 border-b border-border-subtle last:border-b-0"
        >
          <div
            v-for="cell in week"
            :key="cell.date"
            class="min-h-[5rem] p-1.5 border-r border-border-subtle last:border-r-0 cursor-pointer transition-colors hover:bg-bg-elevated focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-accent"
            :class="cell.isCurrentMonth ? '' : 'opacity-40'"
            tabindex="0"
            role="button"
            :aria-label="'View ' + cell.date + (cell.totalSeconds > 0 ? ', ' + formatDuration(cell.totalSeconds) + ' tracked' : '')"
            @click="goToDayFromMonth(cell.date)"
            @keydown.enter="goToDayFromMonth(cell.date)"
          >
            <!-- Day number -->
            <div class="flex items-center justify-between mb-1">
              <span
                class="text-[0.6875rem] font-medium w-6 h-6 flex items-center justify-center rounded-full"
                :class="isTodayStr(cell.date)
                  ? 'bg-accent text-bg-base'
                  : cell.isCurrentMonth ? 'text-text-primary' : 'text-text-tertiary'"
              >
                {{ cell.dayNum }}
              </span>
              <span
                v-if="cell.totalSeconds > 0"
                class="text-[0.5625rem] text-text-secondary font-mono"
              >
                {{ formatDurationShort(cell.totalSeconds) }}
              </span>
            </div>
            <!-- Project color dots (up to 3) -->
            <div class="flex flex-wrap gap-0.5">
              <div
                v-for="(proj, pIdx) in getMonthCellProjects(cell).slice(0, 3)"
                :key="pIdx"
                class="flex items-center gap-0.5 max-w-full"
              >
                <span
                  class="w-1.5 h-1.5 rounded-full shrink-0"
                  :style="{ backgroundColor: proj.color }"
                  aria-hidden="true"
                />
                <span class="text-[0.5rem] text-text-tertiary truncate max-w-[4rem]">{{ proj.name }}</span>
              </div>
            </div>
            <p
              v-if="cell.entries.length > 3"
              class="text-[0.5rem] text-text-tertiary mt-0.5"
            >
              +{{ cell.entries.length - 3 }} more
            </p>
          </div>
        </div>
      </div>
    </div>
    </Transition>

    <!-- Quick-add popover (Task 32) - Teleported to body -->
    <Teleport to="#app">
      <Transition name="fade">
      <div
        v-if="showQuickAdd"
        ref="quickAddRef"
        class="bg-bg-surface border border-border-subtle rounded-lg shadow-[0_1px_3px_rgba(0,0,0,0.3)] p-4 w-72 z-[9999]"
        :style="quickAddStyle"
        @keydown.escape="cancelQuickAdd"
      >
        <p class="text-[0.75rem] font-medium text-text-primary mb-3">New entry</p>
        <p class="text-[0.625rem] text-text-tertiary mb-3">
          {{ quickAddTimeLabel }}
        </p>
        <div class="space-y-3">
          <div>
            <label class="text-[0.625rem] text-text-secondary font-medium block mb-1">Project</label>
            <AppSelect
              v-model="quickAddProject"
              :options="projectsStore.projects.filter(p => !p.archived)"
              label-key="name"
              value-key="id"
              placeholder="Select project..."
            />
          </div>
          <div>
            <label for="quick-add-desc" class="text-[0.625rem] text-text-secondary font-medium block mb-1">Description</label>
            <input
              id="quick-add-desc"
              ref="quickAddDescRef"
              v-model="quickAddDescription"
              type="text"
              placeholder="What are you working on?"
              class="w-full px-3 py-2 bg-bg-inset border border-border-subtle rounded-lg text-[0.75rem] text-text-primary placeholder-text-tertiary focus:outline-none focus:border-accent transition-colors"
              @keydown.enter="saveQuickAdd"
            />
          </div>
          <div class="flex items-center justify-end gap-2 pt-1">
            <button
              @click="cancelQuickAdd"
              class="px-3 py-1.5 text-[0.6875rem] font-medium text-text-secondary hover:text-text-primary transition-colors rounded-lg hover:bg-bg-elevated focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-accent"
            >
              Cancel
            </button>
            <button
              @click="saveQuickAdd"
              :disabled="!quickAddProject"
              class="px-3 py-1.5 text-[0.6875rem] font-medium bg-accent text-bg-base rounded-lg hover:bg-accent-hover transition-colors disabled:opacity-40 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-accent"
            >
              Save
            </button>
          </div>
        </div>
      </div>
      </Transition>
    </Teleport>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'
import { invoke } from '@tauri-apps/api/core'
import { ChevronLeft, ChevronRight, CalendarX, Play } from 'lucide-vue-next'
import { useRouter } from 'vue-router'
import { useEntriesStore, type TimeEntry } from '../stores/entries'
import { useProjectsStore } from '../stores/projects'
import { useTimerStore } from '../stores/timer'
import { useOnboardingStore } from '../stores/onboarding'
import { useTourStore } from '../stores/tour'
import { useToastStore } from '../stores/toast'
import { TOURS } from '../utils/tours'
import { getZoomFactor } from '../utils/dropdown'
import { useFocusTrap } from '../utils/focusTrap'
import AppSelect from '../components/AppSelect.vue'

interface CalendarEvent {
  id?: number
  source_id: number
  uid?: string
  summary?: string
  start_time?: string
  end_time?: string
  duration?: number
  location?: string
}

interface MonthCell {
  date: string
  dayNum: number
  isCurrentMonth: boolean
  entries: TimeEntry[]
  totalSeconds: number
}

const entriesStore = useEntriesStore()
const projectsStore = useProjectsStore()
const timerStore = useTimerStore()
const onboardingStore = useOnboardingStore()
const tourStore = useTourStore()
const toastStore = useToastStore()
const router = useRouter()

function formatTimerDuration(seconds: number): string {
  const h = Math.floor(seconds / 3600)
  const m = Math.floor((seconds % 3600) / 60)
  const s = seconds % 60
  return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`
}
const calendarEvents = ref<CalendarEvent[]>([])
const { activate: activateTrap, deactivate: deactivateTrap } = useFocusTrap()

// -- View state (Task 29) --
const currentView = ref<'day' | 'week' | 'month'>('week')

// -- Week state --
const weekStart = ref(getMonday(new Date()))
const scrollContainer = ref<HTMLElement | null>(null)

// -- Day state (Task 30) --
const currentDay = ref(formatISODate(new Date()))
const dayScrollContainer = ref<HTMLElement | null>(null)

// -- Month state (Task 31) --
const currentMonth = ref({ year: new Date().getFullYear(), month: new Date().getMonth() })

// -- Drag-to-create state (Task 32) --
const isDragging = ref(false)
const dragStart = ref<{ day: number; hour: number; minutes: number } | null>(null)
const dragEnd = ref<{ day: number; hour: number; minutes: number } | null>(null)
const dragView = ref<'week' | 'day'>('week')
const showQuickAdd = ref(false)
const quickAddProject = ref<number | null>(null)
const quickAddDescription = ref('')
const quickAddRef = ref<HTMLElement | null>(null)
const quickAddDescRef = ref<HTMLInputElement | null>(null)
const quickAddStyle = ref<Record<string, string>>({})

// -- Drag-to-move/resize state (Task 33) --
const movingEntryId = ref<number | null>(null)
const resizingEntryId = ref<number | null>(null)
const dragOffsetMinutes = ref(0)
const liveAnnouncement = ref('')
let moveStartY = 0
let moveOriginalStartTime = ''
let moveOriginalDuration = 0
let moveEntry: TimeEntry | null = null

// -- Week empty state --
const weekIsEmpty = computed(() => {
  const hasEntries = weekDays.value.some(day => getEntriesForDay(day).length > 0)
  const hasEvents = weekDays.value.some(day => getCalendarEventsForDay(day).length > 0)
  return !hasEntries && !hasEvents
})

// -- Day empty state --
const dayIsEmpty = computed(() => {
  return dayEntries.value.length === 0 && dayCalendarEvents.value.length === 0
})

// Hours displayed: full 24h (0..23)
const HOUR_START = 0
const HOUR_END = 23
const hours = Array.from({ length: HOUR_END - HOUR_START + 1 }, (_, i) => HOUR_START + i)
const HOUR_HEIGHT = 48 // h-12 = 3rem = 48px

// Current time tracking
const currentTimeOffset = ref<number | null>(null)
let timeInterval: ReturnType<typeof setInterval> | null = null

// Grid template: fixed gutter + 7 equal columns (week)
const weekGridStyle = computed(() => ({
  gridTemplateColumns: '3.5rem repeat(7, 1fr)'
}))

// Grid template: fixed gutter + 1 column (day)
const dayGridStyle = computed(() => ({
  gridTemplateColumns: '3.5rem 1fr'
}))

// Week days (Monday to Sunday)
const weekDays = computed(() => {
  const days: Date[] = []
  for (let i = 0; i < 7; i++) {
    const d = new Date(weekStart.value)
    d.setDate(d.getDate() + i)
    days.push(d)
  }
  return days
})

// Current day as Date object
const currentDayDate = computed(() => {
  const parts = currentDay.value.split('-')
  return new Date(Number(parts[0]), Number(parts[1]) - 1, Number(parts[2]))
})

// Day entries computed (Task 30)
const dayEntries = computed(() => {
  return entriesStore.entries.filter(e => {
    const entryDate = new Date(e.start_time)
    return formatISODate(entryDate) === currentDay.value
  })
})

// Day calendar events computed (Task 30)
const dayCalendarEvents = computed(() => {
  return calendarEvents.value.filter(evt => {
    if (!evt.start_time) return false
    const evtDate = new Date(evt.start_time)
    return formatISODate(evtDate) === currentDay.value
  })
})

// -- View range label (Task 29) --
const viewRangeLabel = computed(() => {
  if (currentView.value === 'week') {
    const start = weekDays.value[0]
    const end = weekDays.value[6]
    const startMonth = start.toLocaleString('en-US', { month: 'short' })
    const endMonth = end.toLocaleString('en-US', { month: 'short' })
    const startDay = start.getDate()
    const endDay = end.getDate()
    const year = end.getFullYear()
    if (startMonth === endMonth) {
      return `${startMonth} ${startDay} - ${endDay}, ${year}`
    }
    return `${startMonth} ${startDay} - ${endMonth} ${endDay}, ${year}`
  } else if (currentView.value === 'day') {
    const d = currentDayDate.value
    return d.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' })
  } else {
    // month
    const { year, month } = currentMonth.value
    const d = new Date(year, month, 1)
    return d.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })
  }
})

// -- Month grid computed (Task 31) --
const monthGrid = computed((): MonthCell[][] => {
  const { year, month } = currentMonth.value
  const firstDay = new Date(year, month, 1)
  const lastDay = new Date(year, month + 1, 0)

  // Start from Monday of the week containing the first day
  const startDate = getMonday(firstDay)

  // End on Sunday of the week containing the last day
  const endDate = new Date(lastDay)
  const endDow = endDate.getDay()
  if (endDow !== 0) {
    endDate.setDate(endDate.getDate() + (7 - endDow))
  }

  // Pre-group entries by date for O(n) lookup instead of O(n*m)
  const entriesByDate = new Map<string, TimeEntry[]>()
  for (const e of entriesStore.entries) {
    const dateStr = formatISODate(new Date(e.start_time))
    const arr = entriesByDate.get(dateStr)
    if (arr) arr.push(e)
    else entriesByDate.set(dateStr, [e])
  }

  const weeks: MonthCell[][] = []
  const cursor = new Date(startDate)

  while (cursor <= endDate && weeks.length < 6) {
    const week: MonthCell[] = []
    for (let i = 0; i < 7; i++) {
      const dateStr = formatISODate(cursor)
      const cellEntries = entriesByDate.get(dateStr) || []
      const totalSeconds = cellEntries.reduce((sum, e) => sum + e.duration, 0)

      week.push({
        date: dateStr,
        dayNum: cursor.getDate(),
        isCurrentMonth: cursor.getMonth() === month && cursor.getFullYear() === year,
        entries: cellEntries,
        totalSeconds
      })
      cursor.setDate(cursor.getDate() + 1)
    }
    weeks.push(week)
  }

  return weeks
})

// -- Navigation (Task 29) --
function navigatePrev() {
  if (currentView.value === 'week') prevWeek()
  else if (currentView.value === 'day') prevDay()
  else prevMonth()
}

function navigateNext() {
  if (currentView.value === 'week') nextWeek()
  else if (currentView.value === 'day') nextDay()
  else nextMonth()
}

function prevWeek() {
  const d = new Date(weekStart.value)
  d.setDate(d.getDate() - 7)
  weekStart.value = d
  fetchWeekEntries()
}

function nextWeek() {
  const d = new Date(weekStart.value)
  d.setDate(d.getDate() + 7)
  weekStart.value = d
  fetchWeekEntries()
}

function prevDay() {
  const d = new Date(currentDay.value + 'T00:00:00')
  d.setDate(d.getDate() - 1)
  currentDay.value = formatISODate(d)
  fetchDayEntries()
}

function nextDay() {
  const d = new Date(currentDay.value + 'T00:00:00')
  d.setDate(d.getDate() + 1)
  currentDay.value = formatISODate(d)
  fetchDayEntries()
}

function prevMonth() {
  const { year, month } = currentMonth.value
  if (month === 0) {
    currentMonth.value = { year: year - 1, month: 11 }
  } else {
    currentMonth.value = { year, month: month - 1 }
  }
  fetchMonthEntries()
}

function nextMonth() {
  const { year, month } = currentMonth.value
  if (month === 11) {
    currentMonth.value = { year: year + 1, month: 0 }
  } else {
    currentMonth.value = { year, month: month + 1 }
  }
  fetchMonthEntries()
}

function goToToday() {
  const now = new Date()
  if (currentView.value === 'week') {
    weekStart.value = getMonday(now)
    fetchWeekEntries()
    nextTick(() => scrollToCurrentTime())
  } else if (currentView.value === 'day') {
    currentDay.value = formatISODate(now)
    fetchDayEntries()
    nextTick(() => scrollToCurrentTime())
  } else {
    currentMonth.value = { year: now.getFullYear(), month: now.getMonth() }
    fetchMonthEntries()
  }
}

// Navigate from month cell to day view (Task 31)
function goToDayFromMonth(dateStr: string) {
  currentDay.value = dateStr
  currentView.value = 'day'
  fetchDayEntries()
}

// -- Fetch functions --
async function fetchWeekEntries() {
  const start = formatISODate(weekDays.value[0])
  const end = formatISODate(weekDays.value[6])
  await Promise.all([
    entriesStore.fetchEntries(start, end),
    fetchCalendarEvents()
  ])
}

async function fetchCalendarEvents() {
  let start: string
  let end: string
  if (currentView.value === 'week') {
    start = formatISODate(weekDays.value[0])
    end = formatISODate(weekDays.value[6])
  } else if (currentView.value === 'day') {
    start = currentDay.value
    end = currentDay.value
  } else {
    // month - use full grid range
    const { year, month } = currentMonth.value
    const firstDay = new Date(year, month, 1)
    const lastDay = new Date(year, month + 1, 0)
    const startDate = getMonday(firstDay)
    const endSunday = new Date(lastDay)
    const endDow = endSunday.getDay()
    if (endDow !== 0) endSunday.setDate(endSunday.getDate() + (7 - endDow))
    start = formatISODate(startDate)
    end = formatISODate(endSunday)
  }
  try {
    calendarEvents.value = await invoke<CalendarEvent[]>('get_calendar_events', { startDate: start, endDate: end })
  } catch (e) {
    console.error('Failed to fetch calendar events:', e)
  }
}

async function fetchDayEntries() {
  await Promise.all([
    entriesStore.fetchEntries(currentDay.value, currentDay.value),
    fetchCalendarEvents()
  ])
}

async function fetchMonthEntries() {
  const { year, month } = currentMonth.value
  const firstDay = new Date(year, month, 1)
  const lastDay = new Date(year, month + 1, 0)
  const startDate = getMonday(firstDay)
  const endSunday = new Date(lastDay)
  const endDow = endSunday.getDay()
  if (endDow !== 0) endSunday.setDate(endSunday.getDate() + (7 - endDow))
  await Promise.all([
    entriesStore.fetchEntries(formatISODate(startDate), formatISODate(endSunday)),
    fetchCalendarEvents()
  ])
}

// Refetch current view entries
async function refetchCurrentView() {
  if (currentView.value === 'week') await fetchWeekEntries()
  else if (currentView.value === 'day') await fetchDayEntries()
  else await fetchMonthEntries()
}

// -- Entry helpers --
function getEntriesForDay(day: Date): TimeEntry[] {
  const dayStr = formatISODate(day)
  return entriesStore.entries.filter(entry => {
    const entryDate = new Date(entry.start_time)
    const entryStr = formatISODate(entryDate)
    return entryStr === dayStr
  })
}

function getEntryStyle(entry: TimeEntry): Record<string, string> {
  const start = new Date(entry.start_time)
  const startHour = start.getHours() + start.getMinutes() / 60
  const durationHours = entry.duration / 3600

  const visibleStart = Math.max(startHour, HOUR_START)
  const visibleEnd = Math.min(startHour + durationHours, HOUR_END + 1)

  if (visibleEnd <= HOUR_START || visibleStart >= HOUR_END + 1) {
    return { display: 'none' }
  }

  const topOffset = (visibleStart - HOUR_START) * HOUR_HEIGHT
  const height = Math.max((visibleEnd - visibleStart) * HOUR_HEIGHT, 20)

  const color = getProjectColor(entry.project_id)

  return {
    top: `${topOffset}px`,
    height: `${height}px`,
    backgroundColor: hexToRgba(color, 0.15),
    borderLeftColor: color
  }
}

function getEntryTooltip(entry: TimeEntry): string {
  const project = getProjectName(entry.project_id)
  const duration = formatDuration(entry.duration)
  const start = new Date(entry.start_time)
  const time = start.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })
  const desc = entry.description ? ` - ${entry.description}` : ''
  return `${project} (${duration}) at ${time}${desc}`
}

// -- Calendar event helpers --
function getCalendarEventsForDay(day: Date): CalendarEvent[] {
  const dayStr = formatISODate(day)
  return calendarEvents.value.filter(evt => {
    if (!evt.start_time) return false
    const evtDate = new Date(evt.start_time)
    return formatISODate(evtDate) === dayStr
  })
}

function getCalendarEventStyle(evt: CalendarEvent): Record<string, string> {
  if (!evt.start_time) return { display: 'none' }

  const start = new Date(evt.start_time)
  const startHour = start.getHours() + start.getMinutes() / 60

  let durationHours = 1
  if (evt.duration && evt.duration > 0) {
    durationHours = evt.duration / 3600
  } else if (evt.end_time) {
    const end = new Date(evt.end_time)
    durationHours = (end.getTime() - start.getTime()) / 3600000
  }

  const visibleStart = Math.max(startHour, HOUR_START)
  const visibleEnd = Math.min(startHour + durationHours, HOUR_END + 1)

  if (visibleEnd <= HOUR_START || visibleStart >= HOUR_END + 1) {
    return { display: 'none' }
  }

  const topOffset = (visibleStart - HOUR_START) * HOUR_HEIGHT
  const height = Math.max((visibleEnd - visibleStart) * HOUR_HEIGHT, 20)

  return {
    top: `${topOffset}px`,
    height: `${height}px`,
    backgroundColor: 'rgba(139, 92, 246, 0.1)',
    borderLeftColor: '#8B5CF6',
    borderLeftStyle: 'dashed',
    opacity: '0.6'
  }
}

function formatCalendarEventTime(evt: CalendarEvent): string {
  if (!evt.start_time) return ''
  const start = new Date(evt.start_time)
  return start.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })
}

// -- Project helpers --
function getProjectName(projectId: number): string {
  const project = projectsStore.projects.find(p => p.id === projectId)
  return project?.name || 'Unknown Project'
}

function getProjectColor(projectId: number): string {
  const project = projectsStore.projects.find(p => p.id === projectId)
  return project?.color || '#6B7280'
}

// -- Month cell project helpers --
function getMonthCellProjects(cell: MonthCell): { name: string; color: string }[] {
  const seen = new Set<number>()
  const result: { name: string; color: string }[] = []
  for (const entry of cell.entries) {
    if (!seen.has(entry.project_id)) {
      seen.add(entry.project_id)
      result.push({
        name: getProjectName(entry.project_id),
        color: getProjectColor(entry.project_id)
      })
    }
  }
  return result
}

// -- Format helpers --
function formatDuration(seconds: number): string {
  const h = Math.floor(seconds / 3600)
  const m = Math.floor((seconds % 3600) / 60)
  if (h > 0) {
    return `${h}h ${m}m`
  }
  return `${m}m`
}

function formatDurationShort(seconds: number): string {
  const h = Math.floor(seconds / 3600)
  const m = Math.floor((seconds % 3600) / 60)
  if (h > 0 && m > 0) return `${h}h${m}m`
  if (h > 0) return `${h}h`
  return `${m}m`
}

function replayEntry(entry: TimeEntry) {
  if (!timerStore.isStopped) {
    toastStore.info('Timer is already running. Stop it first.')
    return
  }
  timerStore.setProject(entry.project_id)
  timerStore.setTask(entry.task_id || null)
  timerStore.setDescription(entry.description || '')
  timerStore.setBillable(entry.billable ?? 1)
  timerStore.start()
  router.push('/timer')
}

// -- Date helpers --
function getMonday(date: Date): Date {
  const d = new Date(date)
  const day = d.getDay()
  const diff = day === 0 ? -6 : 1 - day
  d.setDate(d.getDate() + diff)
  d.setHours(0, 0, 0, 0)
  return d
}

function formatISODate(date: Date): string {
  const y = date.getFullYear()
  const m = String(date.getMonth() + 1).padStart(2, '0')
  const d = String(date.getDate()).padStart(2, '0')
  return `${y}-${m}-${d}`
}

function isToday(date: Date): boolean {
  const today = new Date()
  return formatISODate(date) === formatISODate(today)
}

function isTodayStr(dateStr: string): boolean {
  return dateStr === formatISODate(new Date())
}

function formatDayHeader(date: Date): string {
  const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
  return `${dayNames[date.getDay()]} ${date.getDate()}`
}

function formatDayHeaderLong(date: Date): string {
  const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
  const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
  return `${dayNames[date.getDay()]}, ${monthNames[date.getMonth()]} ${date.getDate()}`
}

function formatHourLabel(hour: number): string {
  if (hour === 0) return '12am'
  if (hour === 12) return '12pm'
  if (hour < 12) return `${hour}am`
  return `${hour - 12}pm`
}

function hexToRgba(hex: string, alpha: number): string {
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
  if (!result) return `rgba(107, 114, 128, ${alpha})`
  const r = parseInt(result[1], 16)
  const g = parseInt(result[2], 16)
  const b = parseInt(result[3], 16)
  return `rgba(${r}, ${g}, ${b}, ${alpha})`
}

// -- Current time indicator --
function updateCurrentTime() {
  const now = new Date()
  const currentHour = now.getHours() + now.getMinutes() / 60

  if (currentHour >= HOUR_START && currentHour <= HOUR_END + 1) {
    currentTimeOffset.value = (currentHour - HOUR_START) * HOUR_HEIGHT
  } else {
    currentTimeOffset.value = null
  }
}

function scrollToCurrentTime() {
  const container = currentView.value === 'day' ? dayScrollContainer.value : scrollContainer.value
  if (!container) return
  const now = new Date()
  const currentHour = now.getHours()
  const targetHour = Math.max(currentHour - 1, HOUR_START)
  const scrollTop = (targetHour - HOUR_START) * HOUR_HEIGHT
  container.scrollTop = scrollTop
}

// -- Drag-to-create (Task 32) --
function getMouseHourMinute(event: MouseEvent): { hour: number; minutes: number } {
  const target = event.currentTarget as HTMLElement
  const rect = target.getBoundingClientRect()
  const y = event.clientY - rect.top
  const totalMinutes = (y / HOUR_HEIGHT) * 60
  // Snap to 15-minute increments, clamp to 0-1440 (24h)
  const snapped = Math.min(Math.max(Math.round(totalMinutes / 15) * 15, 0), 24 * 60)
  const hour = Math.min(Math.floor(snapped / 60), 23)
  const minutes = hour === 23 ? Math.min(snapped - 23 * 60, 45) : snapped % 60
  return { hour, minutes }
}

function onGridMouseDown(event: MouseEvent, dayIndex: number, view: 'week' | 'day') {
  // Only left mouse button, and not on an entry
  if (event.button !== 0) return
  if (movingEntryId.value || resizingEntryId.value) return

  const { hour, minutes } = getMouseHourMinute(event)
  isDragging.value = true
  dragView.value = view
  dragStart.value = { day: dayIndex, hour, minutes }
  dragEnd.value = { day: dayIndex, hour, minutes }
}

function onGridMouseMove(event: MouseEvent, dayIndex: number, _view: 'week' | 'day') {
  if (!isDragging.value || !dragStart.value) return
  if (movingEntryId.value || resizingEntryId.value) return

  const { hour, minutes } = getMouseHourMinute(event)
  dragEnd.value = { day: dayIndex, hour, minutes }
}

function onGridMouseUp() {
  if (!isDragging.value || !dragStart.value || !dragEnd.value) {
    isDragging.value = false
    return
  }

  // Check if there's meaningful drag distance (at least 15 min)
  const startMin = dragStart.value.hour * 60 + dragStart.value.minutes
  const endMin = dragEnd.value.hour * 60 + dragEnd.value.minutes
  if (Math.abs(endMin - startMin) < 15) {
    isDragging.value = false
    dragStart.value = null
    dragEnd.value = null
    return
  }

  isDragging.value = false
  openQuickAdd()
}

function getDragDayIndex(): number {
  return dragStart.value?.day ?? -1
}

function getDragPreviewStyle(): Record<string, string> {
  if (!dragStart.value || !dragEnd.value) return { display: 'none' }

  const startMin = dragStart.value.hour * 60 + dragStart.value.minutes
  const endMin = dragEnd.value.hour * 60 + dragEnd.value.minutes
  const topMin = Math.min(startMin, endMin)
  const bottomMin = Math.max(startMin, endMin)

  const top = (topMin / 60) * HOUR_HEIGHT
  const height = Math.max(((bottomMin - topMin) / 60) * HOUR_HEIGHT, 12)

  return {
    top: `${top}px`,
    height: `${height}px`,
    backgroundColor: 'var(--color-accent)',
    opacity: '0.2',
    borderRadius: '4px'
  }
}

function openQuickAdd() {
  showQuickAdd.value = true
  quickAddProject.value = null
  quickAddDescription.value = ''

  nextTick(() => {
    if (quickAddRef.value) {
      // Position near the drag area
      const zoom = getZoomFactor()
      const style: Record<string, string> = {
        position: 'fixed',
        zIndex: '9999',
      }

      // Position at center of viewport
      const vpW = window.innerWidth
      const vpH = window.innerHeight
      style.top = `${(vpH / 2 - 120) / zoom}px`
      style.left = `${(vpW / 2 - 144) / zoom}px`

      quickAddStyle.value = style

      activateTrap(quickAddRef.value, {
        onDeactivate: cleanupQuickAddState
      })
    }
  })
}

// Quick-add time label
const quickAddTimeLabel = computed(() => {
  if (!dragStart.value || !dragEnd.value) return ''
  const startMin = dragStart.value.hour * 60 + dragStart.value.minutes
  const endMin = dragEnd.value.hour * 60 + dragEnd.value.minutes
  const topMin = Math.min(startMin, endMin)
  const bottomMin = Math.max(startMin, endMin)

  const formatTime = (min: number) => {
    const h = Math.floor(min / 60)
    const m = min % 60
    const ampm = h >= 12 ? 'pm' : 'am'
    const h12 = h === 0 ? 12 : h > 12 ? h - 12 : h
    return `${h12}:${String(m).padStart(2, '0')}${ampm}`
  }

  return `${formatTime(topMin)} - ${formatTime(bottomMin)} (${bottomMin - topMin}min)`
})

function cleanupQuickAddState() {
  showQuickAdd.value = false
  dragStart.value = null
  dragEnd.value = null
}

function cancelQuickAdd() {
  deactivateTrap()
  cleanupQuickAddState()
}

async function saveQuickAdd() {
  if (!quickAddProject.value || !dragStart.value || !dragEnd.value) return

  const startMin = dragStart.value.hour * 60 + dragStart.value.minutes
  const endMin = dragEnd.value.hour * 60 + dragEnd.value.minutes
  const topMin = Math.min(startMin, endMin)
  const bottomMin = Math.max(startMin, endMin)
  const durationSeconds = (bottomMin - topMin) * 60

  // Determine the date for this entry
  let entryDate: string
  if (dragView.value === 'day') {
    entryDate = currentDay.value
  } else {
    const dayDate = weekDays.value[dragStart.value.day]
    entryDate = formatISODate(dayDate)
  }

  const startHour = Math.floor(topMin / 60)
  const startMinute = topMin % 60
  const entryDateObj = new Date(entryDate + 'T00:00:00')
  entryDateObj.setHours(startHour, startMinute, 0, 0)
  const startTime = entryDateObj.toISOString()

  try {
    await entriesStore.createEntry({
      project_id: quickAddProject.value,
      description: quickAddDescription.value || undefined,
      start_time: startTime,
      duration: durationSeconds,
      billable: 0
    })
    cancelQuickAdd()
    await refetchCurrentView()
    toastStore.success('Entry created')
  } catch (e) {
    toastStore.error('Failed to create entry')
  }
}

// -- Drag-to-move (Task 33) --
function startMoveEntry(event: MouseEvent, entry: TimeEntry, _dayIndex: number) {
  if (event.button !== 0 || !entry.id) return
  event.preventDefault()

  movingEntryId.value = entry.id
  moveEntry = { ...entry }
  moveStartY = event.clientY
  moveOriginalStartTime = entry.start_time
  moveOriginalDuration = entry.duration
  dragOffsetMinutes.value = 0

  document.addEventListener('mousemove', onDocumentMouseMove)
  document.addEventListener('mouseup', onDocumentMouseUp)
}

function onDocumentMouseMove(event: MouseEvent) {
  if (movingEntryId.value && moveEntry) {
    const deltaY = event.clientY - moveStartY
    const deltaMinutes = Math.round((deltaY / HOUR_HEIGHT) * 60 / 15) * 15
    if (deltaMinutes !== dragOffsetMinutes.value) {
      dragOffsetMinutes.value = deltaMinutes
      const origStart = new Date(moveOriginalStartTime)
      origStart.setMinutes(origStart.getMinutes() + deltaMinutes)
      const idx = entriesStore.entries.findIndex(e => e.id === movingEntryId.value)
      if (idx !== -1) {
        entriesStore.entries[idx] = {
          ...entriesStore.entries[idx],
          start_time: origStart.toISOString()
        }
      }
    }
    return
  }

  if (resizingEntryId.value && moveEntry) {
    const deltaY = event.clientY - moveStartY
    const deltaMinutes = Math.round((deltaY / HOUR_HEIGHT) * 60 / 15) * 15
    if (deltaMinutes !== dragOffsetMinutes.value) {
      dragOffsetMinutes.value = deltaMinutes
      const newDuration = Math.max(moveOriginalDuration + deltaMinutes * 60, 900)
      const idx = entriesStore.entries.findIndex(e => e.id === resizingEntryId.value)
      if (idx !== -1) {
        entriesStore.entries[idx] = {
          ...entriesStore.entries[idx],
          duration: newDuration
        }
      }
    }
    return
  }
}

function onDocumentMouseUp() {
  document.removeEventListener('mousemove', onDocumentMouseMove)
  document.removeEventListener('mouseup', onDocumentMouseUp)

  if (movingEntryId.value) {
    finishMoveEntry()
  } else if (resizingEntryId.value) {
    finishResizeEntry()
  }
}

async function finishMoveEntry() {
  if (!moveEntry || !movingEntryId.value) return
  const entryId = movingEntryId.value

  if (dragOffsetMinutes.value === 0) {
    movingEntryId.value = null
    moveEntry = null
    return
  }

  const origStart = new Date(moveOriginalStartTime)
  origStart.setMinutes(origStart.getMinutes() + dragOffsetMinutes.value)

  const idx = entriesStore.entries.findIndex(e => e.id === entryId)
  if (idx === -1) {
    movingEntryId.value = null
    moveEntry = null
    return
  }

  const updatedEntry = {
    ...entriesStore.entries[idx],
    start_time: origStart.toISOString()
  }

  movingEntryId.value = null
  moveEntry = null

  try {
    await entriesStore.updateEntry(updatedEntry)
    liveAnnouncement.value = `Entry moved to ${origStart.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}`
    await refetchCurrentView()
  } catch (e) {
    // Restore original state immediately
    const restoreIdx = entriesStore.entries.findIndex(en => en.id === entryId)
    if (restoreIdx !== -1) {
      entriesStore.entries[restoreIdx] = {
        ...entriesStore.entries[restoreIdx],
        start_time: moveOriginalStartTime
      }
    }
    toastStore.error('Failed to move entry')
    await refetchCurrentView()
  }
}

// -- Drag-to-resize (Task 33) --
function startResize(event: MouseEvent, entry: TimeEntry) {
  if (event.button !== 0 || !entry.id) return
  event.preventDefault()

  resizingEntryId.value = entry.id
  moveEntry = { ...entry }
  moveStartY = event.clientY
  moveOriginalStartTime = entry.start_time
  moveOriginalDuration = entry.duration
  dragOffsetMinutes.value = 0

  document.addEventListener('mousemove', onDocumentMouseMove)
  document.addEventListener('mouseup', onDocumentMouseUp)
}

async function finishResizeEntry() {
  if (!moveEntry || !resizingEntryId.value) return
  const entryId = resizingEntryId.value

  if (dragOffsetMinutes.value === 0) {
    resizingEntryId.value = null
    moveEntry = null
    return
  }

  const newDuration = Math.max(moveOriginalDuration + dragOffsetMinutes.value * 60, 900)

  const idx = entriesStore.entries.findIndex(e => e.id === entryId)
  if (idx === -1) {
    resizingEntryId.value = null
    moveEntry = null
    return
  }

  const updatedEntry = {
    ...entriesStore.entries[idx],
    duration: newDuration
  }

  resizingEntryId.value = null
  moveEntry = null

  try {
    await entriesStore.updateEntry(updatedEntry)
    liveAnnouncement.value = `Entry resized to ${formatDuration(newDuration)}`
    await refetchCurrentView()
  } catch (e) {
    // Restore original duration immediately
    const restoreIdx = entriesStore.entries.findIndex(en => en.id === entryId)
    if (restoreIdx !== -1) {
      entriesStore.entries[restoreIdx] = {
        ...entriesStore.entries[restoreIdx],
        duration: moveOriginalDuration
      }
    }
    toastStore.error('Failed to resize entry')
    await refetchCurrentView()
  }
}

// -- Keyboard move/resize on entry blocks (Task 33) --
function onEntryKeydown(event: KeyboardEvent, entry: TimeEntry) {
  if (!entry.id) return

  if (event.key === 'Enter') {
    // Open quick-add at this entry's time position
    event.preventDefault()
    const start = new Date(entry.start_time)
    const startMinutes = start.getHours() * 60 + start.getMinutes()
    const endMinutes = startMinutes + Math.floor(entry.duration / 60)

    // Determine day index
    let dayIndex = 0
    if (currentView.value === 'week') {
      const entryDateStr = formatISODate(start)
      dayIndex = weekDays.value.findIndex(d => formatISODate(d) === entryDateStr)
      if (dayIndex < 0) dayIndex = 0
    }

    dragView.value = currentView.value === 'day' ? 'day' : 'week'
    dragStart.value = { day: dayIndex, hour: Math.floor(endMinutes / 60), minutes: endMinutes % 60 }
    dragEnd.value = { day: dayIndex, hour: Math.floor((endMinutes + 60) / 60), minutes: (endMinutes + 60) % 60 }
    openQuickAdd()
    return
  }

  const isShift = event.shiftKey

  if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
    event.preventDefault()
    const delta = event.key === 'ArrowUp' ? -15 : 15

    if (isShift) {
      // Resize
      resizeEntryByKeyboard(entry, delta)
    } else {
      // Move
      moveEntryByKeyboard(entry, delta)
    }
  }
}

async function moveEntryByKeyboard(entry: TimeEntry, deltaMinutes: number) {
  const start = new Date(entry.start_time)
  const originalDate = formatISODate(start)
  start.setMinutes(start.getMinutes() + deltaMinutes)

  // Clamp to same day - don't cross midnight
  if (formatISODate(start) !== originalDate) {
    liveAnnouncement.value = 'Cannot move entry past day boundary'
    return
  }

  const updatedEntry: TimeEntry = {
    ...entry,
    start_time: start.toISOString()
  }

  try {
    await entriesStore.updateEntry(updatedEntry)
    liveAnnouncement.value = `Entry moved to ${start.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })}`
    await refetchCurrentView()
  } catch (e) {
    toastStore.error('Failed to move entry')
  }
}

async function resizeEntryByKeyboard(entry: TimeEntry, deltaMinutes: number) {
  const newDuration = Math.max(entry.duration + deltaMinutes * 60, 900) // min 15 min

  const updatedEntry: TimeEntry = {
    ...entry,
    duration: newDuration
  }

  try {
    await entriesStore.updateEntry(updatedEntry)
    liveAnnouncement.value = `Entry duration changed to ${formatDuration(newDuration)}`
    await refetchCurrentView()
  } catch (e) {
    toastStore.error('Failed to resize entry')
  }
}

// -- Watch view changes to fetch data --
watch(currentView, (newView) => {
  if (newView === 'week') {
    fetchWeekEntries()
    nextTick(() => scrollToCurrentTime())
  } else if (newView === 'day') {
    fetchDayEntries()
    nextTick(() => scrollToCurrentTime())
  } else {
    fetchMonthEntries()
  }
})

// -- Lifecycle --
onMounted(async () => {
  await Promise.all([
    projectsStore.fetchProjects(),
    fetchWeekEntries()
  ])

  updateCurrentTime()
  timeInterval = setInterval(updateCurrentTime, 60_000)

  nextTick(() => scrollToCurrentTime())
})

onUnmounted(() => {
  if (timeInterval) {
    clearInterval(timeInterval)
    timeInterval = null
  }
  document.removeEventListener('mousemove', onDocumentMouseMove)
  document.removeEventListener('mouseup', onDocumentMouseUp)
})
</script>
