<template>
  <!-- 在线问诊：见 doc/app/在线问诊/在线问诊接口说明.md -->
  <view :class="pageRootClass" class="tab-page oc-page" :style="pageStyle">
    <view class="oc-top">
      <view class="oc-card">
        <up-search
          v-model="searchKeyword"
          shape="round"
          :placeholder="$t('onlineClinicPage.searchPlaceholder')"
          :show-action="false"
          :clearabled="true"
          bg-color="#f5f5f5"
          border-color="#e8e8e8"
        />
      </view>
    </view>

    <view class="oc-body">
      <view v-if="listLoading && !vetRows.length" class="oc-empty">
        <text class="text-body oc-empty__txt">{{ $t('onlineClinicPage.loading') }}</text>
      </view>
      <view v-else-if="!vetRows.length" class="oc-empty">
        <text class="text-body oc-empty__txt">{{ $t('onlineClinicPage.empty') }}</text>
      </view>
      <view v-else class="oc-list-wrap">
        <up-virtual-list
          ref="ocVList"
          class="oc-vlist"
          :list-data="vetRows"
          :item-height="slotHeightPx"
          :height="listHeightPx"
          :buffer="6"
          key-field="id"
          :scroll-top="vScrollTop"
          @update:scrollTop="vScrollTop = $event"
          @scroll="onVirtualListScroll"
        >
        <template #default="{ item }">
          <view class="oc-cell">
            <view class="oc-item" :style="{ height: rowBodyPx + 'px' }">
              <view class="oc-item__row1">
                <up-avatar
                  shape="circle"
                  :src="avatarUrl(item)"
                  :text="avatarUrl(item) ? '' : item.avatarText"
                  size="48"
                  font-size="18"
                  bg-color="#9ca3af"
                  color="#ffffff"
                />
                <view class="oc-item__right">
                  <view class="oc-item__head">
                    <view class="oc-item__titles">
                      <text class="oc-item__name text-body">{{ nameFor(item) }}</text>
                      <text v-if="subFor(item)" class="oc-item__sub text-body">{{ subFor(item) }}</text>
                    </view>
                    <up-button
                      class="oc-item__btn"
                      type="success"
                      shape="circle"
                      plain
                      hairline
                      size="small"
                      :text="$t('onlineClinicPage.btnProfile')"
                      :custom-style="profileBtnStyle"
                      @click="onProfile(item)"
                    />
                  </view>
                  <text class="oc-item__intro text-body">
                    <text class="oc-item__intro-k">{{ $t('onlineClinicPage.introLabel') }}</text>
                    {{ introFor(item) }}
                  </text>
                </view>
              </view>
              <up-button
                type="success"
                shape="circle"
                :text="$t('onlineClinicPage.btnConsult')"
                :custom-style="{ width: '100%' }"
                :loading="consultingId === item.id"
                @click="onConsult(item)"
              />
            </view>
          </view>
        </template>
        </up-virtual-list>
      </view>
    </view>
  </view>
</template>

<script>
import USearch from 'uview-plus/components/u-search/u-search.vue'
import UAvatar from 'uview-plus/components/u-avatar/u-avatar.vue'
import UButton from 'uview-plus/components/u-button/u-button.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 { ensureApiToken } from '@/utils/apiAuth'
import {
  listOnlineConsultVets,
  openOnlineConsultSession,
  saveVetProfileCache
} from '@/api/onlineConsult'

const ROW_BODY_RPX = 320
const ROW_GAP_RPX = 20
const OC_BODY_PAD_RPX = 32
const LIST_PAGE_SIZE = 20
const CONSULT_DETAIL_PATH = '/package-a/consult-detail/index'
const VET_PROFILE_PATH = '/package-a/vet-profile/index'

export default {
  components: {
    'up-search': USearch,
    'up-avatar': UAvatar,
    'up-button': UButton,
    'up-virtual-list': UVirtualList
  },
  mixins: [tabPage],
  data() {
    return {
      navTitleKey: 'homeGrid.onlineClinic',
      searchKeyword: '',
      vetRows: [],
      listLoading: false,
      listLoadingMore: false,
      listTotal: 0,
      pageNum: 1,
      pageSize: LIST_PAGE_SIZE,
      consultingId: '',
      _searchTimer: null,
      loadMoreTimer: null,
      rowBodyPx: 150,
      marginPx: 5,
      slotHeightPx: 155,
      listHeightPx: 400,
      pageHeightPx: 0,
      vScrollTop: 0,
      profileBtnStyle: { flexShrink: 0, marginLeft: '8px' }
    }
  },
  computed: {
    listNoMore() {
      return this.listTotal > 0 && this.vetRows.length >= this.listTotal
    },
    pageStyle() {
      if (this.pageHeightPx > 0) {
        return { height: `${this.pageHeightPx}px` }
      }
      return {}
    }
  },
  watch: {
    searchKeyword() {
      this.vScrollTop = 0
      clearTimeout(this._searchTimer)
      this._searchTimer = setTimeout(() => {
        this.loadVetList(true)
      }, 300)
    }
  },
  onShow() {
    if (ensureApiToken(false)) {
      this.loadVetList(true)
    }
    this.$nextTick(() => this.calcLayoutHeights())
  },
  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 = 5
      this.rowBodyPx = 150
      this.slotHeightPx = 155
    }
    this.applyListHeightFallback()
    this.$nextTick(() => this.calcLayoutHeights())
  },
  onUnload() {
    if (this._searchTimer) clearTimeout(this._searchTimer)
    if (this.loadMoreTimer) clearTimeout(this.loadMoreTimer)
  },
  methods: {
    mapVetRow(row) {
      const name = row.resourceName || ''
      return {
        id: String(row.id != null ? row.id : ''),
        resourceName: name,
        introduction: row.introduction || '',
        contactPhone: row.contactPhone || '',
        affiliatedUnit: row.affiliatedUnit || '',
        detailAddress: row.detailAddress || '',
        serviceStartTime: row.serviceStartTime || '',
        serviceEndTime: row.serviceEndTime || '',
        photoFileUrl: row.photoFileUrl || '',
        avatarText: name.slice(0, 1) || '兽',
        raw: row
      }
    },
    loadVetList(reset = false) {
      if (!ensureApiToken()) return Promise.resolve()
      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
      }
      const params = {
        pageNum: this.pageNum,
        pageSize: this.pageSize
      }
      const kw = (this.searchKeyword || '').trim()
      if (kw) {
        params.resourceName = kw
      }
      return listOnlineConsultVets(params)
        .then((res) => {
          const rows = (res.rows || []).map((row) => this.mapVetRow(row))
          this.listTotal = res.total != null ? Number(res.total) : 0
          this.vetRows = reset ? rows : this.vetRows.concat(rows)
          this.$nextTick(() => {
            this.calcLayoutHeights()
          })
        })
        .catch(() => {
          if (reset) {
            this.vetRows = []
            this.listTotal = 0
          } else {
            this.pageNum -= 1
          }
        })
        .finally(() => {
          if (reset) {
            this.listLoading = false
          } else {
            this.listLoadingMore = false
          }
        })
    },
    tryAutoLoadMore() {
      if (this.listLoading || this.listLoadingMore || this.listNoMore) return
      const totalH = this.vetRows.length * this.slotHeightPx
      const viewH = this.listHeightPx
      if (totalH > 0 && totalH <= viewH) {
        this.loadVetList(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.vetRows.length * this.slotHeightPx
      const viewH = this.listHeightPx
      const threshold = Math.max(this.slotHeightPx * 2, 120)
      if (totalH <= viewH) {
        this.loadVetList(false)
        return
      }
      if (scrollTop + viewH >= totalH - threshold) {
        this.loadVetList(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 topBlock = Math.ceil(uni.upx2px(100))
      const bodyPad = Math.ceil(uni.upx2px(OC_BODY_PAD_RPX))
      this.pageHeightPx = contentH
      this.listHeightPx = Math.max(240, contentH - topBlock - bodyPad)
    },
    calcLayoutHeights() {
      const contentH = this.getViewportContentHeight()
      this.pageHeightPx = contentH

      uni.createSelectorQuery()
        .in(this)
        .select('.oc-body')
        .boundingClientRect((bodyRect) => {
          if (bodyRect && bodyRect.height > 0) {
            this.listHeightPx = Math.max(240, Math.floor(bodyRect.height))
          } else {
            const topFallback = Math.ceil(uni.upx2px(100))
            const bodyPad = Math.ceil(uni.upx2px(OC_BODY_PAD_RPX))
            this.listHeightPx = Math.max(240, contentH - topFallback - bodyPad)
          }
          this.$nextTick(() => {
            this.$refs.ocVList?.measureContainerHeight?.()
            this.tryAutoLoadMore()
          })
        })
        .exec()
    },
    avatarUrl(item) {
      if (item && item.photoFileUrl) {
        return resolveResourceUrl(item.photoFileUrl)
      }
      return ''
    },
    nameFor(item) {
      return item.resourceName || this.$t('onlineClinicPage.nameFallback')
    },
    subFor(item) {
      if (item.affiliatedUnit) {
        return item.affiliatedUnit
      }
      if (item.serviceStartTime && item.serviceEndTime) {
        return this.$t('onlineClinicPage.serviceHoursTpl', {
          start: item.serviceStartTime,
          end: item.serviceEndTime
        })
      }
      return ''
    },
    introFor(item) {
      return item.introduction || this.$t('onlineClinicPage.noIntro')
    },
    onProfile(item) {
      if (!item || !item.id) return
      saveVetProfileCache(item.id, item.raw || item)
      uni.navigateTo({
        url: `${VET_PROFILE_PATH}?id=${encodeURIComponent(item.id)}`
      })
    },
    onConsult(item) {
      if (!item || !item.id || this.consultingId) return
      if (!ensureApiToken()) return
      saveVetProfileCache(item.id, item.raw || item)
      this.consultingId = item.id
      uni.showLoading({ title: this.$t('onlineClinicPage.openingSession'), mask: true })
      openOnlineConsultSession({ vetResourceId: Number(item.id) || item.id })
        .then((res) => {
          const session = res.data || {}
          const sessionId = session.id != null ? String(session.id) : ''
          if (!sessionId) {
            return
          }
          const receiverName = session.receiverName || item.resourceName || ''
          const q = [
            `sessionId=${encodeURIComponent(sessionId)}`,
            `vetResourceId=${encodeURIComponent(item.id)}`,
            `id=${encodeURIComponent(item.id)}`,
            `name=${encodeURIComponent(receiverName)}`,
            `sub=${encodeURIComponent(this.subFor(item))}`,
            `intro=${encodeURIComponent(item.introduction || '')}`,
            `avatar=${encodeURIComponent(item.avatarText || '')}`
          ].join('&')
          uni.navigateTo({ url: `${CONSULT_DETAIL_PATH}?${q}` })
        })
        .finally(() => {
          this.consultingId = ''
          uni.hideLoading()
        })
    }
  }
}
</script>

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

.oc-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;
}

.oc-top {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.oc-card {
  background: #ffffff;
  padding: 16rpx 20rpx;
  box-sizing: border-box;
  border-bottom: 1rpx solid $morandi-border-soft;
}

.oc-body {
  flex: 1;
  min-height: 0;
  height: 0;
  width: 100%;
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: 0 24rpx 16rpx;
  box-sizing: border-box;
  overflow: hidden;
  background: $morandi-bg-page;
}

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

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

.oc-empty__txt {
  color: $morandi-text-muted;
}

.oc-vlist {
  flex: 1;
  min-height: 0;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  padding: 10rpx 0 0;
}

.oc-cell {
  height: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  padding-bottom: 10rpx;
}

.oc-item {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 16rpx;
  padding: 20rpx;
  box-sizing: border-box;
  background: #ffffff;
  border-radius: 12rpx;
  border: 1rpx solid $morandi-border-soft;
  min-width: 0;
  flex-shrink: 0;
  overflow: hidden;
}

.oc-item__row1 {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 16rpx;
  min-width: 0;
}

.oc-item__right {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 10rpx;
}

.oc-item__head {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 12rpx;
  min-width: 0;
}

.oc-item__titles {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4rpx;
}

.oc-item__btn {
  width: 86rpx;
}

.oc-item__name {
  font-size: 30rpx;
  font-weight: 600;
  color: #111827;
  line-height: 1.35;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.oc-item__sub {
  font-size: 24rpx;
  color: $morandi-text-secondary;
  line-height: 1.35;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.oc-item__intro {
  font-size: 22rpx;
  color: $morandi-text-muted;
  line-height: 1.45;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  word-break: break-word;
}

.oc-item__intro-k {
  color: $morandi-text-muted;
  font-weight: 500;
}

.oc-page.lang-bo {
  .oc-item__name {
    font-size: 26rpx;
  }

  .oc-item__sub {
    font-size: 22rpx;
  }
}
</style>
