<template>
  <!-- 我的报名：布局参考农技课堂-实战培训；见 doc/我的报名.md -->
  <view :class="pageRootClass" class="tab-page me-page" :style="pageStyle">
    <view class="me-top">
      <view class="me-chips" role="tablist">
        <view
          v-for="chip in statusChips"
          :key="chip.key"
          class="me-chip"
          :class="{ 'me-chip--on': selectedStatus === chip.key }"
          role="button"
          @click="onPickStatus(chip.key)"
        >
          <text class="text-body me-chip__txt">{{ $t(chip.labelKey) }}</text>
        </view>
      </view>
    </view>

    <view class="me-body">
      <view v-if="listLoading && !pairedRows.length" class="me-empty">
        <text class="text-body me-empty__txt">{{ $t('myEnrollmentPage.loading') }}</text>
      </view>
      <view v-else-if="!pairedRows.length" class="me-empty">
        <text class="text-body me-empty__txt">{{ $t('myEnrollmentPage.empty') }}</text>
      </view>
      <view v-else class="me-list-wrap">
        <up-virtual-list
          ref="meVList"
          class="me-vlist"
          :list-data="pairedRows"
          :item-height="slotHeightPx"
          :height="listHeightPx"
          :buffer="4"
          key-field="id"
          :scroll-top="vScrollTop"
          @update:scrollTop="vScrollTop = $event"
          @scroll="onVirtualListScroll"
        >
          <template #default="{ item }">
            <view class="me-pair-cell">
              <view class="me-pair" :style="{ height: rowBodyPx + 'px' }">
                <view class="me-pair__col">
                  <view v-if="item.left" class="me-card" role="button" @click="openTrainingDetail(item.left)">
                    <up-lazy-load
                      class="me-card__img"
                      :image="cardCover(item.left)"
                      height="120"
                      :border-radius="12"
                      img-mode="aspectFill"
                      :threshold="200"
                      :index="item.left.id"
                    />
                    <text class="me-card__title">{{ item.left.title }}</text>
                    <view class="me-card__foot">
                      <text class="text-body me-card__meta">{{ statusLabel(item.left) }}</text>
                      <up-icon name="account-fill" color="#22C55E" :size="16" />
                      <text class="text-body me-card__meta me-card__meta--num">
                        {{ item.left.enrolled }}/{{ item.left.total }}
                      </text>
                    </view>
                  </view>
                </view>
                <view class="me-pair__col">
                  <view v-if="item.right" class="me-card" role="button" @click="openTrainingDetail(item.right)">
                    <up-lazy-load
                      class="me-card__img"
                      :image="cardCover(item.right)"
                      height="120"
                      :border-radius="12"
                      img-mode="aspectFill"
                      :threshold="200"
                      :index="item.right.id"
                    />
                    <text class="me-card__title">{{ item.right.title }}</text>
                    <view class="me-card__foot">
                      <text class="text-body me-card__meta">{{ statusLabel(item.right) }}</text>
                      <up-icon name="account-fill" color="#22C55E" :size="16" />
                      <text class="text-body me-card__meta me-card__meta--num">
                        {{ item.right.enrolled }}/{{ item.right.total }}
                      </text>
                    </view>
                  </view>
                </view>
              </view>
            </view>
          </template>
        </up-virtual-list>
      </view>
    </view>
  </view>
</template>

<script>
import UIcon from 'uview-plus/components/u-icon/u-icon.vue'
import ULazyLoad from 'uview-plus/components/u-lazy-load/u-lazy-load.vue'
import UVirtualList from 'uview-plus/components/u-virtual-list/u-virtual-list.vue'
import tabPage from '@/mixins/tabPage'
import { resolveResourceUrl } from '@/utils/resourceUrl'
import { listMyEnroll } from '@/api/practicalTraining'

const TRAINING_STATUS_CODES = ['006001', '006002', '006003', '006004']
const CHIP_ALL = 'ALL'
const LIST_PAGE_SIZE = 20
const ROW_BODY_RPX = 320
const ROW_GAP_RPX = 20
const ME_BODY_PAD_RPX = 40
const COVER = '/static/ai/hero.png'
const TRAINING_DETAIL_PATH = '/package-a/training-detail/index'

function formatDateTime(val) {
  if (val == null || val === '') return ''
  const s = String(val)
  return s.length >= 19 ? s.slice(0, 19) : s.slice(0, 10)
}

export default {
  components: {
    'up-icon': UIcon,
    'up-lazy-load': ULazyLoad,
    'up-virtual-list': UVirtualList
  },
  mixins: [tabPage],
  data() {
    return {
      navTitleKey: 'myEnrollmentPage.navTitle',
      selectedStatus: CHIP_ALL,
      allCards: [],
      listLoading: false,
      listLoadingMore: false,
      listTotal: 0,
      pageNum: 1,
      pageSize: LIST_PAGE_SIZE,
      loadMoreTimer: null,
      rowBodyPx: 140,
      marginPx: 10,
      slotHeightPx: 150,
      listHeightPx: 400,
      pageHeightPx: 0,
      vScrollTop: 0,
      coverSrc: COVER
    }
  },
  computed: {
    statusChips() {
      return [
        { key: CHIP_ALL, labelKey: 'myEnrollmentPage.chipAll' },
        ...TRAINING_STATUS_CODES.map((code, idx) => ({
          key: code,
          labelKey: `agriClassroomPage.trainingSubs.s${idx}`
        }))
      ]
    },
    filteredCards() {
      if (this.selectedStatus === CHIP_ALL) {
        return this.allCards
      }
      return this.allCards.filter((c) => c.typeCode === this.selectedStatus)
    },
    listNoMore() {
      return this.listTotal > 0 && this.allCards.length >= this.listTotal
    },
    pageStyle() {
      if (this.pageHeightPx > 0) {
        return { height: `${this.pageHeightPx}px` }
      }
      return {}
    },
    pairedRows() {
      const cards = this.filteredCards
      const rows = []
      for (let i = 0; i < cards.length; i += 2) {
        const L = cards[i]
        const R = cards[i + 1] || null
        rows.push({
          id: `${L.id}_${R ? R.id : 'x'}`,
          left: L,
          right: R
        })
      }
      return rows
    }
  },
  watch: {
    selectedStatus() {
      this.vScrollTop = 0
      this.$nextTick(() => this.tryAutoLoadMoreAfterFilter())
    }
  },
  onShow() {
    this.loadEnrollList(true)
  },
  onReady() {
    try {
      this.marginPx = Math.ceil(uni.upx2px(ROW_GAP_RPX))
      this.rowBodyPx = Math.max(120, Math.ceil(uni.upx2px(ROW_BODY_RPX)))
      this.slotHeightPx = this.rowBodyPx + this.marginPx
    } catch (e) {
      this.marginPx = 10
      this.rowBodyPx = 140
      this.slotHeightPx = 150
    }
    this.applyListHeightFallback()
    this.$nextTick(() => this.calcLayoutHeights())
  },
  onUnload() {
    if (this.loadMoreTimer) clearTimeout(this.loadMoreTimer)
  },
  methods: {
    statusLabel(card) {
      if (card && card.statusName) {
        return card.statusName
      }
      const typeCode = card && card.typeCode
      const idx = TRAINING_STATUS_CODES.indexOf(typeCode)
      const i = idx >= 0 ? idx : 0
      return this.$t(`agriClassroomPage.trainingSubs.s${i}`)
    },
    onPickStatus(key) {
      if (this.selectedStatus === key) return
      this.selectedStatus = key
    },
    mapEnrollRow(row) {
      return {
        id: String(row.id != null ? row.id : ''),
        typeCode: row.trainingStatus || '',
        statusName: row.trainingStatusName || '',
        title: row.trainingTopic || '',
        introduction: row.trainingIntro || '',
        coverFileUrl: row.coverFileUrl || '',
        enrolled: row.actualEnrolledCount != null ? Number(row.actualEnrolledCount) : 0,
        total: row.plannedHeadCount != null ? Number(row.plannedHeadCount) : 0,
        trainingTime: formatDateTime(row.trainingTime),
        registrationEndTime: formatDateTime(row.registrationEndTime),
        enrollTime: formatDateTime(row.enrollTime)
      }
    },
    loadEnrollList(reset = false) {
      if (!reset) {
        if (this.listLoading || this.listLoadingMore || this.listNoMore) {
          return Promise.resolve()
        }
        this.pageNum += 1
        this.listLoadingMore = true
      } else {
        this.pageNum = 1
        this.listLoading = true
      }
      return listMyEnroll({ pageNum: this.pageNum, pageSize: this.pageSize })
        .then((res) => {
          const rows = res.rows || []
          this.listTotal = res.total != null ? Number(res.total) : 0
          const mapped = rows.map((row) => this.mapEnrollRow(row))
          this.allCards = reset ? mapped : this.allCards.concat(mapped)
          this.$nextTick(() => {
            this.calcLayoutHeights()
            this.tryAutoLoadMoreAfterFilter()
          })
        })
        .catch(() => {
          if (reset) {
            this.allCards = []
            this.listTotal = 0
          } else {
            this.pageNum -= 1
          }
        })
        .finally(() => {
          if (reset) {
            this.listLoading = false
          } else {
            this.listLoadingMore = false
          }
        })
    },
    tryAutoLoadMoreAfterFilter() {
      if (this.filteredCards.length > 0 || this.listNoMore || this.listLoading || this.listLoadingMore) {
        return
      }
      this.loadEnrollList(false)
    },
    tryAutoLoadMore() {
      if (this.listLoading || this.listLoadingMore || this.listNoMore) return
      const totalH = this.pairedRows.length * this.slotHeightPx
      const viewH = this.listHeightPx
      if (totalH > 0 && totalH <= viewH) {
        this.loadEnrollList(false)
      }
    },
    onVirtualListScroll(scrollTop) {
      if (this.loadMoreTimer) clearTimeout(this.loadMoreTimer)
      this.loadMoreTimer = setTimeout(() => {
        this.loadMoreTimer = null
        this.tryLoadMoreOnScroll(typeof scrollTop === 'number' ? scrollTop : 0)
      }, 180)
    },
    tryLoadMoreOnScroll(scrollTop) {
      if (this.listLoading || this.listLoadingMore || this.listNoMore) return
      const totalH = this.pairedRows.length * this.slotHeightPx
      const viewH = this.listHeightPx
      const threshold = Math.max(this.slotHeightPx * 2, 120)
      if (totalH <= viewH) {
        this.loadEnrollList(false)
        return
      }
      if (scrollTop + viewH >= totalH - threshold) {
        this.loadEnrollList(false)
      }
    },
    getNavigationBarHeight() {
      const sys = uni.getSystemInfoSync()
      const statusBar = sys.statusBarHeight || 0
      // #ifdef MP-WEIXIN
      try {
        const menu = uni.getMenuButtonBoundingClientRect()
        if (menu && menu.height) {
          return statusBar + (menu.top - statusBar) * 2 + menu.height
        }
      } catch (e) {
        /* noop */
      }
      // #endif
      return statusBar + 44
    },
    getViewportContentHeight() {
      const sys = uni.getSystemInfoSync()
      const navH = this.getNavigationBarHeight()
      // #ifdef H5
      if (typeof window !== 'undefined' && window.innerHeight) {
        return Math.max(320, window.innerHeight - navH)
      }
      // #endif
      const screenH = sys.screenHeight || sys.windowHeight || 600
      return Math.max(320, screenH - navH)
    },
    applyListHeightFallback() {
      const contentH = this.getViewportContentHeight()
      const topFallback = Math.ceil(uni.upx2px(120))
      const bodyPad = Math.ceil(uni.upx2px(ME_BODY_PAD_RPX))
      this.pageHeightPx = contentH
      this.listHeightPx = Math.max(240, contentH - topFallback - bodyPad)
    },
    calcLayoutHeights() {
      const contentH = this.getViewportContentHeight()
      const bodyPad = Math.ceil(uni.upx2px(ME_BODY_PAD_RPX))
      this.pageHeightPx = contentH

      uni.createSelectorQuery()
        .in(this)
        .select('.me-top')
        .boundingClientRect((topRect) => {
          const topH =
            topRect && topRect.height > 0 ? Math.ceil(topRect.height) : Math.ceil(uni.upx2px(120))
          this.listHeightPx = Math.max(240, contentH - topH - bodyPad)
          this.$nextTick(() => {
            this.$refs.meVList?.measureContainerHeight?.()
            this.tryAutoLoadMore()
          })
        })
        .exec()
    },
    cardCover(card) {
      if (card.coverFileUrl) {
        return resolveResourceUrl(card.coverFileUrl)
      }
      return this.coverSrc
    },
    openTrainingDetail(card) {
      if (!card) return
      const q = [
        `id=${encodeURIComponent(card.id)}`,
        `title=${encodeURIComponent(card.title || '')}`,
        `type=${encodeURIComponent(card.typeCode || '')}`,
        `enrolled=${encodeURIComponent(card.enrolled)}`,
        `total=${encodeURIComponent(card.total)}`,
        `introduction=${encodeURIComponent(card.introduction || '')}`,
        `trainingTime=${encodeURIComponent(card.trainingTime || '')}`,
        `cover=${encodeURIComponent(card.coverFileUrl || '')}`,
        `regEnd=${encodeURIComponent(card.registrationEndTime || '')}`
      ].join('&')
      uni.navigateTo({ url: `${TRAINING_DETAIL_PATH}?${q}` })
    }
  }
}
</script>

<style lang="scss" scoped>
@import '@/styles/morandi.scss';
@import '@/styles/tab-page.scss';

.me-page {
  display: flex;
  flex-direction: column;
  min-width: 0;
  width: 100%;
  height: 100%;
  min-height: 100%;
  overflow: hidden;
  box-sizing: border-box;
  background: $morandi-bg-page;
}

.me-top {
  flex-shrink: 0;
  min-width: 0;
  padding: 20rpx 24rpx 16rpx;
  box-sizing: border-box;
  background: $morandi-bg-page;
  border-bottom: 1rpx solid $morandi-border-soft;
}

.me-chips {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  min-width: 0;
  gap: 12rpx;
}

.me-chip {
  padding: 2rpx 30rpx;
  box-sizing: border-box;
  border-radius: 999rpx;
  border: 1rpx solid $morandi-border-strong;
  background: $morandi-bg-card-inner;
}

.me-chip--on {
  border-color: #22c55e;
  background: rgba(34, 197, 94, 0.08);
}

.me-chip__txt {
  font-size: 24rpx;
  color: $morandi-text-secondary;
  text-align: center;
  word-break: break-word;
  overflow-wrap: anywhere;
}

.me-chip--on .me-chip__txt {
  color: #15803d;
  font-weight: 600;
}

.me-body {
  flex: 1;
  min-height: 0;
  height: 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: 16rpx 24rpx 24rpx;
  box-sizing: border-box;
  overflow: hidden;
}

.me-list-wrap {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.me-vlist {
  flex: 1;
  min-height: 0;
  width: 100%;
  height: 100%;
}

.me-empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  padding: 48rpx 24rpx;
  box-sizing: border-box;
}

.me-empty__txt {
  color: $morandi-text-muted;
  text-align: center;
}

.me-pair-cell {
  height: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}

.me-pair {
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: 20rpx;
  min-width: 0;
  box-sizing: border-box;
}

.me-pair__col {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.me-card {
  flex: 1;
  min-height: 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 12rpx;
  padding: 12rpx;
  box-sizing: border-box;
  border-radius: 16rpx;
  background: $morandi-bg-card;
  border: 1rpx solid $morandi-border;
}

.me-card__img {
  width: 100%;
  display: block;
}

.me-card__title {
  font-size: 30rpx;
  font-weight: 600;
  line-height: 1.4;
  color: #111827;
  word-break: break-word;
  overflow-wrap: anywhere;
}

.me-card__foot {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: 8rpx 12rpx;
  min-width: 0;
}

.me-card__meta {
  font-size: 22rpx;
  line-height: 1.45;
  color: $morandi-accent-soft;
}

.me-card__meta--num {
  color: #15803d;
  font-weight: 600;
}

.me-page.lang-bo {
  .me-card__title {
    font-size: 26rpx;
    line-height: 1.65;
    letter-spacing: 2rpx;
    font-family: 'Noto Sans Tibetan', 'PingFang SC', 'Microsoft YaHei', sans-serif;
  }

  .me-chip__txt {
    letter-spacing: 2rpx;
    font-family: 'Noto Sans Tibetan', 'PingFang SC', 'Microsoft YaHei', sans-serif;
  }
}
</style>
