<template>
  <view class="private-page">
    <view class="tab-bar">
      <view
        v-for="item in typeOptions"
        :key="item.value"
        class="tab-item"
        :class="{ active: courtType === item.value }"
        @tap="selectType(item.value)"
      >{{ item.label }}</view>
    </view>

    <scroll-view scroll-x class="date-bar" :show-scrollbar="false">
      <view class="date-list">
        <view
          v-for="item in dateOptions"
          :key="item.value"
          class="date-chip"
          :class="{ active: selectedDate === item.value }"
          @tap="selectDate(item.value)"
        >
          <text class="date-label">{{ item.label }}</text>
          <text class="date-day">{{ item.day }}</text>
        </view>
      </view>
    </scroll-view>

    <view class="grid-wrapper">
      <view v-if="isLoading && !grid" class="state-box">加载中...</view>
      <view v-else-if="loadError" class="state-box">{{ loadError }}</view>
      <view v-else-if="!grid || !grid.courts.length" class="state-box">暂无场地数据</view>
      <scroll-view
        v-else
        scroll-x
        scroll-y
        enable-flex
        class="grid-scroll"
        :show-scrollbar="false"
      >
        <view class="grid-inner">
          <view class="grid-header-row">
            <view class="corner-cell">时间</view>
            <view
              v-for="court in grid.courts"
              :key="court.id"
              class="court-header-cell"
            >{{ court.name }}</view>
          </view>
          <view
            v-for="(slot, si) in grid.time_slots"
            :key="si"
            class="grid-row"
          >
            <view class="time-label-cell">{{ slot.start }}</view>
            <view
              v-for="court in grid.courts"
              :key="court.id"
              class="grid-cell"
              :class="getCellClass(court.id, si)"
              @tap="toggleCell(court.id, si)"
            ></view>
          </view>
          <view
            v-if="currentTimeLineVisible && currentTimeTop >= 0"
            class="current-time-line"
            :style="{ top: currentTimeTop + 'rpx' }"
          >
            <text class="current-time-label">{{ now.format('HH:mm') }}</text>
          </view>
        </view>
      </scroll-view>
    </view>

    <view class="action-bar">
      <button v-if="selectedCourtCount === 0" class="primary-button submit-btn-full" @tap="goConfirm">预约</button>
      <template v-else>
        <view class="selection-info">
          <text class="selection-main">{{ selectedCourtCount }}个场地 {{ selectionTimeText }}</text>
          <text class="selection-sub">¥{{ selectionAmount }}</text>
        </view>
        <button class="primary-button submit-btn" :disabled="!canConfirm" @tap="goConfirm">预约</button>
      </template>
    </view>
  </view>
</template>

<script setup>
import { computed, onMounted, ref } from 'vue'
import dayjs from 'dayjs'
import { privateBookingApi } from '@/modules/comprehensive/api/privateBooking'

const HEADER_HEIGHT = 75
const CELL_HEIGHT = 107
const TIME_COL_WIDTH = 128
const COURT_COL_WIDTH = 149

const typeOptions = [
  { label: '羽毛球', value: 'badminton' },
  { label: '篮球', value: 'basketball' }
]

const courtType = ref('badminton')
const selectedDate = ref(dayjs().format('YYYY-MM-DD'))
const grid = ref(null)
const isLoading = ref(false)
const loadError = ref('')
const selectedCells = ref({})
const now = ref(dayjs())
let loadRequestId = 0

const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
const dateOptions = computed(() => {
  const today = dayjs()
  return Array.from({ length: 7 }).map((_, i) => {
    const d = today.add(i, 'day')
    return {
      label: i === 0 ? '今天' : i === 1 ? '明天' : weekDays[d.day()],
      day: `${d.month() + 1}/${d.date()}`,
      value: d.format('YYYY-MM-DD')
    }
  })
})

const openTime = computed(() => normalizeClock(grid.value?.open_time))
const closeTime = computed(() => normalizeClock(grid.value?.close_time))

const occupationMap = computed(() => {
  const map = {}
  if (!grid.value) return map
  for (const occ of grid.value.occupations) {
    if (!map[occ.court_id]) map[occ.court_id] = []
    map[occ.court_id].push(occ)
  }
  return map
})

const cellStatus = computed(() => {
  if (!grid.value) return {}
  const status = {}
  const isToday = selectedDate.value === dayjs().format('YYYY-MM-DD')
  const currentTime = now.value

  for (const court of grid.value.courts) {
    status[court.id] = {}
    const occs = occupationMap.value[court.id] || []

    for (let si = 0; si < grid.value.time_slots.length; si++) {
      const slot = grid.value.time_slots[si]
      const slotStart = dayjs(`${selectedDate.value} ${slot.start}`)
      const slotEnd = dayjs(`${selectedDate.value} ${slot.end}`)

      if (isToday && slotEnd.isBefore(currentTime)) {
        status[court.id][si] = 'past'
        continue
      }

      const isOccupied = occs.some((occ) => {
        const occStart = dayjs(occ.start_at)
        const occEnd = dayjs(occ.end_at)
        return occStart.isBefore(slotEnd) && occEnd.isAfter(slotStart)
      })

      status[court.id][si] = isOccupied ? 'occupied' : 'available'
    }
  }
  return status
})

const currentTimeLineVisible = computed(() => {
  if (!grid.value) return false
  return selectedDate.value === dayjs().format('YYYY-MM-DD')
})

const currentTimeTop = computed(() => {
  if (!grid.value || !currentTimeLineVisible.value) return -1
  const openMin = parseTimeToMinutes(grid.value.open_time)
  const closeMin = parseTimeToMinutes(grid.value.close_time)
  const currentMin = now.value.hour() * 60 + now.value.minute()
  if (currentMin < openMin || currentMin > closeMin) return -1
  return HEADER_HEIGHT + ((currentMin - openMin) / 30) * CELL_HEIGHT
})

const selectedCourtCount = computed(() => Object.keys(selectedCells.value).length)
const selectedTotalSlots = computed(() =>
  Object.values(selectedCells.value).reduce((sum, indices) => sum + indices.length, 0)
)

const canConfirm = computed(() => selectedCourtCount.value > 0 && selectedTotalSlots.value > 0)

const selectionTimeText = computed(() => {
  const allSlots = []
  for (const indices of Object.values(selectedCells.value)) {
    allSlots.push(...indices)
  }
  if (!allSlots.length) return ''
  const sorted = allSlots.sort((a, b) => a - b)
  const startSlot = grid.value.time_slots[sorted[0]]
  const endSlot = grid.value.time_slots[sorted[sorted.length - 1]]
  return `${startSlot.start}-${endSlot.end}`
})

const selectionAmount = computed(() => {
  const priceVal = parseFloat(grid.value?.unit_price || '0')
  return (priceVal * selectedTotalSlots.value).toFixed(2)
})

onMounted(loadGrid)

async function loadGrid() {
  const requestId = ++loadRequestId
  if (!grid.value) isLoading.value = true
  loadError.value = ''
  try {
    const data = await privateBookingApi.getPrivateBookingGrid({
      date: selectedDate.value,
      courtType: courtType.value
    })
    if (requestId !== loadRequestId) return
    grid.value = data
    selectedCells.value = {}
  } catch (error) {
    if (requestId !== loadRequestId) return
    if (!grid.value) loadError.value = error?.message || '加载失败，请稍后重试'
  } finally {
    if (requestId === loadRequestId) isLoading.value = false
  }
}

function selectType(type) {
  if (courtType.value === type) return
  courtType.value = type
  loadGrid()
}

function selectDate(date) {
  if (selectedDate.value === date) return
  selectedDate.value = date
  now.value = dayjs()
  loadGrid()
}

function getCellClass(courtId, slotIndex) {
  const status = cellStatus.value[courtId]?.[slotIndex] || 'available'
  const isSelected = selectedCells.value[courtId]?.includes(slotIndex)
  if (isSelected) return { 'cell-selected': true }
  return { [`cell-${status}`]: true }
}

function toggleCell(courtId, slotIndex) {
  const status = cellStatus.value[courtId]?.[slotIndex]
  if (status !== 'available') return

  const current = selectedCells.value[courtId] || []

  if (current.includes(slotIndex)) {
    const next = current.filter((i) => i !== slotIndex)
    if (next.length === 0) {
      const next2 = { ...selectedCells.value }
      delete next2[courtId]
      selectedCells.value = next2
      return
    }
    const sorted = next.sort((a, b) => a - b)
    const isContiguous = sorted.every((val, idx) => idx === 0 || val === sorted[idx - 1] + 1)
    if (isContiguous) {
      selectedCells.value = { ...selectedCells.value, [courtId]: sorted }
    } else {
      uni.showToast({ title: '不能取消中间时段', icon: 'none' })
    }
    return
  }

  if (current.length === 0) {
    selectedCells.value = { ...selectedCells.value, [courtId]: [slotIndex] }
    return
  }

  const minIdx = Math.min(...current)
  const maxIdx = Math.max(...current)

  if (slotIndex === minIdx - 1 || slotIndex === maxIdx + 1) {
    selectedCells.value = {
      ...selectedCells.value,
      [courtId]: [...current, slotIndex].sort((a, b) => a - b)
    }
  } else {
    selectedCells.value = { ...selectedCells.value, [courtId]: [slotIndex] }
  }
}

function goConfirm() {
  if (!canConfirm.value) return

  const items = Object.entries(selectedCells.value).map(([courtIdStr, slotIndices]) => {
    const courtId = parseInt(courtIdStr)
    const sorted = [...slotIndices].sort((a, b) => a - b)
    const startSlot = grid.value.time_slots[sorted[0]]
    const endSlot = grid.value.time_slots[sorted[sorted.length - 1]]
    return {
      court_id: courtId,
      start_at: buildDateTime(selectedDate.value, startSlot.start),
      end_at: buildDateTime(selectedDate.value, endSlot.end)
    }
  })

  const payload = encodeURIComponent(JSON.stringify({
    court_type: courtType.value,
    items
  }))
  uni.navigateTo({ url: `/pages/comprehensive/private/confirm?payload=${payload}` })
}

function buildDateTime(date, time) {
  return `${date}T${time}:00${dayjs().format('Z')}`
}

function parseTimeToMinutes(timeStr) {
  if (!timeStr) return 0
  const parts = timeStr.split(':')
  return parseInt(parts[0]) * 60 + parseInt(parts[1])
}

function normalizeClock(value) {
  if (!value) return '--:--'
  return value.slice(0, 5)
}

defineExpose({
  refresh() {
    now.value = dayjs()
    loadGrid()
  }
})
</script>

<style lang="scss" scoped>
.private-page {
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
  background: $bg-color-soft;
}

.tab-bar {
  flex-shrink: 0;
  display: flex;
  height: 88rpx;
  background: $bg-color-main;
  border-bottom: 1rpx solid $border-color;
}

.tab-item {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 28rpx;
  font-weight: 700;
  color: $text-color-muted;
  transition: all 0.2s ease;
}

.tab-item.active {
  color: $text-color-main;
  font-weight: 900;
  position: relative;
}

.tab-item.active::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 48rpx;
  height: 4rpx;
  border-radius: 2rpx;
  background: $text-color-main;
}

.date-bar {
  flex-shrink: 0;
  background: $bg-color-main;
  border-bottom: 1rpx solid $border-color;
  white-space: nowrap;
}

.date-list {
  display: inline-flex;
  padding: $spacing-sm $spacing-xs;
  gap: $spacing-xs;
}

.date-chip {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 96rpx;
  height: 88rpx;
  border-radius: $border-radius-sm;
  gap: 4rpx;
}

.date-chip.active {
  background: $text-color-main;
}

.date-label {
  font-size: 22rpx;
  font-weight: 700;
  color: $text-color-muted;
}

.date-chip.active .date-label {
  color: #FFFFFF;
}

.date-day {
  font-size: 24rpx;
  font-weight: 800;
  color: $text-color-main;
}

.date-chip.active .date-day {
  color: #FFFFFF;
}

.grid-wrapper {
  flex: 1;
  min-height: 0;
  overflow: hidden;
}

.grid-scroll {
  width: 100%;
  height: 100%;
}

.grid-inner {
  position: relative;
  display: inline-block;
}

.grid-header-row {
  display: flex;
  position: sticky;
  top: 0;
  z-index: 10;
}

.corner-cell {
  width: 128rpx;
  flex-shrink: 0;
  height: 75rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  background: $bg-color-main;
  border-right: 1rpx solid $border-color;
  border-bottom: 1rpx solid $border-color;
  color: $text-color-muted;
  font-size: 20rpx;
  font-weight: 800;
  position: sticky;
  top: 0;
  left: 0;
  z-index: 15;
}

.court-header-cell {
  width: 149rpx;
  flex-shrink: 0;
  height: 75rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  background: $bg-color-main;
  border-right: 1rpx solid $border-color;
  border-bottom: 1rpx solid $border-color;
  color: $text-color-regular;
  font-size: 20rpx;
  font-weight: 800;
  @include text-ellipsis;
}

.grid-row {
  display: flex;
}

.time-label-cell {
  width: 128rpx;
  flex-shrink: 0;
  height: 107rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  background: $bg-color-main;
  border-right: 1rpx solid $border-color;
  border-bottom: 1rpx solid $bg-color-hover;
  color: $text-color-muted;
  font-size: 20rpx;
  font-weight: 700;
  position: sticky;
  left: 0;
  z-index: 5;
}

.grid-cell {
  width: 149rpx;
  flex-shrink: 0;
  height: 107rpx;
  border-right: 1rpx solid $bg-color-hover;
  border-bottom: 1rpx solid $bg-color-hover;
}

.cell-available {
  background: $bg-color-main;
}

.cell-occupied {
  background: $color-danger-light;
}

.cell-past {
  background: $bg-color-hover;
}

.cell-selected {
  background: $color-primary;
}

.current-time-line {
  position: absolute;
  left: 0;
  right: 0;
  height: 3rpx;
  background: $color-danger;
  z-index: 8;
}

.current-time-label {
  position: absolute;
  left: 4rpx;
  top: -28rpx;
  padding: 2rpx 8rpx;
  border-radius: 4rpx;
  background: $color-danger;
  color: #FFFFFF;
  font-size: 18rpx;
  font-weight: 700;
  line-height: 1.2;
}

.action-bar {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: $spacing-md;
  padding: $spacing-sm $spacing-md calc($spacing-sm + env(safe-area-inset-bottom));
  background: rgba(255, 255, 255, 0.96);
  border-top: 1rpx solid $border-color;
}

.selection-info {
  flex: 1;
  min-width: 0;
}

.selection-main {
  display: block;
  color: $text-color-main;
  font-size: 28rpx;
  font-weight: 900;
}

.selection-sub {
  display: block;
  margin-top: 4rpx;
  color: $text-color-muted;
  font-size: 22rpx;
}

.submit-btn {
  width: 200rpx;
}

.submit-btn-full {
  flex: 1;
}

.state-box {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  color: $text-color-light;
  font-size: 24rpx;
}
</style>
