<template>
  <view class="container">
    <!-- Header -->
    <view class="header">
      <picker
        v-if="useTeamPicker"
        mode="selector"
        :range="teamNameRange"
        :value="teamPickerIndex"
        @change="onTeamPickerChange"
      >
        <view class="team-selector">
          <text class="team-name">{{ teamDisplay }}</text>
          <text class="team-icon">⇌</text>
        </view>
      </picker>
      <view v-else class="team-selector" @click="toggleTeamPopup">
        <text class="team-name">{{ teamDisplay }}</text>
        <text class="team-icon">⇌</text>
      </view>
      <view class="header-actions">
        <view class="action-item" @click="goToSearch">
          <uni-icons type="search" size="22" color="#333"></uni-icons>
          <text class="action-text">搜索</text>
        </view>
        <view class="action-item" @click="toggleBatchMode">
          <uni-icons type="checkbox" size="22" color="#333"></uni-icons>
          <text class="action-text">批量</text>
        </view>
        <view class="action-item" @click="goToGroupManage">
          <uni-icons type="staff" size="22" color="#333"></uni-icons>
          <text class="action-text">分组</text>
        </view>
        <view class="action-item" @click="handleCreate">
          <uni-icons type="plusempty" size="22" color="#333"></uni-icons>
          <text class="action-text">新增</text>
        </view>
      </view>
    </view>

    <!-- Tabs and Count -->
    <view class="tabs-area">
      <scroll-view scroll-x class="tabs-scroll" :show-scrollbar="false">
        <view class="tabs-container">
          <view v-for="tab in tabs" :key="tab.key" class="tab-item" :class="{ active: currentTabKey === tab.key }"
            @click="onTabClick(tab)">
            {{ tab.label }}
          </view>
        </view>
      </scroll-view>
      <view class="total-count-inline">共{{ totalPatients }}条</view>
    </view>

    <!-- Main Content -->
    <view class="content-body">
      <!-- Patient List -->
      <scroll-view scroll-y class="patient-list" :scroll-into-view="scrollIntoId" :scroll-with-animation="true"
        lower-threshold="80" @scrolltolower="loadMore">
        <view v-for="group in patientList" :key="group.letter" :id="letterToDomId(group.letter)">
          <view class="group-title">{{ group.letter }}</view>

          <view v-for="(patient, pIndex) in group.data" :key="pIndex" class="patient-card"
            @click="handlePatientClick(patient)">
            <!-- Checkbox for Batch Mode -->
            <view v-if="isBatchMode" class="checkbox-area">
              <uni-icons :type="selectedItems.includes(getSelectId(patient)) ? 'checkbox-filled' : 'checkbox'" size="24"
                :color="selectedItems.includes(getSelectId(patient)) ? '#007aff' : '#ccc'"></uni-icons>
            </view>
            <view class="card-content">
              <!-- Row 1 -->
              <view class="card-row-top">
                <view class="patient-info">
                  <text class="patient-name">{{ patient.name }}</text>
                  <text class="patient-meta">{{ patient.gender }}{{ patient.age ? '/' + patient.age + '岁' : '' }}</text>
                </view>
                <view class="patient-tags">
                  <view v-for="(tag, tIndex) in resolveGroupTags(patient)" :key="tIndex" class="tag">
                    {{ tag }}
                  </view>
                </view>
              </view>

              <!-- Row 2 -->
              <view v-if="currentTabKey === 'new'" class="card-row-middle">
                <text v-if="patient.record" class="record-text record-ellipsis">
                  {{ patient.record.type }} / {{ patient.record.date }} / {{ patient.record.diagnosis }}
                </text>
                <text v-else class="no-record">暂无病历记录</text>
              </view>

              <!-- Row 3 -->
              <view class="card-row-bottom">
                <template v-if="currentTabKey === 'new'"> <!-- New Patient Tab -->
                  <text class="record-text">
                    {{ resolveRecentAddTime(patient) }} {{ resolveRecentAddMeta(patient) }}
                  </text>
                </template>
                <template v-else>
                  <text v-if="patient.record" class="record-text record-ellipsis">
                    {{ patient.record.type }} / {{ patient.record.date }} / {{ patient.record.diagnosis }}
                  </text>
                  <text v-else class="no-record">暂无病历记录</text>
                </template>
              </view>
            </view>
          </view>
        </view>
        <!-- Bottom padding for tabbar -->
        <view style="height: 200rpx;"></view> <!-- Increased padding -->
      </scroll-view>

      <!-- Sidebar Index -->
      <view v-if="!isBatchMode" class="sidebar-index">
        <view v-for="letter in indexList" :key="letter" class="index-item" @click="scrollToLetter(letter)">
          {{ letter }}
        </view>
      </view>
    </view>

    <!-- Batch Actions Footer -->
    <view v-if="isBatchMode" class="batch-footer">
      <view class="left-action" @click="handleSelectAll">
        <uni-icons
          :type="selectedItems.length > 0 && selectedItems.length === patientList.flatMap(g => g.data).length ? 'checkbox-filled' : 'checkbox'"
          size="24" color="#666"></uni-icons>
        <text class="footer-text">全选 ({{ selectedItems.length }})</text>
      </view>
      <view class="right-actions">
        <button class="footer-btn plain" @click="cancelBatch">取消</button>
        <button class="footer-btn primary" @click="handleTransfer">转移</button>
        <button class="footer-btn primary" @click="handleShare">共享</button>
      </view>
    </view>
  </view>
</template>

<script setup>
import { ref, computed, watch } from 'vue';
import { onLoad, onShow } from '@dcloudio/uni-app';
import { storeToRefs } from 'pinia';
import dayjs from 'dayjs';

import api from '@/utils/api';
import useAccountStore from '@/store/account';
import useInfoCheck from '@/hooks/useInfoCheck';
import { confirm as uniConfirm, hideLoading, loading as showLoading, toast } from '@/utils/widget';

// State
const teams = ref([]);
const currentTeam = ref(null);
const currentTabKey = ref('all');
const scrollIntoId = ref('');
const teamGroups = ref([]);
const useTeamPicker = computed(() => (Array.isArray(teams.value) ? teams.value.length : 0) > 6);
const teamNameRange = computed(() => (Array.isArray(teams.value) ? teams.value.map((t) => t?.name || '') : []));
const teamPickerIndex = ref(0);
const suppressTabReload = ref(false);
const tabs = computed(() => {
  const base = [
    { key: 'all', label: '全部', kind: 'all' },
    { key: 'new', label: '新患者', kind: 'new' },
  ];
  const groupTabs = (Array.isArray(teamGroups.value) ? teamGroups.value : [])
    .filter((g) => g && g._id && g.groupName)
    .map((g) => ({ key: `group:${g._id}`, label: String(g.groupName), kind: 'group', groupId: String(g._id) }));
  return [...base, ...groupTabs];
});
const isBatchMode = ref(false);
const selectedItems = ref([]); // Stores patient phone or unique ID

const groupNameMap = computed(() => {
  const map = new Map();
  (Array.isArray(teamGroups.value) ? teamGroups.value : []).forEach((g) => {
    const id = g && g._id ? String(g._id) : '';
    const name = g && g.groupName ? String(g.groupName) : '';
    if (id && name) map.set(id, name);
  });
  return map;
});

// Team Members Map
const userNameMap = ref({});
const loadedMembersTeamId = ref('');
const corpMemberNameInflight = new Map(); // userId -> Promise
const corpMemberNameTried = new Set(); // avoid retry storms on failures
let corpMemberBatchInflight = null; // Promise | null

// 新增流程所需状态（认证相关）
const managedArchiveCountAllTeams = ref(0); // 在管档案数（所有团队）
const verifyStatus = ref(''); // unverified | verifying | verified | failed
const isVerified = ref(false); // 是否已认证
const hasVerifyFailedHistory = ref(false); // 是否有历史认证失败
const verifyFailedReason = ref('');

const DETAIL_STORAGE_KEY = 'ykt_case_archive_detail';
const CURRENT_TEAM_STORAGE_KEY = 'ykt_case_current_team';
const NEED_RELOAD_STORAGE_KEY = 'ykt_case_need_reload';
const GROUPS_RELOAD_KEY = 'ykt_case_groups_need_reload';
const BATCH_CUSTOMER_IDS_KEY = 'ykt_case_batch_customer_ids';

const page = ref(1);
const pages = ref(0);
const pageSize = ref(200);
const totalFromApi = ref(0);
const loading = ref(false);
const rawPatients = ref([]);
const more = computed(() => page.value < pages.value);
const currentTab = computed(() => tabs.value.find((t) => t.key === currentTabKey.value) || tabs.value[0]);
const lastTeamsLoadOk = ref(true);
const lastGroupsLoadOk = ref(true);
const lastPatientsLoadOk = ref(true);
const loadedGroupsTeamId = ref('');
let enterRefreshInflight = null;
const hasEnteredOnce = ref(false);
let lastReloadStartedAt = 0;

const accountStore = useAccountStore();
const { account, doctorInfo } = storeToRefs(accountStore);
const { getDoctorInfo } = accountStore;
const { withInfo } = useInfoCheck();

const teamDisplay = computed(() => `${currentTeam.value?.name || ''}`);

watch(
  [teams, currentTeam],
  () => {
    const list = Array.isArray(teams.value) ? teams.value : [];
    const tid = currentTeam.value?.teamId ? String(currentTeam.value.teamId) : '';
    const idx = tid ? list.findIndex((t) => String(t?.teamId || '') === tid) : -1;
    teamPickerIndex.value = idx >= 0 ? idx : 0;
  },
  { immediate: true }
);

function asArray(value) {
  return Array.isArray(value) ? value : [];
}

function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function getNetworkTypeSafe() {
  try {
    return await new Promise((resolve) => {
      let done = false;
      const timer = setTimeout(() => {
        if (done) return;
        done = true;
        resolve('unknown');
      }, 800);

      uni.getNetworkType({
        success: (res) => {
          if (done) return;
          done = true;
          clearTimeout(timer);
          resolve(String(res?.networkType || 'unknown'));
        },
        fail: () => {
          if (done) return;
          done = true;
          clearTimeout(timer);
          resolve('unknown');
        },
      });
    });
  } catch {
    return 'unknown';
  }
}

function pickPageSizeByNetwork(type) {
  const t = String(type || '').toLowerCase();
  if (t === '2g') return 100;
  if (t === '3g') return 150;
  if (t === '4g') return 250;
  if (t === '5g' || t === 'wifi') return 300;
  return 200;
}

async function apiWithRetry(urlId, data, showLoading = true, opts = {}) {
  const retries = Number(opts?.retries ?? 1);
  const baseDelay = Number(opts?.baseDelay ?? 350);
  const shouldRetryMessage = (msg) =>
    /timeout|timed out|超时|网络|network|fail|connect|ECONN|ENOTFOUND/i.test(String(msg || ''));

  for (let i = 0; i <= retries; i += 1) {
    try {
      const res = await api(urlId, data, showLoading);
      if (res?.success) return res;
      const message = String(res?.message || '');
      if (i < retries && shouldRetryMessage(message)) {
        await sleep(Math.min(baseDelay * Math.pow(2, i), 1500));
        continue;
      }
      return res;
    } catch (e) {
      if (i < retries) {
        await sleep(Math.min(baseDelay * Math.pow(2, i), 1500));
        continue;
      }
      throw e;
    }
  }

  return { success: false, message: '请求失败' };
}

function normalizeUserId(value) {
  if (value === null || value === undefined) return '';
  if (typeof value === 'object') {
    const obj = value;
    const picked =
      obj.userid ||
      obj.userId ||
      obj.userID ||
      obj.corpUserId ||
      obj.corpUserID ||
      obj._id ||
      obj.id ||
      '';
    return String(picked || '').trim();
  }
  return String(value || '').trim();
}

function resolveUserName(userId) {
  const id = normalizeUserId(userId);
  if (!id) return '';
  // 优先使用当前登录人的信息（避免映射未命中时只显示 userid）
  const d = doctorInfo.value || {};
  const doctorId = normalizeUserId(d.userid || d.userId || d.corpUserId || '');
  if (doctorId && doctorId === id) {
    const display = String(d.anotherName || d.name || d.username || d.userid || '').trim();
    if (display) return display;
  }
  const mapped = userNameMap.value[id] || userNameMap.value[id.toLowerCase()];
  if (mapped) return mapped;
  // 不阻塞渲染：后台补齐非团队成员姓名（例如其他团队成员创建）
  if (isLikelyUserId(id)) void fetchCorpMemberDisplayName(id).catch(() => {});
  return id;
}

function isLikelyUserId(value) {
  const id = normalizeUserId(value);
  if (!id) return false;
  if (/[\u4e00-\u9fa5]/.test(id)) return false; // already looks like a name
  if (/\s/.test(id)) return false;
  return true;
}

function extractDisplayNameFromAny(payload) {
  if (!payload) return '';
  if (typeof payload === 'string') return payload.trim();
  if (Array.isArray(payload)) return extractDisplayNameFromAny(payload[0]);
  if (typeof payload !== 'object') return '';
  const obj = payload;
  const candidate =
    obj.anotherName ||
    obj.name ||
    obj.realName ||
    obj.username ||
    obj.nickName ||
    obj.nickname ||
    obj.displayName ||
    obj.userName ||
    obj.userid ||
    obj.userId ||
    obj.corpUserId ||
    '';
  return String(candidate || '').trim();
}

function extractDisplayNameFromCorpMember(row) {
  const m = row && typeof row === 'object' ? row : {};
  const name = String(m.anotherName || m.name || '').trim();
  return name;
}

async function batchFetchCorpMemberDisplayNames(userIds) {
  const ids = Array.isArray(userIds) ? userIds.map(normalizeUserId).filter(Boolean) : [];
  if (!ids.length) return;
  const uniq = Array.from(new Set(ids));

  const unresolved = uniq.filter((id) => {
    if (!isLikelyUserId(id)) return false;
    const cached = userNameMap.value?.[id] || userNameMap.value?.[id.toLowerCase()];
    return !cached || cached === id;
  });
  if (!unresolved.length) return;

  if (corpMemberBatchInflight) return corpMemberBatchInflight;

  const corpId = getCorpId() || String(account.value?.corpId || doctorInfo.value?.corpId || '');
  if (!corpId) return;

  corpMemberBatchInflight = (async () => {
    try {
      const res = await api(
        'getCorpMember',
        {
          page: 1,
          pageSize: Math.min(Math.max(unresolved.length, 10), 500),
          params: {
            corpId,
            memberList: unresolved,
          },
        },
        false
      );

      if (!res?.success) return;
      const rows = Array.isArray(res?.data) ? res.data : Array.isArray(res?.data?.data) ? res.data.data : [];
      if (!rows.length) return;

      const next = { ...(userNameMap.value || {}) };
      rows.forEach((m) => {
        const id = normalizeUserId(m?.userid || m?.userId || m?.corpUserId || '');
        if (!id) return;
        const display = extractDisplayNameFromCorpMember(m) || id;
        const existing = next[id] || next[id.toLowerCase()];
        // 仅补缺：不覆盖已有的非空/非同值映射
        if (existing && existing !== id) return;
        if (display && display !== id) {
          next[id] = display;
          next[id.toLowerCase()] = display;
        }
      });
      userNameMap.value = next;
    } catch {
      // ignore
    }
  })().finally(() => {
    corpMemberBatchInflight = null;
  });

  return corpMemberBatchInflight;
}

async function fetchCorpMemberDisplayName(userId) {
  const id = normalizeUserId(userId);
  if (!id) return '';
  if (!isLikelyUserId(id)) return '';

  const cached = userNameMap.value[id] || userNameMap.value[id.toLowerCase()];
  if (cached && cached !== id) return cached;

  if (corpMemberNameInflight.has(id)) return corpMemberNameInflight.get(id);
  if (corpMemberNameTried.has(id)) return '';

  const corpId = getCorpId() || String(account.value?.corpId || doctorInfo.value?.corpId || '');
  if (!corpId) return '';

  const p = (async () => {
    corpMemberNameTried.add(id);

    // 1) 首选：企业成员主页信息（更可能支持用 userid 查询）
    try {
      const res = await api('getCorpMemberHomepageInfo', { corpId, corpUserId: id }, false);
      if (res?.success) {
        const name =
          extractDisplayNameFromAny(res?.data) ||
          extractDisplayNameFromAny(res?.data?.data) ||
          extractDisplayNameFromAny(res?.data?.member) ||
          '';
        if (name) return name;
      }
    } catch {
      // ignore
    }

    // 1.1) 有的后端参数名为 userId
    try {
      const res = await api('getCorpMemberHomepageInfo', { corpId, userId: id }, false);
      if (res?.success) {
        const name =
          extractDisplayNameFromAny(res?.data) ||
          extractDisplayNameFromAny(res?.data?.data) ||
          extractDisplayNameFromAny(res?.data?.member) ||
          '';
        if (name) return name;
      }
    } catch {
      // ignore
    }

    // 2) 兜底：成员列表接口（支持 memberList）
    try {
      const res = await api(
        'getCorpMember',
        {
          page: 1,
          pageSize: 10,
          params: {
            corpId,
            memberList: [id],
          },
        },
        false
      );
      if (res?.success) {
        const rows = Array.isArray(res?.data) ? res.data : Array.isArray(res?.data?.data) ? res.data.data : [];
        const row =
          rows.find((m) => normalizeUserId(m?.userid || m?.userId || m?.corpUserId || '') === id) || rows[0] || null;
        const name = extractDisplayNameFromCorpMember(row) || '';
        if (name) return name;
      }
    } catch {
      // ignore
    }

    return '';
  })()
    .then((name) => {
      const display = String(name || '').trim();
      if (display) {
        const next = { ...(userNameMap.value || {}) };
        next[id] = display;
        next[id.toLowerCase()] = display;
        userNameMap.value = next;
      }
      return display;
    })
    .finally(() => {
      corpMemberNameInflight.delete(id);
    });

  corpMemberNameInflight.set(id, p);
  return p;
}

async function prefetchUserNamesFromPatients(patients) {
  const list = Array.isArray(patients) ? patients : [];
  if (!list.length) return;

  const ids = new Set();
  list.forEach((p) => {
    const u1 = normalizeUserId(p?.recentAddOperatorUserId);
    const u2 = normalizeUserId(p?.creator);
    if (u1) ids.add(u1);
    if (u2) ids.add(u2);
  });

  const targets = Array.from(ids).filter((id) => {
    if (!isLikelyUserId(id)) return false;
    const cached = userNameMap.value[id] || userNameMap.value[id.toLowerCase()];
    return !cached || cached === id;
  });
  if (!targets.length) return;

  // 优先批量补齐（现成后端接口 getCorpMember 支持 memberList）
  await batchFetchCorpMemberDisplayNames(targets);

  const limit = 6;
  let idx = 0;
  const workers = Array.from({ length: Math.min(limit, targets.length) }, async () => {
    while (idx < targets.length) {
      const cur = targets[idx++];
      try {
        const cached = userNameMap.value?.[cur] || userNameMap.value?.[cur.toLowerCase()];
        if (cached && cached !== cur) continue;
        await fetchCorpMemberDisplayName(cur);
      } catch {
        // ignore
      }
    }
  });

  await Promise.allSettled(workers);
}

function normalizeTeam(raw) {
  if (!raw || typeof raw !== 'object') return null;
  const teamId = raw.teamId || raw.id || raw._id || '';
  const name = raw.name || raw.teamName || raw.team || '';
  const corpId = raw.corpId || raw.corpID || '';
  const userId = raw.userId || raw.userid || raw.corpUserId || '';
  if (!teamId || !name) return null;
  return { teamId: String(teamId), name: String(name), corpId: corpId ? String(corpId) : '', userId: userId ? String(userId) : '' };
}

function getUserId() {
  const d = doctorInfo.value || {};
  const a = account.value || {};
  return String(d.userid || d.userId || d.corpUserId || a.userid || a.userId || '') || '';
}

function getCorpId() {
  const t = currentTeam.value || {};
  const a = account.value || {};
  return String(t.corpId || a.corpId || '') || '';
}

function getTeamId() {
  return String(currentTeam.value?.teamId || '') || '';
}

function hasCompleteUserInfo() {
  const userId = getUserId();
  const corpId = String(account.value?.corpId || doctorInfo.value?.corpId || '') || '';
  const anotherName = String(doctorInfo.value?.anotherName || '').trim();
  return Boolean(corpId && userId && anotherName);
}

function ensureUserInfoForFeature() {
  if (hasCompleteUserInfo()) return true;
  toast('请先完善个人信息再使用该功能');
  return false;
}

async function ensureDoctorForQuery() {
  if (account.value?.openid) {
    try {
      const a = account.value || {};
      const accountId = normalizeUserId(a.userid || a.userId || a.corpUserId || '');
      const d = doctorInfo.value || {};
      const doctorId = normalizeUserId(d.userid || d.userId || d.corpUserId || '');
      // doctorInfo 可能是旧缓存：当与当前账号不一致时强制刷新
      if (!doctorId || (accountId && doctorId && accountId !== doctorId)) {
        await getDoctorInfo();
      }
    } catch {
      // ignore
    }
  }
  return Boolean(getUserId());
}

async function loadTeamMembers() {
  const corpId = getCorpId();
  const teamId = getTeamId();
  if (!corpId || !teamId) return;
  if (loadedMembersTeamId.value === teamId && Object.keys(userNameMap.value || {}).length > 0) return;

  try {
    const nextMap = { ...(userNameMap.value || {}) };

    // 以团队详情为准（getTeamMemberAvatarsAndName 存在不全/不准的情况）
    const res = await api('getTeamData', { corpId, teamId });
    if (res?.success) {
      const t = res?.data && typeof res.data === 'object' ? res.data : {};
      const members = Array.isArray(t.memberList) ? t.memberList : [];
      members.forEach((m) => {
        if (typeof m === 'string') {
          const k = normalizeUserId(m);
          if (k) {
            nextMap[k] = nextMap[k] || k;
            nextMap[k.toLowerCase()] = nextMap[k.toLowerCase()] || nextMap[k] || k;
          }
          return;
        }
        const display = String(m?.anotherName || m?.name || m?.userid || m?.userId || m?.corpUserId || '').trim();
        const keys = [m?.userid, m?.userId, m?.corpUserId].map(normalizeUserId).filter(Boolean);
        keys.forEach((k) => {
          nextMap[k] = display || nextMap[k] || k;
          nextMap[String(k).toLowerCase()] = display || nextMap[String(k).toLowerCase()] || k;
        });
      });
    }

    // 补缺：仅当当前没有映射时才用 avatars 接口补齐，避免覆盖正确姓名
    try {
      const avatarRes = await api('getTeamMemberAvatarsAndName', { corpId, teamId });
      const mapObj = avatarRes?.success && avatarRes?.data && typeof avatarRes.data === 'object' ? avatarRes.data : {};
      Object.entries(mapObj).forEach(([uid, info]) => {
        const k = normalizeUserId(uid);
        if (!k) return;
        const existing = nextMap[k] || nextMap[k.toLowerCase()];
        if (existing && existing !== k) return;
        const display = String(info?.name || info?.anotherName || info?.userid || '').trim();
        if (!display || display === k) return;
        nextMap[k] = display;
        nextMap[k.toLowerCase()] = display;
      });
    } catch {
      // ignore
    }

    userNameMap.value = nextMap;
    loadedMembersTeamId.value = teamId;
  } catch (e) {
    console.error('获取团队成员失败', e);
  }
}

function resolveCreatorName(patient) {
  const val = patient.creator;
  if (!val) return '';
  return resolveUserName(val);
}

function resolveRecentAddTime(patient) {
  return patient?.recentAddTime || patient?.createTime || '-';
}

function resolveRecentAddOperatorName(patient) {
  const nameFromApi = String(patient?.recentAddOperatorName || '').trim();
  // 后端部分场景会把 userid 填到 name 字段里，此时仍需通过 userid 解析姓名
  if (nameFromApi && !isLikelyUserId(nameFromApi)) return nameFromApi;
  const uid = patient?.recentAddOperatorUserId || nameFromApi || patient?.creator || '';
  if (!uid) return '';
  return resolveUserName(uid);
}

function resolveRecentAddAction(patient) {
  const t = String(patient?.recentAddType || '').trim();
  if (!t || t === 'create') return '创建';
  if (t === 'share') return '共享';
  if (t.startsWith('transfer')) return '转移';
  return '创建';
}

function resolveRecentAddMeta(patient) {
  const name = resolveRecentAddOperatorName(patient);
  const action = resolveRecentAddAction(patient);
  if (name) return `${name}${action}`;
  if (action === '创建') return '患者创建';
  return '-';
}


function applyVerifyStatus(status, reason) {
  verifyStatus.value = status || '';
  isVerified.value = verifyStatus.value === 'verified';
  hasVerifyFailedHistory.value = verifyStatus.value === 'failed';
  verifyFailedReason.value = hasVerifyFailedHistory.value ? (reason || '') : '';
}

async function refreshVerifyStatus() {
  const corpId = String(account.value?.corpId || doctorInfo.value?.corpId || getCorpId() || '');
  const weChatOpenId = String(account.value?.openid || account.value?.openId || '');
  const id = String(doctorInfo.value?._id || doctorInfo.value?.id || '');
  if (!corpId || !weChatOpenId || !id) {
    applyVerifyStatus(String(doctorInfo.value?.verifyStatus || ''), '');
    return;
  }
  const res = await api('getMemberVerifyStatus', { corpId, weChatOpenId, id });
  if (res && res.success) {
    applyVerifyStatus(String(res.data?.verifyStatus || ''), String(res.data?.reason || ''));
    return;
  }
  applyVerifyStatus(String(doctorInfo.value?.verifyStatus || ''), '');
}

function sortGroupList(list) {
  const { orderList, corpList, restList } = (Array.isArray(list) ? list : []).reduce(
    (p, c) => {
      if (typeof c?.sortOrder === 'number') p.orderList.push(c);
      else if (c?.parentGroupId) p.corpList.push(c);
      else p.restList.push(c);
      return p;
    },
    { orderList: [], corpList: [], restList: [] }
  );
  orderList.sort((a, b) => a.sortOrder - b.sortOrder);
  return [...orderList, ...corpList, ...restList];
}

async function loadGroups() {
  if (!currentTeam.value) return;
  const corpId = getCorpId();
  const teamId = getTeamId();
  if (!corpId || !teamId) return;
  const projection = { _id: 1, groupName: 1, parentGroupId: 1, sortOrder: 1 };
  let res;
  try {
    res = await apiWithRetry(
      'getGroups',
      { corpId, teamId, page: 1, pageSize: 1000, projection, countGroupMember: false },
      false,
      { retries: 1 }
    );
  } catch {
    res = null;
  }
  if (!res?.success) {
    lastGroupsLoadOk.value = false;
    if (!loadedGroupsTeamId.value || loadedGroupsTeamId.value !== teamId) teamGroups.value = [];
    return;
  }
  lastGroupsLoadOk.value = true;
  loadedGroupsTeamId.value = teamId;
  const list = Array.isArray(res.data) ? res.data : Array.isArray(res.data?.data) ? res.data.data : [];
  teamGroups.value = sortGroupList(list);

  // 当前 tab 如果是分组，但分组已不存在，则回退到“全部”
  if (currentTabKey.value.startsWith('group:')) {
    const gid = currentTabKey.value.slice('group:'.length);
    if (!teamGroups.value.some((g) => String(g._id) === String(gid))) currentTabKey.value = 'all';
  }
}

function getLetter(patient) {
  const raw = patient?.firstLetter || patient?.nameFirstLetter || patient?.pinyinFirstLetter || patient?.letter || '';
  const candidate = String(raw || '').trim();
  if (candidate && /^[A-Za-z]$/.test(candidate)) return candidate.toUpperCase();
  const name = String(patient?.name || '').trim();
  const first = name ? name[0] : '';
  if (/^[A-Za-z]$/.test(first)) return first.toUpperCase();
  return '#';
}

function letterToDomId(letter) {
  return `letter-${letter === '#' ? 'HASH' : letter}`;
}

function getSelectId(patient) {
  return patient?._id || patient?.id || patient?.phone || patient?.mobile || '';
}

function getPatientGroupIds(patient) {
  const raw = patient?.groupIds ?? patient?.groupIdList ?? patient?.groupId ?? patient?.groups;
  if (Array.isArray(raw)) return raw.map(String).filter(Boolean);
  if (typeof raw === 'string' || typeof raw === 'number') return [String(raw)].filter(Boolean);
  return [];
}

function resolveGroupTags(patient) {
  const ids = getPatientGroupIds(patient);
  if (!ids.length) return [];
  const map = groupNameMap.value;
  return ids.map((id) => map.get(String(id))).filter(Boolean);
}

function parseCreateTime(value) {
  if (!value) return null;
  if (typeof value === 'number') return dayjs(value);
  if (typeof value === 'string' && /^\d{10,13}$/.test(value)) {
    const n = Number(value);
    const ms = value.length === 10 ? n * 1000 : n;
    return dayjs(ms);
  }
  const asString = String(value);
  const d1 = dayjs(asString);
  if (d1.isValid()) return d1;
  const normalized = asString.replace(/\./g, '-').replace(/\//g, '-');
  const d2 = dayjs(normalized);
  return d2.isValid() ? d2 : null;
}

function normalizeMedicalType(raw) {
  const s = String(raw || '').trim();
  if (!s) return '';
  const lower = s.toLowerCase();
  // 常见后缀：xxxRecord（后端/历史数据可能返回此形式）
  if (lower.endsWith('record') && s.length > 6) {
    return normalizeMedicalType(s.slice(0, -6));
  }
  // 中文兜底（部分接口返回展示名）
  if (s.includes('门诊')) return 'outpatient';
  if (s.includes('住院') || s.includes('入院')) return 'inhospital';
  if (s.includes('预问诊') || s.includes('问诊')) return 'preConsultationRecord';
  if (s.includes('体检')) return 'physicalExaminationTemplate';
  if (lower.includes('preconsult') || (lower.includes('pre') && lower.includes('consult'))) return 'preConsultationRecord';
  if (lower === 'outpatient' || lower === 'out_patient' || lower === 'out-patient') return 'outpatient';
  if (lower === 'inhospital' || lower === 'in_hospital' || lower === 'in-hospital' || lower === 'inpatient') return 'inhospital';
  if (lower === 'physicalexaminationtemplate' || lower === 'physicalexamination' || lower === 'physical_examination') return 'physicalExaminationTemplate';
  if (s === 'outPatient') return 'outpatient';
  if (s === 'inHospital') return 'inhospital';
  if (s === 'preConsultationRecord') return 'preConsultationRecord';
  if (s === 'preConsultationRecord') return 'preConsultationRecord';
  if (s === 'physicalExaminationTemplate') return 'physicalExaminationTemplate';
  return s;
}

function normalizeText(v) {
  if (Array.isArray(v)) {
    const parts = v
      .map((i) => normalizeText(i))
      .filter((i) => i !== null && i !== undefined && String(i).trim());
    return parts.join('，');
  }
  if (v === 0) return '0';
  if (v && typeof v === 'object') {
    const o = v;
    const candidate = o.label ?? o.name ?? o.text ?? o.title ?? o.value ?? o.diseaseName ?? o.code ?? '';
    return candidate ? String(candidate) : '';
  }
  return v ? String(v) : '';
}

function formatAnyDate(value, fmt = 'YYYY-MM-DD') {
  const d = parseCreateTime(value);
  return d ? d.format(fmt) : '';
}

function resolveLatestRecord(lr) {
  if (!lr || typeof lr !== 'object') return null;

  const formData = lr?.formData && typeof lr.formData === 'object' ? lr.formData : null;
  const recordData = lr?.recordData && typeof lr.recordData === 'object' ? lr.recordData : null;
  const data = lr?.data && typeof lr.data === 'object' ? lr.data : null;

  const hasValue = (v) => {
    if (v === 0) return true;
    if (Array.isArray(v)) return v.length > 0;
    if (v && typeof v === 'object') return Object.keys(v).length > 0;
    return v !== null && v !== undefined && String(v).trim() !== '';
  };

  const pick = (...keys) => {
    for (const k of keys) {
      const v0 = lr?.[k];
      if (hasValue(v0)) return v0;
      const v1 = formData?.[k];
      if (hasValue(v1)) return v1;
      const v2 = recordData?.[k];
      if (hasValue(v2)) return v2;
      const v3 = data?.[k];
      if (hasValue(v3)) return v3;
    }
    return undefined;
  };

  const rawType = String(pick('medicalType', 'templateType', 'type') || '').trim();
  const uiType = normalizeMedicalType(rawType);
  // 注意：不要用 type 字段兜底“名称”，很多场景 type 是类型码（如 preConsultation / physicalExaminationTemplate）
  const typeLabelRaw = String(
    pick(
      'tempName',
      'templateName',
      'templateTitle',
      'tempTitle',
      'recordName',
      'medicalName',
      'medicalRecordName',
      'consultName',
      'preConsultationName',
      'inspectName',
      'physicalName',
      'name'
    ) || ''
  ).trim();
  const typeLabel =
    typeLabelRaw && normalizeMedicalType(typeLabelRaw) === uiType ? '' : typeLabelRaw;

  const rawDate = pick(
    'date',
    'visitTime',
    'inhosDate',
    'consultationDate',
    'consultDate',
    'inspectDate',
    'sortTime',
    'createTime',
    'createdAt',
    'updateTime',
    'updatedAt',
    'time'
  );
  const rawDateStr = String(rawDate ?? '').trim();
  const date = (/^\d{10,13}$/.test(rawDateStr) ? (formatAnyDate(rawDateStr, 'YYYY-MM-DD') || rawDateStr) : rawDateStr)
    || formatAnyDate(pick('visitTime', 'inhosDate', 'consultationDate', 'consultDate', 'inspectDate', 'sortTime', 'createTime', 'updateTime'), 'YYYY-MM-DD')
    || '-';

  let third = '';
  // searchCorpCustomerForCaseList 接口存在问题，按类型优先展示：
  // - 预问诊：chiefComplaint
  // - 体检：inspectSummary
  // - 门诊/住院：diagnosisName
  if (uiType === 'outpatient' || uiType === 'inhospital') {
    third = normalizeText(pick('diagnosisName', 'diagnosis'));
  } else if (uiType === 'physicalExaminationTemplate') {
    third = normalizeText(pick('inspectSummary', 'summary', 'summaryText', 'inspectConclusion', 'inspectResult'));
  } else if (uiType === 'preConsultationRecord') {
    third = normalizeText(pick('chiefComplaint', 'chiefComplain', 'chiefComplaintText', 'chiefComplaintContent'));
  } else {
    third = normalizeText(pick('diagnosisName', 'diagnosis', 'inspectSummary', 'summary', 'chiefComplaint'));
  }
  third = String(third || '').replace(/\s+/g, ' ').trim();
  if (!third) {
    // 最后的兜底：避免经常展示为 '-'
    third = normalizeText(pick('summary', 'inspectSummary', 'chiefComplaint', 'presentIllness', 'treatmentPlan', 'abstract', 'brief'));
    third = String(third || '').replace(/\s+/g, ' ').trim();
  }
  if (!String(third || '').trim()) third = '-';

  const type = typeLabel || (uiType === 'outpatient' ? '门诊记录'
    : uiType === 'inhospital' ? '住院记录'
      : uiType === 'preConsultationRecord' ? '预问诊记录'
        : uiType === 'physicalExaminationTemplate' ? '体检档案'
          : '-');

  return { type, date, diagnosis: third };
}

function formatPatient(raw) {
  const name = raw?.name || raw?.customerName || '';
  const sex = raw?.sex || raw?.gender || '';
  const age = raw?.age ?? '';
  const mobiles = asArray(raw?.mobiles).map(String).filter(Boolean);
  const mobile = raw?.mobile ? String(raw.mobile) : (mobiles[0] || '');

  const createTime = parseCreateTime(raw?.createTime);
  const createTimeStr = createTime ? createTime.format('YYYY-MM-DD HH:mm') : '';
  const createTimeTs = createTime ? createTime.valueOf() : 0;

  // 最近一次“新增到当前团队”的时间（后端计算：创建/转移/共享），没有则退化为 createTime
  const recentAddTimeRaw = raw?.recentAddTime ?? raw?.recentAddAt ?? raw?.recentTime;
  const recentAddTime = parseCreateTime(recentAddTimeRaw) || createTime;
  const recentAddTimeStr = recentAddTime ? recentAddTime.format('YYYY-MM-DD HH:mm') : '';
  const recentAddTimeTs = recentAddTime ? recentAddTime.valueOf() : 0;
  const recentAddType = String(raw?.recentAddType || (recentAddTimeRaw ? '' : 'create') || '');
  const recentAddOperatorUserId = String(raw?.recentAddOperatorUserId || raw?.recentAddOperator || raw?.creator || '');

  // 优先使用后端返回的 tagNames（标签名称数组）
  const rawTagNames = asArray(raw?.tagNames).filter((i) => typeof i === 'string' && i.trim());
  // 其次使用 tags（如果是字符串数组）
  const rawTags = asArray(raw?.tags).filter((i) => typeof i === 'string' && i.trim());
  // 最后才使用 tagIds（仅作为兜底，不推荐显示）
  const tagIds = asArray(raw?.tagIds).map(String).filter(Boolean);

  // 解析标签：优先 tagNames > tags（字符串） > tagIds
  const displayTags = rawTagNames.length ? rawTagNames : (rawTags.length ? rawTags : []);

  // 解析病历信息
  let record = null;
  const latestRaw =
    raw?.latestRecord ??
    raw?.latestMedicalRecord ??
    raw?.latestMedicalCase ??
    raw?.lastRecord ??
    raw?.lastMedicalRecord ??
    raw?.recentRecord ??
    null;
  const latest = Array.isArray(latestRaw) ? latestRaw[0] : latestRaw;
  if (latest && typeof latest === 'object') record = resolveLatestRecord(latest);

  return {
    ...raw,
    _id: raw?._id || raw?.id || '',
    name: String(name || ''),
    gender: String(sex || ''),
    age,
    tags: displayTags,
    mobiles,
    mobile,
    createTime: createTimeStr,
    createTimeTs,
    recentAddTime: recentAddTimeStr,
    recentAddTimeTs,
    recentAddType,
    recentAddOperatorUserId,
    recentAddOperatorName: String(
      raw?.recentAddOperatorName
      || raw?.recentAddOperatorAnotherName
      || raw?.recentAddOperatorUserName
      || raw?.recentAddOperatorRealName
      || raw?.recentAddOperatorDisplayName
      || ''
    ).trim(),
    creator: raw?.creatorName || raw?.creator || '',
    hospitalId: raw?.customerNumber || raw?.hospitalId || '',
    record,
    createdByDoctor: raw?.addMethod ? String(raw.addMethod) === 'manual' : Boolean(raw?.createdByDoctor),
    hasBindWechat: Boolean(raw?.externalUserId || raw?.unionid || raw?.hasBindWechat),
  };
}

function groupByLetter(list) {
  const map = new Map();
  list.forEach((item) => {
    const letter = getLetter(item);
    const arr = map.get(letter) || [];
    arr.push(item);
    map.set(letter, arr);
  });
  const letters = Array.from(map.keys()).sort((a, b) => {
    if (a === '#') return 1;
    if (b === '#') return -1;
    return a.localeCompare(b);
  });
  return letters.map((letter) => ({ letter, data: map.get(letter) || [] }));
}

async function loadTeams(opts = {}) {
  if (!doctorInfo.value && account.value?.openid) {
    try {
      await getDoctorInfo();
    } catch (e) {
      // ignore
    }
  }

  const userId = getUserId();
  const corpId = String(account.value?.corpId || doctorInfo.value?.corpId || '') || '';

  if (!corpId || !userId) {
    return;
  }

  let res;
  try {
    res = await apiWithRetry('getTeamBymember', { corpId, corpUserId: userId }, !opts?.silent, { retries: 2 });
  } catch {
    res = null;
  }
  if (!res?.success) {
    lastTeamsLoadOk.value = false;
    if (!opts?.silent) toast(res?.message || '获取团队失败');
    return;
  }

  const list = Array.isArray(res?.data) ? res.data : Array.isArray(res?.data?.data) ? res.data.data : [];
  const normalized = list.map(normalizeTeam).filter(Boolean);
  teams.value = normalized;
  lastTeamsLoadOk.value = true;

  const saved = uni.getStorageSync(CURRENT_TEAM_STORAGE_KEY);
  const savedTeamId = saved?.teamId ? String(saved.teamId) : '';
  currentTeam.value = normalized.find((t) => savedTeamId && t.teamId === savedTeamId) || normalized[0] || null;
  if (currentTeam.value) uni.setStorageSync(CURRENT_TEAM_STORAGE_KEY, currentTeam.value);
}

async function reload(reset = true, opts = {}) {
  if (!currentTeam.value) return false;
  if (loading.value) return false;

  await ensureDoctorForQuery();
  const userId = getUserId();
  const corpId = getCorpId();
  const teamId = getTeamId();
  if (!corpId || !teamId || !userId) {
    return;
  }

  const keepOnFail = Boolean(opts?.keepOnFail);
  const targetPage = reset ? 1 : page.value;

  if (reset && !keepOnFail) {
    page.value = 1;
    rawPatients.value = [];
    pages.value = 0;
    totalFromApi.value = 0;
  }

  if (reset) {
    const net = await getNetworkTypeSafe();
    pageSize.value = pickPageSizeByNetwork(net);
  }

  const query = {
    corpId,
    userId,
    teamId,
    page: targetPage,
    pageSize: pageSize.value, // 按首字母排序时，按网络自适应加载数量
    sortByFirstLetter: true, // 按姓名首字母排序
  };

  if (currentTab.value.kind === 'group' && currentTab.value.groupId) {
    query.groupIds = [currentTab.value.groupId];
  } else if (currentTab.value.kind === 'new') {
    const start = dayjs().subtract(7, 'day').startOf('day').valueOf();
    const end = dayjs().endOf('day').valueOf();
    // “新患者”= 最近7天新增到当前团队：创建 + 转移/共享（时间来自服务记录）
    query.startRecentTime = start;
    query.endRecentTime = end;
    query.includeRecentAddTime = true;
  }

  lastReloadStartedAt = Date.now();
  loading.value = true;
  let res;
  try {
    res = await apiWithRetry('searchCorpCustomerForCaseList', query, !opts?.silent, { retries: 1 });
  } catch {
    res = null;
  } finally {
    loading.value = false;
  }

  if (!res?.success) {
    lastPatientsLoadOk.value = false;
    if (!opts?.silent || (rawPatients.value || []).length === 0) toast(res?.message || '获取患者列表失败');
    return false;
  }

  if (reset) page.value = 1;
  lastPatientsLoadOk.value = true;

  const payload =
    res && typeof res === 'object'
      ? res.data && typeof res.data === 'object' && !Array.isArray(res.data)
        ? res.data
        : res
      : {};
  const list = Array.isArray(payload.list) ? payload.list : Array.isArray(payload.data) ? payload.data : [];
  const next = list.map(formatPatient);
  rawPatients.value = targetPage === 1 ? next : [...rawPatients.value, ...next];
  // 补齐创建人/新增人姓名（部分创建人不在当前团队成员列表中）
  void prefetchUserNamesFromPatients(next).catch(() => {});
  pages.value = Number(payload.pages || 0) || 0;
  totalFromApi.value = Number(payload.total || 0) || rawPatients.value.length;
  managedArchiveCountAllTeams.value =
    Number(
      payload.totalAllTeams ||
      payload.totalAllTeam ||
      payload.totalAllTeamsCount ||
      managedArchiveCountAllTeams.value ||
      totalFromApi.value ||
      0
    ) || (totalFromApi.value || 0);

  return true;
}

const handlePatientClick = (patient) => {
  if (isBatchMode.value) {
    toggleSelect(patient);
    return;
  }

  const id = patient._id || patient.id || patient.mobile || patient.phone || '';
  uni.setStorageSync(DETAIL_STORAGE_KEY, {
    _id: id,
    name: patient.name,
    sex: patient.gender,
    age: patient.age,
    mobile: patient.mobile,
    mobiles: Array.isArray(patient.mobiles) ? patient.mobiles : (patient.mobile ? [patient.mobile] : []),
    outpatientNo: patient.outpatientNo,
    inpatientNo: patient.inpatientNo,
    medicalRecordNo: patient.medicalRecordNo,
    createTime: patient.createTime,
    creator: patient.creator,
    createdByDoctor: patient.createdByDoctor,
    hasBindWechat: patient.hasBindWechat
  });
  uni.navigateTo({ url: `/pages/case/archive-detail?id=${encodeURIComponent(String(id))}` });
};

// Computed
const patientList = computed(() => {
  const all = rawPatients.value || [];

  // New Patient Filter (Last 7 days)
  if (currentTab.value.kind === 'new') {
    const sevenDaysAgo = dayjs().subtract(7, 'day').startOf('day').valueOf();
    const flatList = all
      .filter((p) => Number(p?.recentAddTimeTs || p?.createTimeTs || 0) >= sevenDaysAgo)
      .slice()
      .sort(
        (a, b) =>
          Number(b?.recentAddTimeTs || b?.createTimeTs || 0) -
          Number(a?.recentAddTimeTs || a?.createTimeTs || 0)
      );
    return [{ letter: '最近7天新增', data: flatList }];
  }

  return groupByLetter(all);
});

const indexList = computed(() => {
  if (currentTab.value.kind === 'new') return []; // No index bar for new patient
  const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ#'.split('').filter(l => patientList.value.some(g => g.letter === l));
  return letters;
});

const totalPatients = computed(() => {
  let count = 0;
  patientList.value.forEach(g => count += g.data.length);
  if (currentTab.value.kind === 'new') return count;
  return totalFromApi.value || count;
});

// Methods
const checkBatchMode = () => {
  if (isBatchMode.value) {
    uni.showToast({ title: '请先完成当前批量设置或点击底部“取消”按钮退出', icon: 'none' });
    return true;
  }
  return false;
};

const scrollToLetter = (letter) => {
  if (currentTab.value.kind === 'new') return;
  scrollIntoId.value = letterToDomId(letter);
};

async function applyTeamSelection(nextTeam) {
  if (!nextTeam) return;
  const nextId = String(nextTeam?.teamId || '');
  if (nextId && nextId === getTeamId()) return;

  suppressTabReload.value = true;
  try {
    currentTeam.value = nextTeam;
    if (currentTeam.value) uni.setStorageSync(CURRENT_TEAM_STORAGE_KEY, currentTeam.value);
    currentTabKey.value = 'all';

    await loadGroups();
    await loadTeamMembers();
    await reload(true);
  } finally {
    suppressTabReload.value = false;
  }
}

async function onTeamPickerChange(e) {
  const prev = teamPickerIndex.value;
  if (checkBatchMode()) {
    teamPickerIndex.value = prev;
    return;
  }
  const idx = Number(e?.detail?.value ?? prev);
  if (!Number.isFinite(idx)) return;
  teamPickerIndex.value = idx;
  await applyTeamSelection((Array.isArray(teams.value) ? teams.value : [])[idx]);
}

const toggleTeamPopup = () => {
  if (checkBatchMode()) return;
  if (useTeamPicker.value) return;
  if (!teams.value.length) {
    toast('暂无可选团队');
    return;
  }
  uni.showActionSheet({
    itemList: teams.value.map((i) => i.name),
    success: async (res) => {
      await applyTeamSelection(teams.value[res.tapIndex] || teams.value[0] || null);
    }
  });
};

const goToSearch = () => {
  if (checkBatchMode()) return;
  uni.navigateTo({
    url: '/pages/case/search'
  });
};

const goToGroupManage = withInfo(() => {
  if (checkBatchMode()) return;
  uni.navigateTo({
    url: '/pages/case/group-manage'
  });
});

const toggleBatchMode = withInfo(() => {
  if (isBatchMode.value) {
    // Already in batch mode, do nothing or prompt?
    // Prompt says "click Other operations... prompt please finish".
    // Clicking "Batch" itself while in batch mode: user usually expects toggle off or nothing.
    // Based on "click Cancel button to exit", I'll assume clicking existing batch button doesn't exit.
    return;
  }
  isBatchMode.value = true;
  selectedItems.value = [];
});

const handleCreate = withInfo(async () => {
  if (checkBatchMode()) return;

  // 规则沿用原前端逻辑：
  // - 认证中按未认证控制（上限10，且直接toast阻止）
  // - 未认证：达到10提示去认证
  // - 已认证：达到100提示联系客服
  // 只是把“在管档案数”来源改为新接口（医生创建的所有团队汇总）。
  let total = null;
  let limit = null;
  let unlimited = false;
  try {
    const corpId = getCorpId();
    const userId = getUserId();
    if (corpId && userId) {
      const res = await api('doctorCreatedTeamsCustomerLimitation', { corpId, userId }, false);
      if (res?.success) {
        const count = Number(res?.count ?? res?.data?.count);
        if (Number.isFinite(count)) total = count;

        const rawLimit = Number(res?.limit ?? res?.data?.limit);
        if (Number.isFinite(rawLimit)) {
          limit = rawLimit;
          unlimited = rawLimit === -1;
        }
      }
    }
  } catch {
    // ignore
  }
  if (!Number.isFinite(total)) {
    total = Number(managedArchiveCountAllTeams.value || 0) || 0;
  }

  // 接口异常兜底：仍按旧前端默认规则（未认证10/已认证100）
  if (!Number.isFinite(limit)) {
    limit = isVerified.value ? 100 : 10;
    unlimited = false;
  }

  // 无限：直接放行
  if (unlimited) {
    uni.showActionSheet({
      itemList: ['邀请患者建档', '我帮患者建档'],
      success: (res) => {
        if (res.tapIndex === 0) {
          openInvitePatientEntry();
        } else if (res.tapIndex === 1) {
          openCreatePatientEntry();
        }
      }
    });
    return;
  }

  const limitText = Number.isFinite(limit) ? String(limit) : '';

  // 已认证：达到上限 -> 引导联系客服（预留入口）
  if (isVerified.value && total >= limit) {
    uni.showModal({
      title: '提示',
      content: limitText
        ? `当前管理档案数已达上限 ${limitText} 个，无法继续新增。如需提升档案管理数，请联系客服处理。`
        : '当前管理档案数已达上限，无法继续新增。如需提升档案管理数，请联系客服处理。',
      cancelText: '知道了',
      confirmText: '添加客服',
      success: (res) => {
        if (res.confirm) {
          openAddCustomerServiceEntry();
        }
      }
    });
    return;
  }

  // 未认证/认证中：达到上限 -> 提示去认证（认证中直接toast阻止）
  if (!isVerified.value && total >= limit) {
    if (verifyStatus.value === 'verifying') {
      toast('信息认证中，请耐心等待！');
      return;
    }
    uni.showModal({
      title: '提示',
      content: limitText
        ? `当前管理档案数已达上限 ${limitText} 个，完成认证可提升档案管理上限。`
        : '当前管理档案数已达上限，完成认证可提升档案管理上限。',
      cancelText: '暂不认证',
      confirmText: '去认证',
      success: (res) => {
        if (res.confirm) {
          startVerifyFlow();
        }
      }
    });
    return;
  }

  // 未达上限：显示新增入口
  uni.showActionSheet({
    itemList: ['邀请患者建档', '我帮患者建档'],
    success: (res) => {
      if (res.tapIndex === 0) {
        openInvitePatientEntry();
      } else if (res.tapIndex === 1) {
        openCreatePatientEntry();
      }
    }
  });
});

// 新增流程：认证分支
const startVerifyFlow = () => {
  if (verifyStatus.value === 'verifying') {
    toast('信息认证中，请耐心等待！');
    return;
  }
  // 有历史失败记录 -> 展示失败原因 & 重新认证
  if (hasVerifyFailedHistory.value) {
    uni.showModal({
      title: '提示',
      content: `您有历史认证未通过记录。失败原因为：\n\n${verifyFailedReason.value}`,
      cancelText: '取消',
      confirmText: '重新认证',
      success: (res) => {
        if (res.confirm) {
          openVerifyEntry();
        }
      }
    });
    return;
  }

  // 正常去认证
  openVerifyEntry();
};

// ===== 预留入口（后续对接真实页面/接口） =====
const openVerifyEntry = () => {
  uni.navigateTo({ url: '/pages/work/profile?type=cert' });
};

const openAddCustomerServiceEntry = () => {
  uni.navigateTo({ url: '/pages/work/service/contact-service' });
};

const openInvitePatientEntry = () => {
  uni.navigateTo({ url: '/pages/work/team/invite/invite-patient' });
};

const openCreatePatientEntry = () => {
  uni.navigateTo({ url: '/pages/case/patient-create' });
};

// Batch Operations
const toggleSelect = (patient) => {
  if (!isBatchMode.value) return; // Should not happen if click handler is correct

  const id = patient._id || patient.id || patient.phone || patient.mobile; // Prefer server id
  const index = selectedItems.value.indexOf(id);
  if (index > -1) {
    selectedItems.value.splice(index, 1);
  } else {
    selectedItems.value.push(id);
  }
};

const handleSelectAll = () => {
  // Flatten current list
  const currentList = patientList.value.flatMap(group => group.data);
  if (selectedItems.value.length === currentList.length) {
    selectedItems.value = []; // Unselect All
  } else {
    selectedItems.value = currentList.map(p => p._id || p.id || p.phone || p.mobile);
  }
};

const cancelBatch = () => {
  isBatchMode.value = false;
  selectedItems.value = [];
};

function showActionSheet(itemList = [], title = '') {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(itemList) || itemList.length === 0) {
      reject(new Error('empty'));
      return;
    }
    uni.showActionSheet({
      title,
      itemList,
      success: ({ tapIndex }) => resolve(tapIndex),
      fail: () => reject(new Error('cancel')),
    });
  });
}

async function fetchTeamMembersByTeamId(teamId) {
  const corpId = getCorpId();
  if (!corpId || !teamId) return [];
  const res = await api('getTeamData', { corpId, teamId });
  if (!res?.success) return [];
  const t = res?.data && typeof res.data === 'object' ? res.data : {};
  return Array.isArray(t.memberList) ? t.memberList : [];
}

async function transferToCustomerPool(customerIds) {
  try {
    await uniConfirm('客户将与本团队解除服务关系，本团队成员将没有权限查询到客户档案。');
  } catch (e) {
    return;
  }

  const corpId = getCorpId();
  const currentTeamId = getTeamId();
  const creatorUserId = getUserId();
  if (!corpId || !currentTeamId || !creatorUserId) return toast('缺少用户/团队信息');

  showLoading('保存中...');
  try {
    const res = await api('transferCustomers', {
      corpId,
      customerIds,
      currentTeamId,
      operationType: 'transferToCustomerPool',
      creatorUserId,
    });
    if (!res?.success) {
      toast(res?.message || '操作失败');
      return;
    }
    toast('操作成功');
    cancelBatch();
  } catch (e) {
    toast('操作失败');
  } finally {
    hideLoading();
  }
}

async function transferToOtherTeam(customerIds) {
  const corpId = getCorpId();
  const currentTeamId = getTeamId();
  const creatorUserId = getUserId();
  if (!corpId || !currentTeamId || !creatorUserId) return toast('缺少用户/团队信息');

  if (!teams.value.length) await loadTeams();
  const candidates = teams.value.filter((t) => String(t?.teamId || '') !== String(currentTeamId));
  if (!candidates.length) return toast('暂无可选团队');

  let teamIndex;
  try {
    teamIndex = await showActionSheet(candidates.map((t) => t.name || ''), '选择新负责团队');
  } catch (e) {
    return;
  }
  const pickedTeam = candidates[teamIndex];
  if (!pickedTeam?.teamId) return toast('团队信息异常');

  let members = [];
  try {
    members = await fetchTeamMembersByTeamId(pickedTeam.teamId);
  } catch (e) {
    members = [];
  }
  if (!members.length) return toast('当前团队暂无可选成员');

  const labels = members.map((m) => String(m?.anotherName || m?.name || m?.userid || ''));
  let userIndex;
  try {
    userIndex = await showActionSheet(labels, '选择责任人');
  } catch (e) {
    return;
  }
  const pickedUser = members[userIndex] || null;
  const targetUserId = String(pickedUser?.userid || '') || '';
  if (!targetUserId) return toast('责任人信息异常');

  showLoading('保存中...');
  try {
    const res = await api('transferCustomers', {
      corpId,
      customerIds,
      currentTeamId,
      targetTeamId: String(pickedTeam.teamId),
      targetUserId,
      operationType: 'transferToOtherTeam',
      creatorUserId,
    });
    if (!res?.success) {
      toast(res?.message || '操作失败');
      return;
    }
    toast('操作成功');
    cancelBatch();
  } catch (e) {
    toast('操作失败');
  } finally {
    hideLoading();
  }
}

const handleTransfer = async () => {
  if (selectedItems.value.length === 0) {
    uni.showToast({ title: '请选择患者', icon: 'none' });
    return;
  }

  let tapIndex;
  try {
    tapIndex = await showActionSheet(['转移给其他团队', '转移至客户公共池']);
  } catch (e) {
    return;
  }

  const customerIds = selectedItems.value.slice().map(String).filter(Boolean);
  if (!customerIds.length) return toast('请选择患者');

  if (tapIndex === 0) {
    await transferToOtherTeam(customerIds);
  } else if (tapIndex === 1) {
    await transferToCustomerPool(customerIds);
  }
};

const handleShare = () => {
  if (selectedItems.value.length === 0) {
    uni.showToast({ title: '请选择患者', icon: 'none' });
    return;
  }
  uni.setStorageSync(BATCH_CUSTOMER_IDS_KEY, selectedItems.value.slice());
  // Navigate to Share Page
  uni.navigateTo({ url: '/pages/case/batch-share' });
};

async function loadMore() {
  if (!more.value || loading.value) return;
  const nextPage = page.value + 1;
  page.value = nextPage;
  const ok = await reload(false, { silent: true });
  if (!ok) page.value = nextPage - 1;
}

watch(currentTeam, (t) => {
  if (!t) return;
  uni.setStorageSync(CURRENT_TEAM_STORAGE_KEY, t);
});

watch(currentTabKey, () => {
  if (!currentTeam.value) return;
  if (suppressTabReload.value) return;
  loadTeamMembers();
  reload(true);
});

function onTabClick(tab) {
  if (checkBatchMode()) return;
  if (!tab || !tab.key) return;
  if (currentTabKey.value === tab.key) return;

  if (tab.kind === 'group' && !ensureUserInfoForFeature()) return;
  currentTabKey.value = tab.key;
}

async function refreshOnEnter() {
  if (enterRefreshInflight) return enterRefreshInflight;

  enterRefreshInflight = (async () => {
    const silent = hasEnteredOnce.value;
    let didLoadSomething = false;

    // 低网/离线时尽量用缓存的当前团队兜底
    if (!currentTeam.value) {
      const saved = uni.getStorageSync(CURRENT_TEAM_STORAGE_KEY);
      if (saved && saved.teamId) currentTeam.value = saved;
    }

    await loadTeams({ silent });
    if (lastTeamsLoadOk.value) didLoadSomething = true;
    if (currentTeam.value) {
      await loadGroups();
      await loadTeamMembers();
      const ok = await reload(true, { silent, keepOnFail: true });
      if (ok) didLoadSomething = true;
    }
    await refreshVerifyStatus();
    if (didLoadSomething) hasEnteredOnce.value = true;
  })().finally(() => {
    enterRefreshInflight = null;
  });

  return enterRefreshInflight;
}

onLoad(async () => {
  await refreshOnEnter();
});

onShow(async () => {
  const showAt = Date.now();
  const wasEnteredOnce = hasEnteredOnce.value;
  const need = uni.getStorageSync(NEED_RELOAD_STORAGE_KEY);
  if (need) {
    uni.removeStorageSync(NEED_RELOAD_STORAGE_KEY);
    // 批量操作完成后回到列表，默认退出批量态
    isBatchMode.value = false;
    selectedItems.value = [];
  }

  const needGroups = uni.getStorageSync(GROUPS_RELOAD_KEY);
  if (needGroups) uni.removeStorageSync(GROUPS_RELOAD_KEY);

  await refreshOnEnter();

  // 避免“复用旧的 inflight 请求”导致回到本页仍然展示旧数据：
  // 只有在非首次进入且本次 onShow 没有发起新的 reload 时，才补一次 reload。
  if (wasEnteredOnce && lastReloadStartedAt && lastReloadStartedAt < showAt) {
    await reload(true, { silent: true, keepOnFail: true });
  }
});

</script>

<style lang="scss" scoped>
.container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background-color: #fff;
  padding-bottom: 0; // Default

  // Padding for batch footer
  /* &.is-batch {
    padding-bottom: 100rpx; 
  } */
  // We can't use &.is-batch because scoped style and root element is tricky depending on uni-app version/style
  // Instead we handle it in content-body or separate view
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20rpx 30rpx;
  background-color: #fff;
  border-bottom: 2rpx solid #f0f0f0;

  .team-selector {
    display: flex;
    align-items: center;
    font-size: 32rpx;
    font-weight: bold;
    color: #333;

    .team-name {
      margin-right: 10rpx;
    }

    .team-icon {
      font-size: 32rpx;
      color: #333;
    }
  }

  .header-actions {
    display: flex;
    gap: 30rpx;

    .action-item {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;

      .action-text {
        font-size: 20rpx;
        color: #333;
        margin-top: 4rpx;
      }
    }
  }
}

.tabs-area {
  display: flex;
  align-items: center;
  background-color: #f5f7fa;
  border-bottom: 2rpx solid #eee;
  padding-right: 30rpx; // Padding for the count

  .tabs-scroll {
    flex: 1;
    white-space: nowrap;
    overflow: hidden;

    .tabs-container {
      display: flex;
      padding: 20rpx 30rpx;

      .tab-item {
        padding: 10rpx 30rpx;
        margin-right: 20rpx;
        font-size: 28rpx;
        color: #666;
        background-color: #fff;
        border-radius: 8rpx;
        flex-shrink: 0;

        &.active {
          color: #5d8aff;
          background-color: #e6f0ff;
          font-weight: bold;
        }
      }
    }
  }

  .total-count-inline {
    font-size: 24rpx;
    color: #666;
    white-space: nowrap;
    flex-shrink: 0;
    min-width: 70rpx;
    text-align: right;
  }
}

.content-body {
  flex: 1;
  display: flex;
  position: relative;
  overflow: hidden;
}

.patient-list {
  flex: 1;
  height: 100%;
  background-color: #f7f8fa;
}

.group-title {
  padding: 10rpx 30rpx;
  font-size: 28rpx;
  color: #333;
}

.patient-card {
  display: flex;
  background-color: #fff;
  padding: 30rpx;
  margin-bottom: 2rpx; // Separator line
  border-bottom: 2rpx solid #f0f0f0;

  .checkbox-area {
    display: flex;
    align-items: center;
    margin-right: 20rpx;
  }

  .card-content {
    flex: 1;
  }

  .card-row-top {
    display: flex;
    align-items: center;
    margin-bottom: 16rpx;
    flex-wrap: wrap;

    .patient-info {
      display: flex;
      align-items: flex-end;
      margin-right: 20rpx;

      .patient-name {
        font-size: 32rpx;
        font-weight: bold;
        color: #333;
        margin-right: 16rpx;
      }

      .patient-meta {
        font-size: 24rpx;
        color: #999;
        margin-bottom: 4rpx;
      }
    }

    .patient-tags {
      display: flex;
      gap: 10rpx;

      .tag {
        font-size: 20rpx;
        color: #5d8aff;
        border: 2rpx solid #5d8aff;
        padding: 0 8rpx;
        border-radius: 16rpx;
        height: 32rpx;
        line-height: 28rpx;
      }
    }
  }

  .card-row-middle {
    font-size: 28rpx;
    margin-bottom: 12rpx;

    .record-text {
      color: #666;
    }

    .record-ellipsis {
      display: block;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      max-width: 100%;
    }

    .no-record {
      color: #bdc3c7;
    }
  }

  .card-row-bottom {
    font-size: 28rpx;

    .record-text {
      color: #666;
    }

    .record-ellipsis {
      display: block;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
      max-width: 100%;
    }

    .no-record {
      color: #bdc3c7;
    }
  }
}

.batch-footer {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 100rpx;
  background-color: #fff;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 30rpx;
  box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
  z-index: 99;

  .left-action {
    display: flex;
    align-items: center;

    .footer-text {
      margin-left: 10rpx;
      font-size: 28rpx;
      color: #333;
    }
  }

  .right-actions {
    display: flex;
    gap: 20rpx;

    .footer-btn {
      font-size: 28rpx;
      padding: 0 30rpx;
      height: 64rpx;
      line-height: 64rpx;
      margin: 0;
      border-radius: 8rpx;

      &.plain {
        border: 2rpx solid #ddd;
        background-color: #fff;
        color: #666;
      }

      &.primary {
        background-color: #5d8aff;
        color: #fff;
        border: none;
      }

      &::after {
        border: none;
      }
    }
  }
}

.sidebar-index {
  width: 40rpx;
  position: absolute;
  right: 0;
  top: 40rpx;
  bottom: 40rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: transparent;
  z-index: 10;

  .index-item {
    font-size: 20rpx;
    color: #555;
    padding: 4rpx 0;
    width: 40rpx;
    text-align: center;
    font-weight: 500;
  }
}
</style>
