<template>
  <view :class="pageRootClass" class="tab-page msg-page">
    <view class="msg-head">
      <view class="msg-search">
        <up-icon class="msg-search__icon" name="search" color="#9A9590" :size="18" />
        <input
          v-model="query"
          class="msg-search__input"
          type="text"
          :placeholder="$t('messagePage.searchPlaceholder')"
          placeholder-class="msg-search__ph"
          confirm-type="search"
        />
      </view>
    </view>

    <view class="msg-scroll" :style="scrollWrapStyle">
      <view v-if="listLoading && !sessions.length" class="msg-empty">
        <text class="msg-empty__txt">{{ $t('messagePage.loading') }}</text>
      </view>
      <view v-else-if="!displayRows.length" class="msg-empty">
        <text class="msg-empty__txt">{{ emptyHint }}</text>
      </view>
      <up-virtual-list
        v-else
        class="msg-vlist"
        :list-data="displayRows"
        :item-height="slotHeightPx"
        :height="scrollAreaPx"
        :buffer="8"
        key-field="id"
        :scroll-top="vScrollTop"
        @update:scrollTop="vScrollTop = $event"
        @scroll="onVirtualListScroll"
      >
        <template #default="{ item }">
          <view class="msg-vlist__cell">
            <view
              class="msg-row"
              :class="{ 'msg-row--hl': highlightId === item.id }"
              :style="{ height: rowBodyPx + 'px' }"
              role="button"
              @tap="onRowTap(item)"
              @longpress.stop="onRowLongPress(item)"
            >
              <up-avatar
                v-if="avatarUrl(item)"
                class="msg-row__avatar"
                shape="circle"
                :src="avatarUrl(item)"
                size="44"
                font-size="16"
              />
              <up-avatar
                v-else
                class="msg-row__avatar"
                shape="circle"
                :text="avatarLetter(item)"
                size="44"
                font-size="16"
                :bg-color="avatarBg(item)"
                color="#fdfcfa"
              />
              <view class="msg-row__mid">
                <text class="text-body msg-row__title">{{ vetTitle(item) }}</text>
                <text class="text-body msg-row__preview">{{ previewText(item) }}</text>
              </view>
              <text class="msg-row__date">{{ formatDate(item.ts) }}</text>
            </view>
          </view>
        </template>
      </up-virtual-list>
    </view>
  </view>
</template>

<script>
/**
 * 消息 Tab：对接 GET /app/consult/vet/session/list（M3）、POST .../hide（M4）
 */
import UIcon from 'uview-plus/components/u-icon/u-icon.vue'
import UAvatar from 'uview-plus/components/u-avatar/u-avatar.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 {
  listAskerConsultSessions,
  hideAskerConsultSession,
  loadVetProfileCache
} from '@/api/onlineConsult'
import { loadBookingResourceCache } from '@/api/bookingService'

const ROW_BODY_RPX = 168
const ROW_GAP_RPX = 20
const PAGE_SIZE = 24
const CONSULT_DETAIL_PATH = '/package-a/consult-detail/index'
const HIDDEN_SESSIONS_KEY = 'asker_hidden_consult_sessions'

export default {
  components: {
    'up-icon': UIcon,
    'up-avatar': UAvatar,
    'up-virtual-list': UVirtualList
  },
  mixins: [tabPage],
  data() {
    return {
      navTitleKey: 'nav.message',
      query: '',
      sessions: [],
      listLoading: false,
      listLoadingMore: false,
      listTotal: 0,
      pageNum: 1,
      pageSize: PAGE_SIZE,
      rowBodyPx: 76,
      marginPx: 10,
      slotHeightPx: 86,
      scrollAreaPx: 400,
      highlightId: '',
      headHeightPx: 0,
      vScrollTop: 0,
      loadMoreTimer: null,
      hiddenSessionIds: []
    }
  },
  computed: {
    queryTrim() {
      return (this.query || '').trim()
    },
    listNoMore() {
      return this.listTotal > 0 && this.sessions.length >= this.listTotal
    },
    visibleSessions() {
      const hidden = new Set(this.hiddenSessionIds)
      return this.sessions.filter((row) => !hidden.has(row.id))
    },
    filteredFull() {
      const q = this.queryTrim.toLowerCase()
      if (!q) return this.visibleSessions
      return this.visibleSessions.filter((row) => {
        const t = `${this.vetTitle(row)} ${this.previewText(row)}`.toLowerCase()
        return t.includes(q)
      })
    },
    displayRows() {
      return this.filteredFull
    },
    emptyHint() {
      if (this.queryTrim) {
        return this.$t('messagePage.empty')
      }
      return this.$t('messagePage.emptyList')
    },
    scrollWrapStyle() {
      const h = this.scrollAreaPx
      const top = this.headHeightPx
      return {
        height: `${h}px`,
        marginTop: `${top}px`
      }
    }
  },
  watch: {
    queryTrim() {
      this.vScrollTop = 0
    }
  },
  onShow() {
    this.loadHiddenSessionIds()
    if (ensureApiToken(false)) {
      this.loadSessionList(true)
    }
  },
  onReady() {
    try {
      this.marginPx = Math.ceil(uni.upx2px(ROW_GAP_RPX))
      this.rowBodyPx = Math.max(64, Math.ceil(uni.upx2px(ROW_BODY_RPX)))
      this.slotHeightPx = this.rowBodyPx + this.marginPx
    } catch (e) {
      this.marginPx = 10
      this.rowBodyPx = 76
      this.slotHeightPx = 86
    }
    this.headHeightPx = Math.ceil(uni.upx2px(132))
    this.applyScrollLayout()
    this.$nextTick(() => {
      uni.createSelectorQuery()
        .in(this)
        .select('.msg-head')
        .boundingClientRect((rect) => {
          if (rect && rect.height) {
            this.headHeightPx = Math.ceil(rect.height)
            this.applyScrollLayout()
          }
        })
        .exec()
    })
  },
  onUnload() {
    if (this.loadMoreTimer) clearTimeout(this.loadMoreTimer)
  },
  methods: {
    loadHiddenSessionIds() {
      try {
        const raw = uni.getStorageSync(HIDDEN_SESSIONS_KEY)
        if (!raw) {
          this.hiddenSessionIds = []
          return
        }
        const arr = typeof raw === 'string' ? JSON.parse(raw) : raw
        this.hiddenSessionIds = Array.isArray(arr) ? arr.map(String) : []
      } catch (e) {
        this.hiddenSessionIds = []
      }
    },
    saveHiddenSessionIds() {
      try {
        uni.setStorageSync(HIDDEN_SESSIONS_KEY, JSON.stringify(this.hiddenSessionIds))
      } catch (e) {
        /* noop */
      }
    },
    applyScrollLayout() {
      const sys = uni.getSystemInfoSync()
      const winH = sys.windowHeight || 600
      const head = this.headHeightPx || Math.ceil(uni.upx2px(132))
      this.scrollAreaPx = Math.max(200, winH - head - 30)
    },
    mapSessionRow(row) {
      const name = row.receiverName || ''
      const ts = row.lastMessageTime ? new Date(row.lastMessageTime).getTime() : 0
      return {
        id: String(row.id != null ? row.id : ''),
        sessionId: String(row.id != null ? row.id : ''),
        vetResourceId: row.receiverProviderId != null ? String(row.receiverProviderId) : '',
        receiverName: name,
        lastMessagePreview: row.lastMessagePreview || '',
        lastMessageTime: row.lastMessageTime || '',
        ts: Number.isFinite(ts) ? ts : 0,
        avatarText: name.slice(0, 1) || '?',
        raw: row
      }
    },
    loadSessionList(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
      }
      return listAskerConsultSessions({
        pageNum: this.pageNum,
        pageSize: this.pageSize
      })
        .then((res) => {
          const rows = (res.rows || []).map((row) => this.mapSessionRow(row))
          this.listTotal = res.total != null ? Number(res.total) : 0
          this.sessions = reset ? rows : this.sessions.concat(rows)
          this.$nextTick(() => this.tryAutoLoadMore())
        })
        .catch(() => {
          if (reset) {
            this.sessions = []
            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.displayRows.length * this.slotHeightPx
      const viewH = this.scrollAreaPx
      if (totalH > 0 && totalH <= viewH) {
        this.loadSessionList(false)
      }
    },
    vetTitle(row) {
      if (row.receiverName) {
        return row.receiverName
      }
      return this.$t('messagePage.vetName', { n: '?' })
    },
    previewText(row) {
      const p = (row.lastMessagePreview || '').trim()
      return p || this.$t('messagePage.noPreview')
    },
    formatDate(ts) {
      if (!ts) return ''
      const d = new Date(ts)
      if (isNaN(d.getTime())) return ''
      const m = d.getMonth() + 1
      const day = d.getDate()
      if (this.lang === 'bo') {
        return `${m}/${day}`
      }
      return `${m}月${day}日`
    },
    avatarUrl(item) {
      if (!item || !item.vetResourceId) return ''
      const id = item.vetResourceId
      const fromProfile = loadVetProfileCache(id)
      const fromBooking = loadBookingResourceCache('004001', id)
      const url = (fromProfile && fromProfile.photoFileUrl) || (fromBooking && fromBooking.photoFileUrl)
      return url ? resolveResourceUrl(url) : ''
    },
    avatarBg(row) {
      const colors = ['#B8C5B8', '#C9B8A8', '#A8B4C9', '#C4B8C8', '#B5A896']
      const seed = row.vetResourceId || row.id || '0'
      let n = 0
      for (let i = 0; i < seed.length; i++) {
        n += seed.charCodeAt(i)
      }
      return colors[n % colors.length]
    },
    avatarLetter(row) {
      const t = row.avatarText || this.vetTitle(row)
      if (!t) return '?'
      return t.charAt(0)
    },
    onVirtualListScroll(scrollTop) {
      const stitle = typeof scrollTop === 'number' ? scrollTop : 0
      if (this.loadMoreTimer) clearTimeout(this.loadMoreTimer)
      this.loadMoreTimer = setTimeout(() => {
        this.loadMoreTimer = null
        this.tryLoadMoreOnScroll(st)
      }, 180)
    },
    tryLoadMoreOnScroll(scrollTop) {
      if (this.listLoading || this.listLoadingMore || this.listNoMore) return
      const totalH = this.displayRows.length * this.slotHeightPx
      const viewH = this.scrollAreaPx
      const threshold = Math.max(this.slotHeightPx * 2, 120)
      if (totalH <= viewH) {
        this.loadSessionList(false)
        return
      }
      if (scrollTop + viewH >= totalH - threshold) {
        this.loadSessionList(false)
      }
    },
    onRowTap(item) {
      if (!item || !item.sessionId) return
      const q = [
        `sessionId=${encodeURIComponent(item.sessionId)}`,
        `vetResourceId=${encodeURIComponent(item.vetResourceId || '')}`,
        `id=${encodeURIComponent(item.vetResourceId || '')}`,
        `name=${encodeURIComponent(item.receiverName || '')}`
      ].join('&')
      uni.navigateTo({
        url: `${CONSULT_DETAIL_PATH}?${q}`
      })
    },
    onRowLongPress(row) {
      this.highlightId = row.id
      uni.showActionSheet({
        itemList: [this.$t('messagePage.delete')],
        success: (res) => {
          if (res.tapIndex === 0) {
            this.removeConversation(row)
          }
          this.highlightId = ''
        },
        fail: () => {
          this.highlightId = ''
        }
      })
    },
    removeConversation(row) {
      if (!row || !row.sessionId) return
      hideAskerConsultSession(row.sessionId)
        .then(() => {
          const sid = String(row.sessionId)
          if (!this.hiddenSessionIds.includes(sid)) {
            this.hiddenSessionIds.push(sid)
            this.saveHiddenSessionIds()
          }
          const i = this.sessions.findIndex((x) => x.id === row.id)
          if (i !== -1) {
            this.sessions.splice(i, 1)
          }
          this.vScrollTop = 0
        })
        .catch(() => {
          uni.showToast({ title: this.$t('messagePage.deleteFail'), icon: 'none' })
        })
    }
  }
}
</script>

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

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

.msg-head {
  position: fixed;
  top: 80rpx;
  left: 0;
  right: 0;
  z-index: 100;
  flex-shrink: 0;
  padding: 24rpx 24rpx 20rpx;
  box-sizing: border-box;
  background: $morandi-bg-page;
  box-shadow: 0 4rpx 16rpx rgba(74, 69, 66, 0.06);
}

.msg-search {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 16rpx;
  min-width: 0;
  padding: 18rpx 22rpx;
  border-radius: 20rpx;
  background: $morandi-bg-card-inner;
  border: 1rpx solid $morandi-border-soft;
}

.msg-search__icon {
  flex-shrink: 0;
}

.msg-search__input {
  flex: 1;
  min-width: 0;
  font-size: 28rpx;
  line-height: 1.4;
  color: $morandi-text-secondary;
}

.msg-search__ph {
  color: $morandi-text-muted;
  font-size: 26rpx;
}

.msg-scroll {
  flex: 1;
  min-height: 0;
  min-width: 0;
  padding: 0 16rpx 16rpx;
  box-sizing: border-box;
}

.msg-vlist {
  width: 100%;
}

.msg-vlist__cell {
  height: 100%;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}

.msg-row {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 20rpx;
  min-width: 0;
  padding: 22rpx 20rpx;
  box-sizing: border-box;
  border-radius: 16rpx;
  background: $morandi-bg-card;
  border: 1rpx solid $morandi-border;
  flex-shrink: 0;
}

.msg-row--hl {
  background: $morandi-highlight;
  border-color: $morandi-highlight-border;
}

.msg-row__avatar {
  flex-shrink: 0;
}

.msg-row__mid {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 10rpx;
  min-height: 0;
}

.msg-row__title {
  font-size: 32rpx;
  font-weight: 600;
  line-height: 1.35;
  color: $morandi-text;
  word-break: break-word;
  overflow-wrap: anywhere;
}

.msg-row__preview {
  font-size: 24rpx;
  line-height: 1.45;
  color: $morandi-text-muted;
  word-break: break-word;
  overflow-wrap: anywhere;
}

.msg-row__date {
  flex-shrink: 0;
  align-self: flex-start;
  margin-top: 4rpx;
  font-size: 22rpx;
  color: $morandi-text-soft;
}

.msg-empty {
  padding: 80rpx 24rpx;
  display: flex;
  justify-content: center;
}

.msg-empty__txt {
  font-size: 26rpx;
  color: $morandi-text-muted;
}
</style>
