<template>
  <view class="page" :style="{ paddingTop: navBarHeight }">
    <CustomNavBar title="会员管理" show-back />

    <!-- Search bar -->
    <view class="filter-bar">
      <input
        class="search-input"
        v-model="searchQuery"
        placeholder="搜索昵称或 OpenID"
        placeholder-style="color:#bbb"
        @confirm="onSearch"
        confirm-type="search"
      />
      <view v-if="searchQuery" class="search-clear" @tap="onClear">
        <text class="search-clear-icon">×</text>
      </view>
      <view class="search-btn" @tap="onSearch">
        <text class="search-btn-text">搜索</text>
      </view>
    </view>

    <!-- Stats row -->
    <view class="stats-row">
      <view class="stat-item">
        <text class="stat-value">{{ total }}</text>
        <text class="stat-label">总会员</text>
      </view>
    </view>

    <!-- Loading skeleton -->
    <view v-if="loading && !members.length" class="skeleton-list">
      <view v-for="i in 5" :key="i" class="skeleton-item" />
    </view>

    <!-- Empty -->
    <view v-else-if="!loading && !members.length" class="empty-state">
      <view class="empty-icon-wrap">
        <view class="empty-icon-person" />
      </view>
      <text class="empty-text">{{ searchQuery ? '未找到匹配的会员' : '暂无会员数据' }}</text>
    </view>

    <!-- Member list -->
    <view v-else class="member-list">
      <view
        v-for="m in members"
        :key="m.userId"
        class="member-row"
        @tap="openDetail(m)"
      >
        <view class="member-avatar">
          <image v-if="m.avatarUrl" class="avatar-img" :src="m.avatarUrl" mode="aspectFill" />
          <view v-else class="avatar-placeholder">
            <text class="avatar-text">{{ (m.nickname || '?').slice(0, 1) }}</text>
          </view>
        </view>
        <view class="member-info">
          <text class="member-name">{{ m.nickname || '未知用户' }}</text>
          <text class="member-openid">{{ m.openid }}</text>
        </view>
        <view class="member-stats">
          <text class="member-stat-value">{{ m.totalBookings }}</text>
          <text class="member-stat-label">次预约</text>
        </view>
        <text class="member-arrow">›</text>
      </view>
    </view>

    <!-- Bottom status -->
    <view v-if="members.length" class="list-footer">
      <text class="list-footer-text">
        {{ loading ? '加载中...' : hasMore ? '上拉加载更多' : '— 已加载全部 —' }}
      </text>
    </view>

    <!-- Detail modal -->
    <view v-if="showDetail && detailMember" class="modal-mask" @tap.self="showDetail = false">
      <view class="modal">
        <view class="detail-header">
          <view class="detail-avatar">
            <image v-if="detailMember.avatarUrl" class="avatar-img" :src="detailMember.avatarUrl" mode="aspectFill" />
            <view v-else class="avatar-placeholder avatar-placeholder--lg">
              <text class="avatar-text avatar-text--lg">{{ (detailMember.nickname || '?').slice(0, 1) }}</text>
            </view>
          </view>
          <text class="detail-name">{{ detailMember.nickname || '未知用户' }}</text>
          <text class="detail-openid" @tap="copyOpenid(detailMember.openid)">
            {{ detailMember.openid }}
          </text>
          <text class="detail-phone">{{ detailMember.phone || '未绑定手机' }}</text>
        </view>

        <view class="detail-stats">
          <view class="detail-stat">
            <text class="detail-stat-value">{{ detailMember.totalBookings }}</text>
            <text class="detail-stat-label">总预约</text>
          </view>
          <view class="detail-stat">
            <text class="detail-stat-value">{{ detailMember.completedBookings }}</text>
            <text class="detail-stat-label">已完成</text>
          </view>
          <view class="detail-stat">
            <text class="detail-stat-value">{{ detailMember.cancelledBookings }}</text>
            <text class="detail-stat-label">已取消</text>
          </view>
        </view>

        <view class="modal-close" @tap="showDetail = false">
          <text class="modal-close-text">关闭</text>
        </view>
      </view>
    </view>
  </view>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { onReachBottom } from '@dcloudio/uni-app'
import CustomNavBar from '../../components/CustomNavBar.vue'
import { getSystemLayout } from '../../utils/system'
import { useAdminStore } from '../../stores/admin'
import type { MemberSummary } from '../../stores/admin'

const adminStore = useAdminStore()

const navBarHeight = ref('64px')
onMounted(() => {
  navBarHeight.value = `${getSystemLayout().navBarHeight}px`
})

const members = ref<MemberSummary[]>([])
const loading = ref(false)
const searchQuery = ref('')
const page = ref(1)
const total = ref(0)
const hasMore = ref(false)
const showDetail = ref(false)
const detailMember = ref<MemberSummary | null>(null)

const LIMIT = 20

async function loadMembers(reset = false) {
  if (loading.value) return
  if (reset) {
    page.value = 1
    members.value = []
  }
  loading.value = true
  try {
    const search = searchQuery.value.trim()
    const result = await adminStore.fetchMembers({
      page: page.value,
      limit: LIMIT,
      ...(search ? { search } : {}),
    })
    if (reset) {
      members.value = [...result.items]
    } else {
      members.value = [...members.value, ...result.items]
    }
    total.value = result.total
    hasMore.value = members.value.length < result.total
  } catch {
    uni.showToast({ title: '加载失败', icon: 'none' })
  } finally {
    loading.value = false
  }
}

function onSearch() {
  loadMembers(true)
}

function onClear() {
  searchQuery.value = ''
  loadMembers(true)
}

// Scroll to bottom → load next page
onReachBottom(() => {
  if (!hasMore.value || loading.value) return
  page.value++
  loadMembers(false)
})

function openDetail(m: MemberSummary) {
  detailMember.value = m
  showDetail.value = true
}

function copyOpenid(openid: string) {
  uni.setClipboardData({
    data: openid,
    success: () => uni.showToast({ title: '已复制 OpenID', icon: 'success' }),
  })
}

onMounted(() => loadMembers(true))
</script>

<style lang="scss" scoped>
.page {
  min-height: 100vh;
  background: $bg-page;
  padding-bottom: 40rpx;
}

/* ── Filter bar ──────────────────────────── */
.filter-bar {
  display: flex;
  align-items: center;
  gap: 16rpx;
  padding: 24rpx;
  background: $bg-card;
  border-bottom: 1rpx solid $border-color;
  position: relative;
}

.search-input {
  flex: 1;
  height: 72rpx;
  background: $bg-page;
  border-radius: 36rpx;
  padding: 0 28rpx;
  font-size: 26rpx;
  color: $text-primary;
}

.search-clear {
  position: absolute;
  right: 168rpx;
  width: 44rpx;
  height: 44rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.search-clear-icon {
  font-size: 32rpx;
  color: $text-hint;
  line-height: 1;
}

.search-btn {
  background: $brand-color;
  border-radius: 36rpx;
  padding: 16rpx 32rpx;
}

.search-btn-text { font-size: 26rpx; font-weight: 600; color: $accent-color; }

/* ── Stats row ───────────────────────────── */
.stats-row {
  display: flex;
  padding: 24rpx 28rpx 16rpx;
}

.stat-item { display: flex; align-items: baseline; gap: 8rpx; }
.stat-value { font-size: 36rpx; font-weight: 800; color: $accent-color; }
.stat-label { font-size: 24rpx; color: $text-hint; }

/* ── Skeleton ────────────────────────────── */
.skeleton-list { padding: 0 24rpx; }

.skeleton-item {
  height: 120rpx;
  border-radius: $radius-md;
  margin-bottom: 16rpx;
  background: linear-gradient(90deg, #f0f0f0 25%, #e8e8e8 50%, #f0f0f0 75%);
  background-size: 400% 100%;
  animation: shimmer 1.4s infinite;
}

/* ── Empty ───────────────────────────────── */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 120rpx 0;
  gap: 24rpx;
}

.empty-icon-wrap {
  width: 96rpx;
  height: 96rpx;
  border-radius: 50%;
  background: rgba($brand-color, 0.06);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.empty-icon-person {
  &::before {
    content: '';
    width: 20rpx;
    height: 20rpx;
    border: 3rpx solid $text-hint;
    border-radius: 50%;
    position: absolute;
    top: 22rpx;
    left: 50%;
    transform: translateX(-50%);
    box-sizing: border-box;
  }
  &::after {
    content: '';
    width: 36rpx;
    height: 16rpx;
    border: 3rpx solid $text-hint;
    border-bottom: none;
    border-radius: 20rpx 20rpx 0 0;
    position: absolute;
    bottom: 20rpx;
    left: 50%;
    transform: translateX(-50%);
    box-sizing: border-box;
  }
}

.empty-text { font-size: 28rpx; color: $text-hint; }

/* ── Member list ─────────────────────────── */
.member-list { padding: 0 24rpx; padding-top: 8rpx; }

.member-row {
  background: $bg-card;
  border-radius: $radius-md;
  padding: 24rpx;
  display: flex;
  align-items: center;
  gap: 20rpx;
  margin-bottom: 16rpx;
  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
}

.member-avatar {
  width: 80rpx;
  height: 80rpx;
  border-radius: 50%;
  overflow: hidden;
  flex-shrink: 0;
}

.avatar-img { width: 100%; height: 100%; }

.avatar-placeholder {
  width: 80rpx;
  height: 80rpx;
  border-radius: 50%;
  background: $brand-color;
  display: flex;
  align-items: center;
  justify-content: center;
}

.avatar-placeholder--lg {
  width: 120rpx;
  height: 120rpx;
}

.avatar-text {
  font-size: 32rpx;
  font-weight: 700;
  color: $accent-color;
}

.avatar-text--lg { font-size: 48rpx; }

.member-info {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 6rpx;
  min-width: 0;
}

.member-name {
  font-size: 28rpx;
  font-weight: 600;
  color: $brand-color;
}

.member-openid {
  font-size: 20rpx;
  color: $text-hint;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: Menlo, Monaco, Consolas, monospace;
}

.member-stats { display: flex; flex-direction: column; align-items: flex-end; gap: 4rpx; flex-shrink: 0; }
.member-stat-value { font-size: 32rpx; font-weight: 700; color: $accent-color; }
.member-stat-label { font-size: 20rpx; color: $text-hint; }

.member-arrow { font-size: 36rpx; color: $text-hint; transform: scaleX(0.6); }

/* ── List footer ─────────────────────────── */
.list-footer {
  text-align: center;
  padding: 28rpx 0 16rpx;
}

.list-footer-text { font-size: 24rpx; color: $text-hint; }

/* ── Detail modal ────────────────────────── */
.modal-mask {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: flex-end;
  z-index: 100;
}

.modal {
  width: 100%;
  background: $bg-card;
  border-radius: $radius-lg $radius-lg 0 0;
  padding: 48rpx 32rpx 60rpx;
}

.detail-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12rpx;
  margin-bottom: 40rpx;
}

.detail-avatar {
  width: 120rpx;
  height: 120rpx;
  border-radius: 50%;
  overflow: hidden;
  margin-bottom: 8rpx;
}

.detail-name { font-size: 32rpx; font-weight: 700; color: $brand-color; }

.detail-openid {
  font-size: 22rpx;
  color: $accent-color;
  font-family: Menlo, Monaco, Consolas, monospace;
  padding: 6rpx 16rpx;
  background: rgba($accent-color, 0.08);
  border-radius: 8rpx;
}

.detail-phone { font-size: 26rpx; color: $text-secondary; }

.detail-stats {
  display: flex;
  justify-content: space-around;
  background: $bg-page;
  border-radius: $radius-md;
  padding: 28rpx;
  margin-bottom: 32rpx;
}

.detail-stat { display: flex; flex-direction: column; align-items: center; gap: 8rpx; }
.detail-stat-value { font-size: 40rpx; font-weight: 800; color: $accent-color; }
.detail-stat-label { font-size: 22rpx; color: $text-hint; }

.modal-close {
  width: 100%;
  height: 88rpx;
  background: $bg-page;
  border-radius: 44rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-close-text { font-size: 28rpx; color: $text-secondary; }
</style>
