<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { NButton, NSelect } from 'naive-ui'

export interface ContributionHeatmapCell {
  date: string
  count: number
}

interface MonthStat {
  key: string
  label: string
  totalCount: number
  activeDays: number
}

const today = new Date()
const currentYear = today.getFullYear()
const currentMonth = today.getMonth() + 1

const props = withDefaults(defineProps<{
  cells: ContributionHeatmapCell[]
  totalCount: number
  activeDays: number
  currentStreak: number
  bestStreak: number
  activeDate?: string | null
  title?: string
  subtitle?: string
}>(), {
  activeDate: null,
  title: '上传活跃度',
  subtitle: '按上传日期统计',
})

const emit = defineEmits<{
  selectDate: [date: string]
}>()

const viewMode = ref<'year' | 'month'>('year')
const selectedYear = ref(currentYear)
const selectedMonth = ref(currentMonth)
const hoveredMonthKey = ref<string | null>(null)

function parseLocalDate(dateString: string) {
  const [year, month, day] = dateString.split('-').map(Number)
  return new Date(year, (month || 1) - 1, day || 1)
}

function formatDayKey(date: Date) {
  const month = `${date.getMonth() + 1}`.padStart(2, '0')
  const day = `${date.getDate()}`.padStart(2, '0')
  return `${date.getFullYear()}-${month}-${day}`
}

function toMonthKey(dateString: string) {
  const date = parseLocalDate(dateString)
  return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`
}

function startOfDay(date: Date) {
  return new Date(date.getFullYear(), date.getMonth(), date.getDate())
}

function startOfWeek(date: Date) {
  const result = startOfDay(date)
  const weekday = result.getDay()
  const offset = weekday === 0 ? 6 : weekday - 1
  result.setDate(result.getDate() - offset)
  return result
}

const dailyCounts = computed(() => {
  const counts = new Map<string, number>()
  for (const cell of props.cells) {
    counts.set(cell.date, cell.count)
  }
  return counts
})

const availableYears = computed(() => {
  const years = new Set<number>([currentYear])
  for (const cell of props.cells) {
    years.add(parseLocalDate(cell.date).getFullYear())
  }
  return Array.from(years).sort((a, b) => b - a)
})

const yearOptions = computed(() => {
  return availableYears.value.map(year => ({
    label: `${year}年`,
    value: year,
  }))
})

const monthOptions = Array.from({ length: 12 }, (_, index) => ({
  label: `${index + 1}月`,
  value: index + 1,
}))

watch(availableYears, years => {
  if (!years.includes(selectedYear.value)) {
    selectedYear.value = years[0] || currentYear
  }
}, { immediate: true })

const visibleCells = computed(() => {
  if (viewMode.value === 'year') {
    const start = new Date(selectedYear.value, 0, 1)
    const end = new Date(selectedYear.value, 11, 31)
    const alignedStart = startOfWeek(start)
    const alignedEnd = startOfWeek(end)
    alignedEnd.setDate(alignedEnd.getDate() + 6)

    const cells: Array<ContributionHeatmapCell & { inRange: boolean; isToday: boolean }> = []
    const cursor = new Date(alignedStart)

    while (cursor <= alignedEnd) {
      const key = formatDayKey(cursor)
      const inRange = cursor >= start && cursor <= end
      cells.push({
        date: key,
        count: dailyCounts.value.get(key) || 0,
        inRange,
        isToday: key === formatDayKey(today),
      })
      cursor.setDate(cursor.getDate() + 1)
    }

    return cells
  }

  const start = new Date(selectedYear.value, selectedMonth.value - 1, 1)
  const end = new Date(selectedYear.value, selectedMonth.value, 0)
  const alignedStart = startOfWeek(start)
  const alignedEnd = startOfWeek(end)
  alignedEnd.setDate(alignedEnd.getDate() + 6)

  const cells: Array<ContributionHeatmapCell & { inRange: boolean; isToday: boolean }> = []
  const cursor = new Date(alignedStart)

  while (cursor <= alignedEnd) {
    const key = formatDayKey(cursor)
    const inRange = cursor >= start && cursor <= end
    cells.push({
      date: key,
      count: dailyCounts.value.get(key) || 0,
      inRange,
      isToday: key === formatDayKey(today),
    })
    cursor.setDate(cursor.getDate() + 1)
  }

  return cells
})

const maxCount = computed(() => {
  return visibleCells.value.reduce((max, cell) => Math.max(max, cell.count), 0)
})

function getLevel(count: number) {
  if (count <= 0) return 0

  const max = Math.max(maxCount.value, 1)
  const ratio = count / max

  if (ratio >= 0.75 || count >= 8) return 4
  if (ratio >= 0.5 || count >= 5) return 3
  if (ratio >= 0.25 || count >= 3) return 2
  return 1
}

function getCellClass(cell: { count: number; inRange: boolean }) {
  if (!cell.inRange) return 'bg-transparent border-transparent'

  const level = getLevel(cell.count)
  if (level === 0) return 'bg-zinc-100 border-zinc-200'
  if (level === 1) return 'bg-[#e0f2fe] border-[#bae6fd]'
  if (level === 2) return 'bg-[#7dd3fc] border-[#38bdf8]'
  if (level === 3) return 'bg-[#0ea5e9] border-[#0284c7]'
  return 'bg-[#0f3d63] border-[#0b2f4d]'
}

const weeks = computed(() => {
  const buckets: Array<typeof visibleCells.value> = []
  for (let index = 0; index < visibleCells.value.length; index += 7) {
    buckets.push(visibleCells.value.slice(index, index + 7))
  }
  return buckets
})

const monthStats = computed(() => {
  const stats = new Map<string, MonthStat>()

  for (const cell of visibleCells.value) {
    if (!cell.inRange) continue
    const monthKey = toMonthKey(cell.date)
    const date = parseLocalDate(cell.date)
    const label = `${date.getMonth() + 1}月`
    const existing = stats.get(monthKey)

    if (existing) {
      existing.totalCount += cell.count
      if (cell.count > 0) existing.activeDays += 1
      continue
    }

    stats.set(monthKey, {
      key: monthKey,
      label,
      totalCount: cell.count,
      activeDays: cell.count > 0 ? 1 : 0,
    })
  }

  return stats
})

const monthLabels = computed(() => {
  if (viewMode.value !== 'year') return []

  let lastMonth = -1
  return weeks.value.map(week => {
    const firstInRange = week.find(cell => cell.inRange)
    if (!firstInRange) return null

    const date = parseLocalDate(firstInRange.date)
    const month = date.getMonth()
    const labelMonth = month + 1
    const monthKey = toMonthKey(firstInRange.date)

    if (month === lastMonth) return null

    lastMonth = month
    return {
      key: monthKey,
      label: labelMonth === 1 ? `${selectedYear.value}/1月` : `${labelMonth}月`,
    }
  })
})

const monthPreview = computed(() => {
  if (!hoveredMonthKey.value) return null
  return monthStats.value.get(hoveredMonthKey.value) || null
})

const panelSummary = computed(() => {
  if (viewMode.value === 'year') {
    return `${selectedYear.value} 年完整视图，包含尚未来到的日期。`
  }

  return `${selectedYear.value} 年 ${selectedMonth.value} 月完整视图。`
})

function formatTooltip(cell: ContributionHeatmapCell & { inRange: boolean; isToday: boolean }) {
  const date = parseLocalDate(cell.date)
  const label = date.toLocaleDateString('zh-CN', {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
  })

  if (!cell.inRange) {
    return `${label}：不在当前视图范围内`
  }

  if (cell.count === 0) {
    return `${label}${cell.isToday ? '（今天）' : ''}：当天没有上传`
  }

  return `${label}${cell.isToday ? '（今天）' : ''}：上传 ${cell.count} 张`
}

function handleCellClick(cell: ContributionHeatmapCell & { inRange: boolean }) {
  if (!cell.inRange) return
  emit('selectDate', cell.date)
}

function formatMonthTooltip(monthKey: string) {
  const stats = monthStats.value.get(monthKey)
  if (!stats) return ''
  return `${stats.label}：共上传 ${stats.totalCount} 张，活跃于 ${stats.activeDays} 天`
}
</script>

<template>
  <div class="oc-panel-card border border-slate-200 bg-white p-6 shadow-sm">
    <div class="flex flex-wrap items-start justify-between gap-6">
      <div class="min-w-0 flex-1">
        <h3 class="text-2xl font-bold text-slate-900">{{ title }}</h3>
        <p class="mt-3 text-sm leading-7 text-slate-500">
          共上传 {{ totalCount }} 张，活跃于 {{ activeDays }} 天。颜色越深，代表当天上传的照片越多。
        </p>
        <p class="mt-2 text-sm text-slate-500">{{ panelSummary }}</p>
      </div>

      <div class="flex flex-wrap items-center justify-end gap-3">
        <div class="flex flex-wrap items-center gap-2">
          <NButton
            secondary
            strong
            type="default"
            :class="viewMode === 'year' ? 'oc-panel-button oc-panel-button--teal' : 'oc-panel-button oc-panel-button--neutral'"
            @click="viewMode = 'year'"
          >
            年度视图
          </NButton>
          <NButton
            secondary
            strong
            type="default"
            :class="viewMode === 'month' ? 'oc-panel-button oc-panel-button--teal' : 'oc-panel-button oc-panel-button--neutral'"
            @click="viewMode = 'month'"
          >
            月度视图
          </NButton>
        </div>
        <NSelect
          v-model:value="selectedYear"
          :options="yearOptions"
          class="w-28"
        />
        <NSelect
          v-if="viewMode === 'month'"
          v-model:value="selectedMonth"
          :options="monthOptions"
          class="w-24"
        />
      </div>
    </div>

    <div class="mt-4 flex min-h-6 items-center justify-between gap-4 text-xs text-slate-500">
      <span>点击某一天可筛选当天上传记录。</span>
      <span v-if="monthPreview" class="text-right">
        {{ monthPreview.label }}：{{ monthPreview.totalCount }} 张 / {{ monthPreview.activeDays }} 天
      </span>
      <span v-else class="text-right">
        <template v-if="viewMode === 'year'">悬停月份标签可预览当月上传摘要。</template>
        <template v-else>当前显示整个月的上传情况。</template>
      </span>
    </div>

    <div class="mt-8 overflow-x-auto">
      <div :class="viewMode === 'year' ? 'min-w-[980px]' : 'min-w-[360px]'">
        <div
          v-if="viewMode === 'year'"
          class="mb-2 grid grid-flow-col auto-cols-[14px] gap-1 pl-8 text-[11px] text-slate-400"
        >
          <div
            v-for="(month, index) in monthLabels"
            :key="`${month?.key || 'empty'}-${index}`"
            class="w-[14px] whitespace-nowrap"
            :title="month ? formatMonthTooltip(month.key) : ''"
            @mouseenter="hoveredMonthKey = month?.key || null"
            @mouseleave="hoveredMonthKey = null"
          >
            {{ month?.label || '' }}
          </div>
        </div>

        <div class="flex gap-3">
          <div class="grid grid-rows-7 gap-1 pt-[2px] text-[11px] text-slate-400">
            <span>一</span>
            <span></span>
            <span>三</span>
            <span></span>
            <span>五</span>
            <span></span>
            <span>日</span>
          </div>

          <div class="grid grid-flow-col auto-cols-[14px] gap-1">
            <div
              v-for="(week, weekIndex) in weeks"
              :key="weekIndex"
              class="grid grid-rows-7 gap-1"
            >
              <button
                v-for="cell in week"
                :key="cell.date"
                type="button"
                class="relative h-3.5 w-3.5 border transition-transform hover:scale-125"
                :class="[
                  getCellClass(cell),
                  props.activeDate === cell.date ? 'ring-2 ring-slate-900 ring-offset-1' : '',
                  cell.isToday && cell.inRange ? 'outline outline-1 outline-slate-900 outline-offset-1' : '',
                ]"
                :title="formatTooltip(cell)"
                @click="handleCellClick(cell)"
              ></button>
            </div>
          </div>
        </div>
      </div>
    </div>

    <div class="mt-5 flex items-center justify-end gap-2 text-xs text-slate-500">
      <span>少</span>
      <span class="h-3 w-3 border border-slate-200 bg-slate-100"></span>
      <span class="h-3 w-3 border border-sky-200 bg-sky-100"></span>
      <span class="h-3 w-3 border border-sky-400 bg-sky-300"></span>
      <span class="h-3 w-3 border border-teal-600 bg-teal-500"></span>
      <span class="h-3 w-3 border border-slate-900 bg-slate-900"></span>
      <span>多</span>
    </div>
  </div>
</template>
