<script setup lang="ts">
import type { CheckInBadgeDefinition, CheckInProfile } from '@/domain/check-in'
import type { PoiResolved } from '@/domain/poi'
import { getCheckInTasks } from '@/data/check-in'
import { getPoiRepository } from '@/data/poi'
import { CHECK_IN_BADGES } from '@/domain/check-in'
import { clearCheckInProfile, getCheckInProfile } from '@/services/check-in'

interface BadgeView extends CheckInBadgeDefinition {
  obtainedAt: string | null
}

const profile = ref<CheckInProfile>({ points: 0, badges: [], checkIns: [] })
const storageError = ref('')
const poiDirectoryError = ref('')
const availablePois = shallowRef<PoiResolved[]>([])
const badgeViews = computed<BadgeView[]>(() => CHECK_IN_BADGES.map(definition => ({
  ...definition,
  obtainedAt: profile.value.badges.find(badge => badge.code === definition.code)?.obtainedAt ?? null,
})))
const availableCheckInCount = computed(() => {
  const availablePoiIds = new Set(availablePois.value.map(poi => poi.id))
  return profile.value.checkIns.filter(record => availablePoiIds.has(record.poiId)).length
})

function formatTime(value: string): string {
  const date = new Date(value)
  if (Number.isNaN(date.getTime()))
    return '时间待确认'
  const pad = (number: number) => String(number).padStart(2, '0')
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`
}

function openPoi(poiId: string) {
  let poi: PoiResolved | null = null
  try {
    poi = getPoiRepository().getPoiById(poiId)
  }
  catch (error) {
    poiDirectoryError.value = error instanceof Error ? error.message : '开放打卡点位暂时无法读取'
    uni.showModal({
      title: '暂时无法打开点位',
      content: '开放点位目录暂时无法读取，请稍后重新加载。',
      showCancel: false,
    })
    return
  }
  if (!poi) {
    uni.showModal({
      title: '点位已下线',
      content: '该点位暂不在当前开放列表中，本机历史打卡记录仍会保留。',
      showCancel: false,
    })
    return
  }
  uni.navigateTo({ url: `/pages-poi/poi-detail?poiId=${encodeURIComponent(poiId)}` })
}

function loadProfile() {
  try {
    profile.value = getCheckInProfile()
    storageError.value = ''
  }
  catch (error) {
    profile.value = { points: 0, badges: [], checkIns: [] }
    storageError.value = error instanceof Error ? error.message : '本机打卡记录暂时无法读取'
  }
}

function loadAvailablePois() {
  try {
    const repository = getPoiRepository()
    availablePois.value = getCheckInTasks().flatMap((task) => {
      const poi = repository.getPoiById(task.poiId)
      return poi ? [poi] : []
    })
    poiDirectoryError.value = ''
  }
  catch (error) {
    availablePois.value = []
    poiDirectoryError.value = error instanceof Error ? error.message : '开放打卡点位暂时无法读取'
  }
}

function loadRecords() {
  loadProfile()
  loadAvailablePois()
}

function poiCheckedIn(poiId: string): boolean {
  return profile.value.checkIns.some(record => record.poiId === poiId)
}

function clearLocalRecords() {
  uni.showModal({
    title: '清除本机打卡数据',
    content: '积分、徽章和打卡记录将从本机删除且无法恢复，不影响路线、偏好或其他数据。',
    confirmText: '确认清除',
    confirmColor: '#b53a32',
    success: (result) => {
      if (!result.confirm)
        return
      try {
        clearCheckInProfile()
        profile.value = getCheckInProfile()
        storageError.value = ''
        uni.showToast({ title: '打卡数据已清除', icon: 'success' })
      }
      catch (error) {
        uni.showModal({
          title: '清除失败',
          content: error instanceof Error ? error.message : '请稍后重试',
          showCancel: false,
        })
      }
    },
  })
}

onShow(loadRecords)
</script>

<template>
  <view class="records-page">
    <view class="records-hero">
      <text class="records-hero__eyebrow">LOCAL JOURNEY</text>
      <text class="records-hero__title">我的光明足迹</text>
      <view class="records-stats">
        <view><text>{{ storageError ? '--' : profile.points }}</text><text>本机积分</text></view>
        <view><text>{{ storageError ? '--' : profile.checkIns.length }}</text><text>打卡点位</text></view>
        <view><text>{{ storageError ? '--' : profile.badges.length }}</text><text>已获徽章</text></view>
      </view>
    </view>

    <view class="records-content">
      <view v-if="storageError" class="records-error">
        <text class="records-error__title">打卡数据需要处理</text>
        <text>{{ storageError }}</text>
        <button @click="clearLocalRecords">清除异常打卡数据</button>
      </view>

      <view v-if="poiDirectoryError" class="records-error">
        <text class="records-error__title">开放点位暂时无法读取</text>
        <text>{{ poiDirectoryError }}</text>
        <button @click="loadAvailablePois">重新加载开放点位</button>
      </view>

      <view v-if="!storageError" class="records-section">
        <view class="records-section__heading">
          <view><text class="records-section__kicker">BADGES</text><text class="records-section__title">光明徽章</text></view>
          <text>{{ profile.badges.length }} / {{ badgeViews.length }}</text>
        </view>
        <view class="badge-grid">
          <view
            v-for="badge in badgeViews"
            :key="badge.code"
            class="badge-card"
            :class="{ 'badge-card--locked': !badge.obtainedAt }"
          >
            <view class="badge-card__icon">{{ badge.icon }}</view>
            <text class="badge-card__name">{{ badge.name }}</text>
            <text class="badge-card__description">{{ badge.description }}</text>
            <text class="badge-card__time">{{ badge.obtainedAt ? formatTime(badge.obtainedAt) : '尚未解锁' }}</text>
          </view>
        </view>
      </view>

      <view v-if="!poiDirectoryError && !storageError" class="records-section">
        <view class="records-section__heading">
          <view><text class="records-section__kicker">PLACES</text><text class="records-section__title">开放打卡点位</text></view>
          <text>{{ availableCheckInCount }} / {{ availablePois.length }}</text>
        </view>
        <view class="check-in-poi-list">
          <button v-for="poi in availablePois" :key="poi.id" class="check-in-poi-card" @click="openPoi(poi.id)">
            <view class="check-in-poi-card__mark" :class="{ 'check-in-poi-card__mark--done': poiCheckedIn(poi.id) }">
              {{ poiCheckedIn(poi.id) ? '✓' : '◎' }}
            </view>
            <view class="check-in-poi-card__copy">
              <text>{{ poi.name }}</text>
              <text>{{ poi.category.name }} · {{ poiCheckedIn(poi.id) ? '已打卡' : '可前往打卡' }}</text>
            </view>
            <text class="check-in-poi-card__arrow">›</text>
          </button>
        </view>
      </view>

      <view v-if="!storageError" class="records-section">
        <view class="records-section__heading">
          <view><text class="records-section__kicker">HISTORY</text><text class="records-section__title">打卡记录</text></view>
          <text>{{ profile.checkIns.length }} 条</text>
        </view>
        <view v-if="profile.checkIns.length" class="record-list">
          <button v-for="record in profile.checkIns" :key="record.id" class="record-card" @click="openPoi(record.poiId)">
            <view class="record-card__index">✓</view>
            <view class="record-card__copy">
              <text class="record-card__name">{{ record.poiName }}</text>
              <text>{{ formatTime(record.checkedInAt) }} · +{{ record.pointsAwarded }} 积分</text>
            </view>
            <text class="record-card__arrow">›</text>
          </button>
        </view>
        <view v-else class="record-empty">
          <view class="record-empty__mark">◎</view>
          <text class="record-empty__title">还没有光明足迹</text>
          <text>从地图选择一个点位，到达现场后即可开始第一次打卡。</text>
        </view>
      </view>

      <view class="records-privacy">
        <text class="records-privacy__title">匿名本机数据</text>
        <text>打卡记录、积分和徽章仅保存在当前设备；不登录、不上传，也不保存打卡时的精确经纬度。</text>
        <button v-if="profile.checkIns.length" @click="clearLocalRecords">清除本机打卡数据</button>
      </view>
    </view>
  </view>
</template>

<route lang="yaml">
layout: map
style:
  navigationBarTitleText: 我的打卡
  navigationBarBackgroundColor: '#ffffff'
  navigationBarTextStyle: black
  backgroundColor: '#f4f8f6'
</route>

<style scoped lang="scss">
.records-page {
  min-height: 100vh;
  color: #18201d;
  background: #f4f8f6;
}

.records-hero {
  padding: 46rpx 32rpx 36rpx;
  color: #fff;
  background: linear-gradient(145deg, #123d35, #08724d 62%, #07573d);
}

.records-hero__eyebrow,
.records-section__kicker {
  display: block;
  font-size: 18rpx;
  line-height: 28rpx;
  color: #9fe0bd;
  letter-spacing: 4rpx;
}

.records-hero__title {
  display: block;
  margin-top: 10rpx;
  font-size: 42rpx;
  font-weight: 800;
  line-height: 58rpx;
}

.records-stats {
  display: flex;
  gap: 16rpx;
  margin-top: 30rpx;
}

.records-stats view {
  display: flex;
  flex: 1;
  flex-direction: column;
  padding: 18rpx;
  background: rgb(255 255 255 / 11%);
  border: 1rpx solid rgb(255 255 255 / 20%);
  border-radius: 16rpx;
}

.records-stats view text:first-child {
  font-size: 32rpx;
  font-weight: 800;
}

.records-stats view text:last-child {
  margin-top: 4rpx;
  font-size: 18rpx;
  color: rgb(239 255 246 / 68%);
}

.records-content {
  padding: 8rpx 28rpx calc(48rpx + env(safe-area-inset-bottom));
}

.records-section {
  margin-top: 40rpx;
}

.records-error {
  display: flex;
  flex-direction: column;
  padding: 24rpx;
  margin-top: 28rpx;
  font-size: 21rpx;
  line-height: 34rpx;
  color: #6d3a36;
  background: #fff5f3;
  border: 1rpx solid #ead6d2;
  border-radius: 18rpx;
}

.records-error__title {
  margin-bottom: 6rpx;
  font-size: 26rpx;
  font-weight: 800;
  color: #873e38;
}

.records-error button {
  width: 100%;
  height: 64rpx;
  margin: 16rpx 0 0;
  font-size: 22rpx;
  line-height: 64rpx;
  color: #fff;
  background: #873e38;
  border: 0;
  border-radius: 12rpx;
}

.records-error button::after {
  border: 0;
}

.records-section__heading {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  margin-bottom: 20rpx;
  font-size: 21rpx;
  color: #718078;
}

.records-section__kicker {
  color: #16805c;
}

.records-section__title {
  display: block;
  margin-top: 7rpx;
  font-size: 34rpx;
  font-weight: 800;
  line-height: 48rpx;
  color: #18201d;
}

.badge-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14rpx;
}

.badge-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 0;
  padding: 22rpx 12rpx;
  text-align: center;
  background: #e8f4ee;
  border: 1rpx solid #cfe4d9;
  border-radius: 20rpx;
}

.badge-card--locked {
  color: #718078;
  background: #f0f3f1;
  border-color: #e0e6e3;
}

.badge-card__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 72rpx;
  height: 72rpx;
  font-family: Georgia, serif;
  font-size: 25rpx;
  font-weight: 800;
  color: #fff;
  background: #167a5b;
  border-radius: 50%;
}

.badge-card--locked .badge-card__icon {
  background: #a8b3ad;
}

.badge-card__name {
  margin-top: 14rpx;
  font-size: 22rpx;
  font-weight: 800;
  line-height: 32rpx;
}

.badge-card__description,
.badge-card__time {
  margin-top: 7rpx;
  font-size: 17rpx;
  line-height: 27rpx;
  color: #637069;
}

.badge-card__time {
  color: #16805c;
}

.record-list {
  display: flex;
  flex-direction: column;
  gap: 14rpx;
}

.check-in-poi-list {
  display: flex;
  flex-direction: column;
  gap: 14rpx;
}

.check-in-poi-card {
  display: flex;
  gap: 18rpx;
  align-items: center;
  width: 100%;
  min-height: 100rpx;
  padding: 18rpx 22rpx;
  margin: 0;
  text-align: left;
  background: #fff;
  border: 1rpx solid #dce6e1;
  border-radius: 18rpx;
}

.check-in-poi-card::after {
  border: 0;
}

.check-in-poi-card__mark {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 54rpx;
  height: 54rpx;
  font-size: 25rpx;
  color: #16805c;
  background: #e8f4ee;
  border-radius: 50%;
}

.check-in-poi-card__mark--done {
  color: #fff;
  background: #167a5b;
}

.check-in-poi-card__copy {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-width: 0;
}

.check-in-poi-card__copy text:first-child {
  overflow: hidden;
  font-size: 26rpx;
  font-weight: 750;
  line-height: 38rpx;
  color: #18201d;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.check-in-poi-card__copy text:last-child {
  margin-top: 3rpx;
  font-size: 20rpx;
  line-height: 30rpx;
  color: #718078;
}

.check-in-poi-card__arrow {
  font-size: 38rpx;
  color: #16805c;
}

.record-card {
  display: flex;
  gap: 18rpx;
  align-items: center;
  width: 100%;
  padding: 22rpx;
  margin: 0;
  text-align: left;
  background: #fff;
  border: 1rpx solid #dce6e1;
  border-radius: 18rpx;
}

.record-card::after,
.records-privacy button::after {
  border: 0;
}

.record-card__index {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 52rpx;
  height: 52rpx;
  font-size: 24rpx;
  color: #fff;
  background: #167a5b;
  border-radius: 50%;
}

.record-card__copy {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-width: 0;
  font-size: 20rpx;
  line-height: 31rpx;
  color: #718078;
}

.record-card__name {
  overflow: hidden;
  font-size: 27rpx;
  font-weight: 750;
  line-height: 38rpx;
  color: #18201d;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.record-card__arrow {
  font-size: 38rpx;
  color: #16805c;
}

.record-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 52rpx 30rpx;
  font-size: 22rpx;
  line-height: 36rpx;
  color: #718078;
  text-align: center;
  background: #fff;
  border: 1rpx solid #dce6e1;
  border-radius: 20rpx;
}

.record-empty__mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 88rpx;
  height: 88rpx;
  font-size: 40rpx;
  color: #16805c;
  background: #e8f4ee;
  border-radius: 50%;
}

.record-empty__title {
  margin: 18rpx 0 7rpx;
  font-size: 28rpx;
  font-weight: 800;
  color: #18201d;
}

.records-privacy {
  display: flex;
  flex-direction: column;
  padding: 24rpx;
  margin-top: 36rpx;
  font-size: 21rpx;
  line-height: 34rpx;
  color: #637069;
  background: #eef6f2;
  border: 1rpx solid #d6e7df;
  border-radius: 20rpx;
}

.records-privacy__title {
  margin-bottom: 7rpx;
  font-size: 25rpx;
  font-weight: 800;
  color: #0e6247;
}

.records-privacy button {
  width: 100%;
  height: 70rpx;
  margin: 18rpx 0 0;
  font-size: 23rpx;
  line-height: 70rpx;
  color: #8d3732;
  background: #fff;
  border: 1rpx solid #e8d6d4;
  border-radius: 14rpx;
}
</style>
