<script setup>
import { ref, onMounted, onBeforeUnmount, computed } from 'vue'
import { useCalendarStore } from '@/stores/CalendarStore'
import CalendarHeader from '@/components/CalendarHeader.vue'
import CalendarWeek from '@/components/CalendarWeek.vue'
import Jogwheel from '@/components/Jogwheel.vue'
import EventDialog from '@/components/EventDialog.vue'
import { isoWeekInfo, getLocalizedMonthName, monthAbbr, lunarPhaseSymbol, pad, mondayIndex, daysInclusive, addDaysStr, formatDateRange } from '@/utils/date'
import { toLocalString, fromLocalString } from '@/utils/date'

const calendarStore = useCalendarStore()
const viewport = ref(null)
const eventDialog = ref(null)

// UI state moved from store
const scrollTop = ref(0)
const viewportHeight = ref(600)
const rowHeight = ref(64)
const baseDate = new Date(2024, 0, 1) // Monday

// Selection state moved from store
const selection = ref({ start: null, end: null })
const isDragging = ref(false)
const dragAnchor = ref(null)

const WEEK_MS = 7 * 24 * 60 * 60 * 1000

const minVirtualWeek = computed(() => {
  const date = new Date(calendarStore.minYear, 0, 1)
  const monday = new Date(date)
  monday.setDate(date.getDate() - ((date.getDay() + 6) % 7))
  return Math.floor((monday - baseDate) / WEEK_MS)
})

const maxVirtualWeek = computed(() => {
  const date = new Date(calendarStore.maxYear, 11, 31)
  const monday = new Date(date)
  monday.setDate(date.getDate() - ((date.getDay() + 6) % 7))
  return Math.floor((monday - baseDate) / WEEK_MS)
})

const totalVirtualWeeks = computed(() => {
  return maxVirtualWeek.value - minVirtualWeek.value + 1
})

const initialScrollTop = computed(() => {
  const targetWeekIndex = getWeekIndex(calendarStore.now) - 3
  return (targetWeekIndex - minVirtualWeek.value) * rowHeight.value
})

const selectedDateRange = computed(() => {
  if (!selection.value.start || !selection.value.end) return ''
  return formatDateRange(fromLocalString(selection.value.start), fromLocalString(selection.value.end))
})

const todayString = computed(() => {
  const t = calendarStore.now.toLocaleDateString(undefined, { weekday: 'long', month: 'long', day: 'numeric'}).replace(/,? /, "\n")
  return t.charAt(0).toUpperCase() + t.slice(1)
})

const visibleWeeks = computed(() => {
  const buffer = 10
  const startIdx = Math.floor((scrollTop.value - buffer * rowHeight.value) / rowHeight.value)
  const endIdx = Math.ceil((scrollTop.value + viewportHeight.value + buffer * rowHeight.value) / rowHeight.value)

  const startVW = Math.max(minVirtualWeek.value, startIdx + minVirtualWeek.value)
  const endVW = Math.min(maxVirtualWeek.value, endIdx + minVirtualWeek.value)

  const weeks = []
  for (let vw = startVW; vw <= endVW; vw++) {
    weeks.push(createWeek(vw))
  }
  return weeks
})

const contentHeight = computed(() => {
  return totalVirtualWeeks.value * rowHeight.value
})

// Functions moved from store
function computeRowHeight() {
  const el = document.createElement('div')
  el.style.position = 'absolute'
  el.style.visibility = 'hidden'
  el.style.height = 'var(--cell-h)'
  document.body.appendChild(el)
  const h = el.getBoundingClientRect().height || 64
  el.remove()
  rowHeight.value = Math.round(h)
  return rowHeight.value
}

function getWeekIndex(date) {
  const monday = new Date(date)
  monday.setDate(date.getDate() - mondayIndex(date))
  return Math.floor((monday - baseDate) / WEEK_MS)
}

function getMondayForVirtualWeek(virtualWeek) {
  const monday = new Date(baseDate)
  monday.setDate(monday.getDate() + virtualWeek * 7)
  return monday
}

function createWeek(virtualWeek) {
  const monday = getMondayForVirtualWeek(virtualWeek)
  const weekNumber = isoWeekInfo(monday).week
  const days = []
  const cur = new Date(monday)
  let hasFirst = false
  let monthToLabel = null
  let labelYear = null

  for (let i = 0; i < 7; i++) {
    const dateStr = toLocalString(cur)
    const eventsForDay = calendarStore.events.get(dateStr) || []
    const dow = cur.getDay()
    const isFirst = cur.getDate() === 1
    
    if (isFirst) {
      hasFirst = true
      monthToLabel = cur.getMonth()
      labelYear = cur.getFullYear()
    }

    let displayText = String(cur.getDate())
    if (isFirst) {
      if (cur.getMonth() === 0) {
        displayText = cur.getFullYear()
      } else {
        displayText = monthAbbr[cur.getMonth()].slice(0,3).toUpperCase()
      }
    }

    days.push({
      date: dateStr,
      dayOfMonth: cur.getDate(),
      displayText,
      monthClass: monthAbbr[cur.getMonth()],
      isToday: dateStr === calendarStore.today,
      isWeekend: calendarStore.weekend[dow],
      isFirstDay: isFirst,
      lunarPhase: lunarPhaseSymbol(cur),
      isSelected: selection.value.start && selection.value.end && dateStr >= selection.value.start && dateStr <= selection.value.end,
      events: eventsForDay
    })
    cur.setDate(cur.getDate() + 1)
  }

  let monthLabel = null
  if (hasFirst && monthToLabel !== null) {
    if (labelYear && labelYear <= calendarStore.config.max_year) {
      // Calculate how many weeks this month spans
      let weeksSpan = 0
      const d = new Date(cur)
      d.setDate(cur.getDate() - 1) // Go back to last day of the week we just processed
      
      for (let i = 0; i < 6; i++) {
        d.setDate(cur.getDate() - 1 + i * 7)
        if (d.getMonth() === monthToLabel) weeksSpan++
      }

      const remainingWeeks = Math.max(1, maxVirtualWeek.value - virtualWeek + 1)
      weeksSpan = Math.max(1, Math.min(weeksSpan, remainingWeeks))

      const year = String(labelYear).slice(-2)
      monthLabel = {
        text: `${getLocalizedMonthName(monthToLabel)} '${year}`,
        month: monthToLabel,
        weeksSpan: weeksSpan,
        height: weeksSpan * rowHeight.value
      }
    }
  }

  return {
    virtualWeek,
    weekNumber: pad(weekNumber),
    days,
    monthLabel,
    top: (virtualWeek - minVirtualWeek.value) * rowHeight.value
  }
}

function goToToday() {
  const top = new Date(calendarStore.now)
  top.setDate(top.getDate() - 21)
  const targetWeekIndex = getWeekIndex(top)
  scrollTop.value = (targetWeekIndex - minVirtualWeek.value) * rowHeight.value
  if (viewport.value) {
    viewport.value.scrollTop = scrollTop.value
  }
}

function clearSelection() {
  selection.value = { start: null, end: null }
}

function startDrag(dateStr) {
  if (calendarStore.config.select_days === 0) return
  isDragging.value = true
  dragAnchor.value = dateStr
  selection.value = { start: dateStr, end: dateStr }
}

function updateDrag(dateStr) {
  if (!isDragging.value) return
  const [start, end] = clampRange(dragAnchor.value, dateStr)
  selection.value = { start, end }
}

function endDrag(dateStr) {
  if (!isDragging.value) return
  isDragging.value = false
  const [start, end] = clampRange(dragAnchor.value, dateStr)
  selection.value = { start, end }
}

function clampRange(anchorStr, otherStr) {
  const limit = calendarStore.config.select_days
  const forward = fromLocalString(otherStr) >= fromLocalString(anchorStr)
  const span = daysInclusive(anchorStr, otherStr)
  if (span <= limit) {
    const a = [anchorStr, otherStr].sort()
    return [a[0], a[1]]
  }
  if (forward) return [anchorStr, addDaysStr(anchorStr, limit - 1)]
  return [addDaysStr(anchorStr, -(limit - 1)), anchorStr]
}

const onScroll = () => {
  if (viewport.value) {
    scrollTop.value = viewport.value.scrollTop
  }
}

const handleJogwheelScrollTo = (newScrollTop) => {
  if (viewport.value) {
    viewport.value.scrollTop = newScrollTop
  }
}

onMounted(() => {
  // Compute row height and initialize
  computeRowHeight()
  calendarStore.updateCurrentDate()
  
  if (viewport.value) {
    viewportHeight.value = viewport.value.clientHeight
    viewport.value.scrollTop = initialScrollTop.value
    viewport.value.addEventListener('scroll', onScroll)
  }
  
  // Update time periodically
  const timer = setInterval(() => {
    calendarStore.updateCurrentDate()
  }, 60000) // Update every minute

  onBeforeUnmount(() => {
    clearInterval(timer)
  })
})

onBeforeUnmount(() => {
  if (viewport.value) {
    viewport.value.removeEventListener('scroll', onScroll)
  }
})

const handleDayMouseDown = (dateStr) => {
  startDrag(dateStr)
}

const handleDayMouseEnter = (dateStr) => {
  if (isDragging.value) {
    updateDrag(dateStr)
  }
}

const handleDayMouseUp = (dateStr) => {
  if (isDragging.value) {
    endDrag(dateStr)
    // Show event dialog if we have a selection
    if (selection.value.start && selection.value.end && eventDialog.value) {
      setTimeout(() => eventDialog.value.openCreateDialog(), 50)
    }
  }
}

// Touch event handlers
const handleDayTouchStart = (dateStr) => {
  startDrag(dateStr)
}

const handleDayTouchMove = (dateStr) => {
  if (isDragging.value) {
    updateDrag(dateStr)
  }
}

const handleDayTouchEnd = (dateStr) => {
  if (isDragging.value) {
    endDrag(dateStr)
    // Show event dialog if we have a selection
    if (selection.value.start && selection.value.end && eventDialog.value) {
      setTimeout(() => eventDialog.value.openCreateDialog(), 50)
    }
  }
}

const handleEventClick = (eventInstanceId) => {
  if (eventDialog.value) {
    eventDialog.value.openEditDialog(eventInstanceId)
  }
}
</script>

<template>
  <div class="wrap">
    <header>
      <h1>Calendar</h1>
      <div class="header-controls">
        <div class="today-date" @click="goToToday">{{ todayString }}</div>
      </div>
    </header>
    <CalendarHeader 
      :scroll-top="scrollTop" 
      :row-height="rowHeight" 
      :min-virtual-week="minVirtualWeek" 
    />
    <div class="calendar-container">
      <div class="calendar-viewport" ref="viewport">
                <div class="calendar-content" :style="{ height: contentHeight + 'px' }">
          <CalendarWeek
            v-for="week in visibleWeeks"
            :key="week.virtualWeek"
            :week="week"
            :style="{ top: week.top + 'px' }"
            @day-mousedown="handleDayMouseDown"
            @day-mouseenter="handleDayMouseEnter"
            @day-mouseup="handleDayMouseUp"
            @day-touchstart="handleDayTouchStart"
            @day-touchmove="handleDayTouchMove"
            @day-touchend="handleDayTouchEnd"
            @event-click="handleEventClick"
          />
          <!-- Month labels positioned absolutely -->
          <div
            v-for="week in visibleWeeks"
            :key="`month-${week.virtualWeek}`"
            v-show="week.monthLabel"
            class="month-name-label"
            :style="{ 
              top: week.top + 'px',
              height: week.monthLabel?.height + 'px'
            }"
          >
            <span>{{ week.monthLabel?.text }}</span>
          </div>
        </div>
      </div>
      <!-- Jogwheel as sibling to calendar-viewport -->
      <Jogwheel 
        :total-virtual-weeks="totalVirtualWeeks"
        :row-height="rowHeight"
        :viewport-height="viewportHeight"
        :scroll-top="scrollTop"
        @scroll-to="handleJogwheelScrollTo" 
      />
    </div>
    <EventDialog 
      ref="eventDialog" 
      :selection="selection" 
      @clear-selection="clearSelection"
    />
  </div>
</template>

<style scoped>
.wrap {
  height: 100vh;
  display: flex;
  flex-direction: column;
}

header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

header h1 {
  margin: 0;
  padding: 0;
}
.header-controls {
  display: flex;
  gap: 1rem;
  align-items: center;
}

.today-date {
  cursor: pointer;
  padding: 0.5rem;
  background: var(--today-btn-bg);
  color: var(--today-btn-text);
  border-radius: 4px;
  white-space: pre-line;
  text-align: center;
  font-size: 0.9rem;
}

.today-date:hover {
  background: var(--today-btn-hover-bg);
}

.calendar-container {
  flex: 1;
  display: flex;
  position: relative;
  /* Prevent text selection in calendar */
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}

.calendar-viewport {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  position: relative;
}

.calendar-content {
  position: relative;
  width: 100%;
}

.month-name-label {
  position: absolute;
  right: 0;
  width: 3rem; /* Match jogwheel width */
  font-size: 2em;
  font-weight: 700;
  color: var(--muted);
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 15;
  overflow: visible;
}

.month-name-label > span {
  display: inline-block;
  white-space: nowrap;
  writing-mode: vertical-rl;
  text-orientation: mixed;
  transform: rotate(180deg);
  transform-origin: center;
}
</style>
