<template>
  <!-- 根容器：使用 Flex 布局实现全屏高度 -->
  <view class="chat-container">

    <!-- ================= 顶部导航栏区域 ================= -->
    <!-- 采用了毛玻璃效果 (backdrop-filter) 增加现代感 -->
    <view class="chat-header">
      <!-- 头部主体内容：医生信息与展开按钮 -->
      <view class="header-main" @click="toggleDoctorDetail">
        <view class="doctor-info-group">
          <text class="doctor-name">{{ doctorUserInfo.nick_name }}</text>
          <view class="doctor-badges">
            <!-- 在线状态指示点 -->
            <text class="status-dot"></text>
            <text class="sub-text">在线 · 心血管内科</text>
          </view>
        </view>
        <!-- 右侧折叠/展开指示器 -->
        <view class="header-action">
          <text class="expand-tip">{{ showDoctorDetail ? '收起' : '详情' }}</text>
          <u-icon :name="showDoctorDetail ? 'arrow-up' : 'arrow-down'" size="14" color="#999"></u-icon>
        </view>
      </view>

      <!-- 医生详情下拉面板：利用 max-height 实现平滑过渡动画 -->
      <view class="doctor-detail-panel" :class="{ 'panel-show': showDoctorDetail }">
        <view class="detail-grid">
          <view class="detail-row">
            <text class="label">职称</text>
            <text class="value">主任医师</text>
          </view>
          <view class="detail-row">
            <text class="label">医院</text>
            <text class="value">北京协和医院</text>
          </view>
          <view class="detail-row full-width">
            <text class="label">擅长</text>
            <text class="value">冠心病、高血压、心力衰竭、心律失常的诊断与药物治疗</text>
          </view>
        </view>
      </view>
    </view>

    <!-- ================= 聊天消息滚动区域 ================= -->
    <!-- 修复: 增加 :scroll-into-view 绑定，这是更稳定的自动滚动方案 -->
    <scroll-view
        class="chat-messages"
        :style="scrollViewStyle"
        scroll-y="true"
        :scroll-into-view="scrollIntoView"
        :scroll-top="scrollTop"
        scroll-with-animation
        @scrolltoupper="loadHistory"
        @scroll="onScroll"
    >
      <view class="message-list-container" id="msglist">
        <!-- 加载指示器 (移到顶部，避免滚动到底部时显示在底部) -->
        <view v-if="loading" class="loading-spinner">
          <view class="spinner"></view>
          <text style="font-size: 24rpx; color: #999; margin-left: 16rpx;">加载历史消息...</text>
        </view>
        
        <!-- 无更多消息提示 -->
        <view v-if="isEmpty" class="empty-state">
          <text>没有更多历史消息了</text>
        </view>

        <!-- 消息列表循环 -->
        <view v-for="(msg, index) in messages" :key="index" class="message-wrapper">

          <!-- >>>>> 情况 A: 对方发送的消息 (医生) <<<<< -->
          <view v-if="getSenderIdWithoutPrefix(msg.sender_user_id) !== getCurrentUserIdWithoutPrefix()" class="chat-row other">
            <!-- 医生头像 -->
            <image class="avatar" :src="doctorAvatar" mode="aspectFill"></image>

            <view class="bubble-container">
              <!-- 消息气泡主体 -->
              <!-- 修复: UniApp 小程序编译不支持在 :class 中直接调用 methods 方法，改为内联字符串拼接 -->
              <view class="message-bubble" :class="'type-' + msg.message_type">

                <!-- 类型1: 图片消息 -->
                <image
                    v-if="msg.message_type === 1"
                    :src="getParsedContent(msg.message_type, msg.message_content)"
                    mode="widthFix"
                    class="media-content image"
                    @click="previewImage(getParsedContent(msg.message_type, msg.message_content))"
                ></image>

                <!-- 类型3: 视频消息 -->
                <video
                    v-else-if="msg.message_type === 3"
                    :src="getParsedContent(msg.message_type, msg.message_content)"
                    controls
                    class="media-content video"
                ></video>

                <!-- 类型2: 语音消息 -->
                <view v-else-if="msg.message_type === 2" class="audio-content" @click="playAudio(msg)">
                  <u-icon name="volume" size="18" color="#333"></u-icon>
                  <text class="duration">{{ msg.duration }}''</text>
                  <!-- 声波动画占位 -->
                  <view class="wifi-symbol" :class="{'active': isPlaying(msg)}">
                    <view class="wifi-circle first"></view>
                    <view class="wifi-circle second"></view>
                    <view class="wifi-circle third"></view>
                  </view>
                </view>

                <!-- 复杂卡片类消息容器 (挂号、处方、档案等) -->
                <view v-else-if="[4, 9, 10, 11, 12, 13].includes(msg.message_type)" class="card-bubble">

                  <!-- 类型10: 挂号信息卡片 -->
                  <block v-if="msg.message_type === 10">
                    <view class="card-header">
                      <view class="title-group">
                        <u-icon name="calendar-fill" size="18" color="#059669"></u-icon>
                        <text class="card-title">预约挂号</text>
                      </view>
                      <text class="status-tag" :class="'status-' + getParsedContent(msg.message_type, msg.message_content).status">
                        {{ getRegisterStatusText(getParsedContent(msg.message_type, msg.message_content).status) }}
                      </text>
                    </view>
                    <view class="card-body">
                      <view class="info-row">
                        <text class="label">订单号</text>
                        <text class="value">{{ getParsedContent(msg.message_type, msg.message_content).order_no }}</text>
                      </view>
                      <view class="info-row">
                        <text class="label">就诊人</text>
                        <!-- 修复: 将 ?. 语法改为 && 语法，解决小程序编译报错 -->
                        <text class="value">{{ (getParsedContent(msg.message_type, msg.message_content).user_patient && getParsedContent(msg.message_type, msg.message_content).user_patient.name) || '未知' }}</text>
                      </view>
                      <view class="info-row highlight">
                        <text class="label">费用</text>
                        <text class="value price">¥{{ getParsedContent(msg.message_type, msg.message_content).price }}</text>
                      </view>
                    </view>
                    <view class="card-footer">
                      <button class="action-btn" @click="viewRegister(getParsedContent(msg.message_type, msg.message_content).id)">查看详情</button>
                    </view>
                  </block>

                  <!-- 类型11: 患者就诊经历/档案卡片 (修复: 内容顺序与原始版本一致) -->
                  <block v-else-if="msg.message_type === 11">
                    <view class="card-header">
                      <view class="title-group">
                        <u-icon name="file-text-fill" size="18" color="#059669"></u-icon>
                        <text class="card-title">患者就诊经历</text>
                      </view>
                    </view>
                    <view class="card-body">
                      <!-- 1. 药品名称 (修正标签为'药品名称') -->
                      <view class="info-row" v-if="getParsedContent(msg.message_type, msg.message_content).drug_name">
                        <text class="label">药品名称</text>
                        <text class="value">{{ getParsedContent(msg.message_type, msg.message_content).drug_name }}</text>
                      </view>

                      <!-- 2. 症状描述 (移到第二位) -->
                      <view class="text-block" v-if="getParsedContent(msg.message_type, msg.message_content).illness_info">
                        <text class="block-label">症状描述</text>
                        <text class="block-content">{{ getParsedContent(msg.message_type, msg.message_content).illness_info }}</text>
                      </view>

                      <!-- 3. 是否就诊过 -->
                      <view class="info-row">
                        <text class="label">是否就诊过</text>
                        <text class="value">{{ getParsedContent(msg.message_type, msg.message_content).has_visited === '1' ? '是' : '否' }}</text>
                      </view>

                      <!-- 4. 是否使用过药品 -->
                      <view class="info-row">
                        <text class="label">是否使用过药品</text>
                        <text class="value">{{ getParsedContent(msg.message_type, msg.message_content).has_used_drug === '1' ? '是' : '否' }}</text>
                      </view>
                    </view>
                  </block>

                  <!-- 类型4: 电子处方卡片 -->
                  <block v-else-if="msg.message_type === 4">
                    <view class="card-header">
                      <view class="title-group">
                        <u-icon name="order" size="18" color="#298dff"></u-icon>
                        <text class="card-title">电子处方</text>
                      </view>
                      <text class="status-tag blue">
                        {{ getStatusText(getParsedContent(msg.message_type, msg.message_content).status) }}
                      </text>
                    </view>
                    <view class="card-body">
                      <view class="info-row">
                        <text class="label">诊断单号</text>
                        <text class="value small">{{ getParsedContent(msg.message_type, msg.message_content).order_no }}</text>
                      </view>
                      <view class="info-row">
                        <text class="label">金额</text>
                        <text class="value price">¥{{ getParsedContent(msg.message_type, msg.message_content).total_pay_price }}</text>
                      </view>
                    </view>
                    <view class="card-footer split">
                      <!-- 修正: 详情跳转传参增加 order_no -->
                      <text class="link-btn" @click="viewPrescription(getParsedContent(msg.message_type, msg.message_content).id, getParsedContent(msg.message_type, msg.message_content).order_no)">详情</text>
                      <!-- 修正: 购买传参改为 order_id -->
                      <button class="action-btn primary" @click="goToOrderMedicine(getParsedContent(msg.message_type, msg.message_content).order_id, getParsedContent(msg.message_type, msg.message_content).order_no)">一键购药</button>
                    </view>
                  </block>

                  <!-- 类型12: 商品卡片 -->
                  <block v-else-if="msg.message_type === 12">
                    <view class="card-header">
                      <view class="title-group">
                        <u-icon name="shopping-cart-fill" size="18" color="#2563eb"></u-icon>
                        <text class="card-title">推荐商品</text>
                      </view>
                    </view>
                    <view class="card-body">
                      <view class="info-row">
                        <text class="label">名称</text>
                        <text class="value">{{ getParsedContent(msg.message_type, msg.message_content).product_name }}</text>
                      </view>
                      <view class="info-row">
                        <text class="label">价格</text>
                        <text class="value price">¥{{ getParsedContent(msg.message_type, msg.message_content).price }}</text>
                      </view>
                    </view>
                  </block>

                  <!-- 类型9: 系统消息 - 价格变更通知 -->
                  <block v-else-if="msg.message_type === 9">
                    <view class="system-message-card" v-if="getParsedContent(msg.message_type, msg.message_content).type === 'price_update'">
                      <view class="price-update-notice">
                        <u-icon name="info-circle-fill" size="18" color="#fa8c16"></u-icon>
                        <text class="notice-text">{{ getParsedContent(msg.message_type, msg.message_content).message }}</text>
                      </view>
                      <view class="price-detail">
                        <text>订单号：{{ getParsedContent(msg.message_type, msg.message_content).order_no }}</text>
                        <text>应付金额：¥{{ getParsedContent(msg.message_type, msg.message_content).total_pay_price }}</text>
                      </view>
                    </view>
                    <view v-else class="system-message">
                      <text>{{ msg.message_content }}</text>
                    </view>
                  </block>

                  <!-- 类型13: 结束问诊卡片 -->
                  <block v-else-if="msg.message_type === 13">
                    <view class="card-header">
                      <view class="title-group">
                        <u-icon name="checkmark-circle-fill" size="18" color="#059669"></u-icon>
                        <text class="card-title">问诊结束</text>
                      </view>
                    </view>
                    <view class="card-body">
                      <view class="text-block">
                        <text class="block-content">{{ getParsedContent(msg.message_type, msg.message_content).reason || '本次服务已完成' }}</text>
                      </view>
                    </view>
                  </block>

                  <!-- 未知类型兜底 -->
                  <block v-else>
                    <view class="card-body">
                      <text>不支持的消息类型</text>
                    </view>
                  </block>
                </view>

                <!-- 类型0: 纯文本消息 -->
                <text v-else class="text-content">{{ getParsedContent(msg.message_type, msg.message_content) }}</text>
              </view>
            </view>
          </view>

          <!-- >>>>> 情况 B: 我发送的消息 (患者) <<<<< -->
          <view v-else-if="getSenderIdWithoutPrefix(msg.sender_user_id) === getCurrentUserIdWithoutPrefix()" class="chat-row mine">
            <!-- 修复: 调整DOM顺序，将头像放在气泡前，配合 row-reverse 实现头像在最右侧 -->
            <image class="avatar" :src="userAvatar" mode="aspectFill"></image>

            <view class="bubble-container">
              <!-- 修复: 同样将此处的 :class 函数调用改为内联拼接 -->
              <view class="message-bubble" :class="'type-' + msg.message_type">

                <!-- 图片 -->
                <image v-if="msg.message_type === 1" :src="getParsedContent(msg.message_type, msg.message_content)" mode="widthFix" class="media-content image" @click="previewImage(getParsedContent(msg.message_type, msg.message_content))"></image>

                <!-- 视频 -->
                <video v-else-if="msg.message_type === 3" :src="getParsedContent(msg.message_type, msg.message_content)" controls class="media-content video"></video>

                <!-- 语音 -->
                <view v-else-if="msg.message_type === 2" class="audio-content mine-audio" @click="playAudio(msg)">
                  <text class="duration">{{ msg.duration }}''</text>
                  <u-icon name="volume-fill" size="18" color="#fff"></u-icon>
                </view>

                <!-- 卡片: 我发送的卡片 -->
                <view v-else-if="[4, 9, 10, 11, 12, 13].includes(msg.message_type)" class="card-bubble">

                  <!-- 类型10: 挂号 -->
                  <block v-if="msg.message_type === 10">
                    <view class="card-header">
                      <view class="title-group">
                        <u-icon name="calendar-fill" size="18" color="#059669"></u-icon>
                        <text class="card-title">预约挂号</text>
                      </view>
                      <text class="status-tag" :class="'status-' + getParsedContent(msg.message_type, msg.message_content).status">
                        {{ getRegisterStatusText(getParsedContent(msg.message_type, msg.message_content).status) }}
                      </text>
                    </view>
                    <view class="card-body">
                      <view class="info-row">
                        <text class="label">订单号</text>
                        <text class="value">{{ getParsedContent(msg.message_type, msg.message_content).order_no }}</text>
                      </view>
                      <view class="info-row">
                        <text class="label">就诊人</text>
                        <text class="value">{{ (getParsedContent(msg.message_type, msg.message_content).user_patient && getParsedContent(msg.message_type, msg.message_content).user_patient.name) || '未知' }}</text>
                      </view>
                      <view class="info-row highlight">
                        <text class="label">费用</text>
                        <text class="value price">¥{{ getParsedContent(msg.message_type, msg.message_content).price }}</text>
                      </view>
                    </view>
                    <view class="card-footer">
                      <button class="action-btn" @click="viewRegister(getParsedContent(msg.message_type, msg.message_content).id)">查看详情</button>
                    </view>
                  </block>

                  <!-- 类型11: 患者就诊经历 (修复: 与上方完全一致) -->
                  <block v-else-if="msg.message_type === 11">
                    <view class="card-header">
                      <view class="title-group">
                        <u-icon name="file-text-fill" size="18" color="#059669"></u-icon>
                        <text class="card-title">患者就诊经历</text>
                      </view>
                    </view>
                    <view class="card-body">
                      <!-- 1. 药品名称 -->
                      <view class="info-row" v-if="getParsedContent(msg.message_type, msg.message_content).drug_name">
                        <text class="label">药品名称</text>
                        <text class="value">{{ getParsedContent(msg.message_type, msg.message_content).drug_name }}</text>
                      </view>

                      <!-- 2. 症状描述 -->
                      <view class="text-block" v-if="getParsedContent(msg.message_type, msg.message_content).illness_info">
                        <text class="block-label">症状描述</text>
                        <text class="block-content">{{ getParsedContent(msg.message_type, msg.message_content).illness_info }}</text>
                      </view>

                      <!-- 3. 是否就诊过 -->
                      <view class="info-row">
                        <text class="label">是否就诊过</text>
                        <text class="value">{{ getParsedContent(msg.message_type, msg.message_content).has_visited === '1' ? '是' : '否' }}</text>
                      </view>

                      <!-- 4. 是否使用过药品 -->
                      <view class="info-row">
                        <text class="label">是否使用过药品</text>
                        <text class="value">{{ getParsedContent(msg.message_type, msg.message_content).has_used_drug === '1' ? '是' : '否' }}</text>
                      </view>
                    </view>
                  </block>

                  <!-- 类型4: 电子处方 -->
                  <block v-else-if="msg.message_type === 4">
                    <view class="card-header">
                      <view class="title-group">
                        <u-icon name="order" size="18" color="#298dff"></u-icon>
                        <text class="card-title">电子处方</text>
                      </view>
                      <text class="status-tag blue">
                        {{ getStatusText(getParsedContent(msg.message_type, msg.message_content).status) }}
                      </text>
                    </view>
                    <view class="card-body">
                      <view class="info-row">
                        <text class="label">诊断单号</text>
                        <text class="value small">{{ getParsedContent(msg.message_type, msg.message_content).order_no }}</text>
                      </view>
                      <view class="info-row">
                        <text class="label">金额</text>
                        <text class="value price">¥{{ getParsedContent(msg.message_type, msg.message_content).total_pay_price }}</text>
                      </view>
                    </view>
                    <view class="card-footer split">
                      <text class="link-btn" @click="viewPrescription(getParsedContent(msg.message_type, msg.message_content).id, getParsedContent(msg.message_type, msg.message_content).order_no)">详情</text>
                      <button class="action-btn primary" @click="goToOrderMedicine(getParsedContent(msg.message_type, msg.message_content).order_id, getParsedContent(msg.message_type, msg.message_content).order_no)">一键购药</button>
                    </view>
                  </block>

                  <!-- 类型12: 商品 -->
                  <block v-else-if="msg.message_type === 12">
                    <view class="card-header">
                      <view class="title-group">
                        <u-icon name="shopping-cart-fill" size="18" color="#2563eb"></u-icon>
                        <text class="card-title">推荐商品</text>
                      </view>
                    </view>
                    <view class="card-body">
                      <view class="info-row">
                        <text class="label">名称</text>
                        <text class="value">{{ getParsedContent(msg.message_type, msg.message_content).product_name }}</text>
                      </view>
                      <view class="info-row">
                        <text class="label">价格</text>
                        <text class="value price">¥{{ getParsedContent(msg.message_type, msg.message_content).price }}</text>
                      </view>
                    </view>
                  </block>

                  <!-- 类型13: 结束问诊 -->
                  <block v-else-if="msg.message_type === 13">
                    <view class="card-header">
                      <view class="title-group">
                        <u-icon name="checkmark-circle-fill" size="18" color="#059669"></u-icon>
                        <text class="card-title">问诊结束</text>
                      </view>
                    </view>
                    <view class="card-body">
                      <view class="text-block">
                        <text class="block-content">{{ getParsedContent(msg.message_type, msg.message_content).reason || '本次服务已完成' }}</text>
                      </view>
                    </view>
                  </block>

                  <block v-else>
                    <view class="card-body"><text>不支持的消息类型</text></view>
                  </block>
                </view>

                <!-- 文本 -->
                <text v-else class="text-content">{{ getParsedContent(msg.message_type, msg.message_content) }}</text>
              </view>
            </view>
          </view>

          <!-- 时间分割线：智能判断是否显示 -->
          <view class="time-divider" v-if="shouldShowTime(msg, index)">
            {{ msg.created_at_text || msg.time }}
          </view>
        </view>

        <!-- 修复: 增加一个空view作为滚动锚点，用于scroll-into-view定位到最底部 -->
        <view id="chat-bottom-anchor" style="height: 1px; width: 100%;"></view>
      </view>

      <!-- 底部占位，防止被输入框遮挡 -->
      <!-- Fix 1: 增加底部占位高度至 320rpx，确保消息不被悬浮工具栏遮挡 -->
      <!-- Fix for blocking issue: Increased to 360rpx just to be super safe -->
      <view style="height: 360rpx;"></view>
    </scroll-view>

    <!-- ================= 底部输入与工具栏 (重构版) ================= -->
    <view class="chat-footer safe-area-inset-bottom">
      <!-- 问诊已结束提示 -->
      <view v-if="isConsultationEnded" class="consultation-ended-notice">
        <u-icon name="checkmark-circle-fill" size="20" color="#999"></u-icon>
        <text>本次问诊已结束</text>
      </view>
      
      <!-- 正常输入区域 -->
      <template v-else>
        <!-- 上半部分：快捷工具栏 -->
        <view class="toolbar-area">
          <view class="action-grid">
            <!-- 相册按钮 -->
            <view class="action-item" @click="openMediaPicker('image')">
              <view class="icon-circle"><u-icon name="photo-fill" size="20" color="#606266"></u-icon></view>
              <text>相册</text>
            </view>
  <!--          &lt;!&ndash; 拍照按钮 &ndash;&gt;-->
  <!--          <view class="action-item" @click="openCamera">-->
  <!--            <view class="icon-circle"><u-icon name="camera-fill" size="20" color="#606266"></u-icon></view>-->
  <!--            <text>拍摄</text>-->
  <!--          </view>-->
  <!--          &lt;!&ndash; 视频按钮 &ndash;&gt;-->
  <!--          <view class="action-item" @click="openMediaPicker('video')">-->
  <!--            <view class="icon-circle"><u-icon name="play-circle-fill" size="20" color="#606266"></u-icon></view>-->
  <!--            <text>视频</text>-->
  <!--          </view>-->
          </view>
        </view>

        <!-- 下半部分：输入框行 -->
        <view class="input-bar">
          <!-- 语音/键盘切换 -->
  <!--        <view class="voice-switch" @click="toggleVoiceInput">-->
  <!--          <u-icon :name="voiceInputActive ? 'keyboard' : 'mic'" size="28" color="#333"></u-icon>-->
  <!--        </view>-->

          <!-- 输入区域容器 -->
          <view class="input-field-wrapper">
            <!-- 文本输入框 -->
            <input
                v-if="!voiceInputActive"
                v-model="newMessage"
                class="main-input"
                placeholder="描述您的症状或问题..."
                confirm-type="send"
                @confirm="sendTextMessage"
                :adjust-position="false"
                cursor-spacing="20"
            />
            <!-- 语音按住按钮 -->
            <view v-else class="voice-btn"
                  :class="{ 'recording': recording }"
                  @touchstart="startVoiceRecording"
                  @touchend="stopVoiceRecording"
                  @touchcancel="stopVoiceRecording">
              <text>{{ recording ? '松开 发送' : '按住 说话' }}</text>
            </view>
          </view>

          <!-- 发送按钮 (仅在有内容或语音模式下显示) -->
          <view class="send-btn-wrapper" v-if="newMessage.trim() || voiceInputActive">
            <view class="send-btn" @click="sendTextMessage">
              <!-- 文本发送图标 -->
              <image src="/static/chat/send2.png" style="width: 30rpx; height: 30rpx;" />
  <!--            <uni-icons type="paperplane-filled"></uni-icons>-->
  <!--            <u-icon name="arrow-up" size="18" color="#fff" v-if="!voiceInputActive"></u-icon>-->
  <!--            &lt;!&ndash; 语音状态图标 &ndash;&gt;-->
  <!--            <view class="voice-wave" v-else>-->
  <!--              <u-icon name="mic-fill" size="16" color="#fff"></u-icon>-->
  <!--            </view>-->
            </view>
          </view>
        </view>
      </template>
    </view>

    <!-- ================= 录音全屏蒙层 ================= -->
    <view v-if="showRecordingModal" class="recording-overlay">
      <view class="recording-box">
        <view class="wave-animation"></view>
        <view style="margin: 20rpx 0;">
          <u-icon name="mic-fill" size="40" color="#fff"></u-icon>
        </view>
        <text>正在录音 {{ recordingTime }}s</text>
        <text class="tip">手指上滑取消</text>
      </view>
    </view>
  </view>
</template>

<script>
/**
 * 导入依赖
 * webSocketManager: WebSocket 管理类
 * ChatManager: 聊天状态管理 Store
 * API: 后端接口
 */
import { webSocketManager } from '@/utils/ws/websocket';
import { ChatManager } from '@/store/chat/chat.js';
import {getChatRegisterInfoApi, sendToUserApi, upLoadChatFileApi, getDoctorInfoApi, getRoomStatusApi} from "@/request/api/im";
import { checkDev } from '@/utils/utils';

// 根据环境获取上传URL
const isDev = checkDev('dev');
const uploadBaseUrl = isDev ? 'http://127.0.0.1:18001/api/mobile' : 'https://api.xiaokang88.com/api/mobile';

/**
 * 辅助函数：按需解析消息内容
 * 针对需要 JSON.parse 的消息类型进行处理
 */
function getParsedContent(messageType, messageContent) {
  // 需要解析的类型：处方(4), 挂号(10), 经历(11), 商品(12), 结束(13)
  if ([4, 9, 10, 11, 12, 13].includes(messageType)) {
    try {
      // 如果已经是对象则直接返回，否则解析字符串
      return typeof messageContent === 'object' ? messageContent : JSON.parse(messageContent);
    } catch (e) {
      console.error('解析消息内容失败:', e);
      return messageContent;
    }
  }
  return messageContent;
}

/**
 * 辅助函数：获取消息类型名称
 */
function getMessageTypeName(messageType) {
  const types = {
    0: 'text',
    1: 'image',
    2: 'audio',
    3: 'video',
    4: 'prescription',
    5: 'file',
    6: 'video-call',
    7: 'audio-call',
    8: 'file',
    9: 'system',
    10: 'register',
    11: 'patient-experience',
    12: 'product-card',
    13: 'end-consultation',
  };
  return types[messageType] || 'unknown';
}

export default {
  data() {
    return {
      // 医生基本信息
      doctorUserInfo: {
        id: 0,
        avatar: '',
        nick_name: '医生',
        room_id: ''
      },
      showDoctorDetail: false, // 控制顶部详情面板展开/收起
      currentUserId: '', // 当前用户ID
      doctorId: 'doctor-1', // 聊天对象ID

      // 默认头像占位
      doctorAvatar: 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk_upload_20250108/29bdb9560340373bb3096394a845afa5.jpg',
      userAvatar: 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk_upload_20250108/29bdb9560340373bb3096394a845afa5.jpg',

      // 消息列表与输入状态
      messages: [],
      newMessage: '', // 输入框内容

      // 语音与录音相关状态
      voiceInputActive: false, // 是否切换到语音输入模式
      recording: false, // 是否正在录音中
      recordingTime: 0, // 录音时长计时
      recordingTimer: null, // 计时器引用
      showRecordingModal: false, // 是否显示录音弹窗
      currentPlayingAudio: null, // 当前播放的音频ID
      hasRecordPermission: false, // 录音权限标记

      // 滚动相关
      scrollTop: 0, // 滚动条位置
      scrollIntoView: '', // 修复: 增加 scrollIntoView 变量
      scrollViewHeight: 0, // 滚动视图高度
      oldScrollViewHeight: 0, // 加载历史消息前的滚动高度
      scrollViewStyle: '', // 动态设置 scroll-view 高度样式
      currentScrollPosition: 0, // 仅用于内部记录位置，不绑定到 view

      // 分页相关
      page: 1,
      pageSize: 20,
      hasMore: true,
      loading: false,
      isEmpty: false,
      isLoadHistory: false, // 标记是否为触顶加载（而非初始化加载）

      // 预置数据结构 (用于发送)
      prescriptionData: {
        id: 159,
        order_no: "XY9478601753943762",
        status: 0,
        express_name: "李测试",
        total_pay_price: "11.94"
      },
      registerData: {},
      
      // 问诊是否已结束
      isConsultationEnded: false
    };
  },

  // 页面加载生命周期
  onLoad: function (option) {
    // 1. 初始化页面参数
    this.doctorUserInfo = option;
    if (option.avatar) {
      this.doctorAvatar = option.avatar;
    }

    // 2. 初始化用户ID (加前缀逻辑)
    this.currentUserId = 'user-' + uni.getStorageSync('user_id') || 'user-2512';

    // 3. 处理医生ID，移除可能存在的后缀
    let doctorId = option.user_id || '';
    if (doctorId.includes('-type-')) {
      doctorId = doctorId.split('-type-')[0];
    }
    this.doctorId = doctorId;

    // 4. 初始化聊天管理器
    if (typeof ChatManager !== 'undefined') {
      ChatManager.enterRoom(option.room_id);
      ChatManager.resetUnreadCount(option.room_id);
      // 获取缓存的房间消息
      this.messages = ChatManager.getRoomMessages(option.room_id);
      
      // 检查房间状态是否为结束问诊（优先使用 option 参数）
      if (option.room_status === '1' || option.room_status === 1) {
        this.isConsultationEnded = true;
      } else if (this.messages.some(msg => msg.message_type === 13)) {
        // 兼容：检查历史消息中是否有结束问诊消息
        this.isConsultationEnded = true;
      }
    }

    // 5. 从 API 获取最新房间状态（确保状态最新）
    if (option.room_id) {
      this.fetchRoomStatus(option.room_id);
    }

    // 6. 检查医生信息是否完整，如果不完整则从API获取
    const nickName = this.doctorUserInfo.nick_name;
    if (!nickName || nickName === 'undefined' || nickName === 'null') {
      this.fetchDoctorInfo(doctorId);
    } else {
      // 设置导航栏标题
      uni.setNavigationBarTitle({
        title: `与${nickName}医生的对话`,
        success: () => {}
      });
    }

    // 7. 初次渲染滚动到底部
    // Fix: 页面加载时延时触发滚动到锚点
    this.$nextTick(() => {
      this.scrollToBottom();
      setTimeout(() => {
        this.scrollToBottom();
      }, 500); // 增加延时以等待图片渲染
    });
  },

  onUnload() {
    // 离开页面时退出房间
    if (typeof ChatManager !== 'undefined') {
      ChatManager.leaveRoom();
    }
  },

  onShow() {
    // 注册全局事件监听
    uni.$on('new-room-message', this.handleNewRoomMessage);
    uni.$on('load-room-message', this.handLoadMessageList);
    uni.$on('no-more-history', this.handNoMessageList);
  },

  onHide() {
    // 移除全局事件监听
    uni.$off('new-room-message', this.handleNewRoomMessage);
    uni.$off('load-room-message', this.handLoadMessageList);
    uni.$off('no-more-history', this.handNoMessageList);
  },

  mounted() {
    // 初始化录音管理器
    this.recorderManager = uni.getRecorderManager();
    this.initRecorder();

    // 添加 WebSocket 消息处理器
    if (typeof webSocketManager !== 'undefined') {
      webSocketManager.addMessageHandler(this.handleSocketMessage);
    }

    // 计算并设置 scroll-view 的明确高度
    this.calculateScrollViewHeight();
  },

  beforeDestroy() {
    // 移除 WebSocket 消息处理器
    if (typeof webSocketManager !== 'undefined') {
      webSocketManager.removeMessageHandler(this.handleSocketMessage);
    }
  },

  methods: {
    /**
     * 获取医生信息
     * 当进入聊天页面时，如果医生信息不完整（undefined），则调用此方法获取
     */
    async fetchDoctorInfo(doctorId) {
      try {
        // 移除可能存在的前缀
        const cleanDoctorId = String(doctorId).replace('doctor-', '');
        const res = await getDoctorInfoApi({ doctor_id: cleanDoctorId });
        
        if (res.data && res.data.result) {
          const doctorInfo = res.data.result;
          // 更新医生信息
          this.doctorUserInfo = {
            ...this.doctorUserInfo,
            nick_name: doctorInfo.nick_name || '医生',
            avatar: doctorInfo.avatar || this.doctorAvatar,
            department: doctorInfo.department || '',
            title: doctorInfo.title || ''
          };
          
          // 更新医生头像
          if (doctorInfo.avatar) {
            this.doctorAvatar = doctorInfo.avatar;
          }
          
          // 更新导航栏标题
          uni.setNavigationBarTitle({
            title: `与${this.doctorUserInfo.nick_name}医生的对话`,
            success: () => {}
          });
        }
      } catch (error) {
        console.error('获取医生信息失败:', error);
        // 即使获取失败，也设置一个默认标题
        uni.setNavigationBarTitle({
          title: '与医生的对话'
        });
      }
    },

    /**
     * 获取房间状态
     * 判断问诊是否已结束
     */
    async fetchRoomStatus(roomId) {
      try {
        const res = await getRoomStatusApi({ room_id: roomId });
        if (res.data && res.data.result) {
          // 根据房间状态设置问诊结束状态（status=1结束，status=0正常）
          this.isConsultationEnded = res.data.result.status === 1;
        }
      } catch (error) {
        console.error('获取房间状态失败:', error);
      }
    },

    /**
     * 检查隐私授权
     * 微信小程序从2023年9月起要求在使用敏感API前获取用户隐私授权
     */
    checkPrivacyAuthorize() {
      return new Promise((resolve, reject) => {
        // #ifdef MP-WEIXIN
        if (typeof uni.requirePrivacyAuthorize === 'function') {
          uni.requirePrivacyAuthorize({
            success: () => resolve(),
            fail: () => {
              uni.showToast({ title: '需要同意隐私协议才能使用此功能', icon: 'none' });
              reject(new Error('隐私授权被拒绝'));
            }
          });
        } else {
          // 低版本基础库不需要隐私授权
          resolve();
        }
        // #endif
        // #ifndef MP-WEIXIN
        resolve();
        // #endif
      });
    },

    /**
     * 计算并设置 scroll-view 的明确高度
     * 根据 uni-app 文档，scroll-view 需要明确的高度才能正常滚动
     */
    calculateScrollViewHeight() {
      this.$nextTick(() => {
        const systemInfo = uni.getSystemInfoSync();
        const windowHeight = systemInfo.windowHeight; // 窗口高度（px）

        // 使用 uni.createSelectorQuery 获取头部和底部的高度
        const query = uni.createSelectorQuery().in(this);

        // 查询头部高度
        query.select('.chat-header').boundingClientRect((headerRect) => {
          const headerHeight = headerRect ? headerRect.height : 0;

          // 查询底部高度
          const query2 = uni.createSelectorQuery().in(this);
          query2.select('.chat-footer').boundingClientRect((footerRect) => {
            const footerHeight = footerRect ? footerRect.height : 0;

            // 计算 scroll-view 的可用高度
            const scrollHeight = windowHeight - headerHeight - footerHeight;

            // 设置样式（使用 px 单位，因为系统信息返回的是 px）
            this.scrollViewStyle = `height: ${scrollHeight}px;`;
          }).exec();
        }).exec();
      });
    },

    /**
     * 滚动事件监听
     * 记录当前滚动位置
     */
    onScroll(e) {
      // >>>>>>>>>> 修复的核心改动 >>>>>>>>>>
      // 移除: this.scrollTop = e.detail.scrollTop;
      // 原因: 实时将滚动位置赋值回绑定了 :scroll-top 的变量，会导致 scroll-view
      // 在用户触摸时不断尝试"矫正"位置，从而引发画面抽搐。
      // 只需记录位置供内部逻辑使用即可，不要去更新绑定的属性。

      this.currentScrollPosition = e.detail.scrollTop;
    },

    /**
     * 工具方法：移除 ID 前缀以便比较
     * e.g. "doctor-123" -> "123"
     */
    getSenderIdWithoutPrefix(senderId) {
      if (!senderId) return '';
      return String(senderId).replace(/^(user-|doctor-)/, '');
    },

    getCurrentUserIdWithoutPrefix() {
      if (!this.currentUserId) return '';
      return String(this.currentUserId).replace(/^(user-|doctor-)/, '');
    },

    // 暴露给模板使用
    getParsedContent(messageType, messageContent) {
      return getParsedContent(messageType, messageContent);
    },

    getMessageTypeName(messageType) {
      return getMessageTypeName(messageType);
    },

    /**
     * 获取CSS类名，用于区分消息类型样式
     */
    getMessageTypeClass(type) {
      return `type-${type}`;
    },

    /**
     * 判断是否显示时间分割线
     * 规则：第一条必显，后续如果与上一条间隔超过5分钟则显示
     */
    shouldShowTime(msg, index) {
      if (index === 0) return true;
      const prevMsg = this.messages[index - 1];
      // 5分钟 = 300,000 毫秒
      // 兼容 created_at 和 timestamp 字段
      const currTime = msg.created_at || msg.timestamp || 0;
      const prevTime = prevMsg.created_at || prevMsg.timestamp || 0;
      return currTime - prevTime > 300000;
    },

    /**
     * 批量加载挂号卡片信息
     * 遍历消息列表，找出需要加载的挂号卡片并请求后端
     */
    loadRegisterCards() {
      let that = this;
      this.messages.forEach((msg, index) => {
        if (msg.message_type === 10) {
          const parsedContent = getParsedContent(msg.message_type, msg.message_content);
          // 如果已加载或没有 id，跳过
          if (parsedContent._loaded || !parsedContent.id) {
            return;
          }
          // 调用 API 获取完整信息
          that.fetchRegisterCardInfo(msg, index, parsedContent.id);
        }
      });
    },
    
    /**
     * 获取单个挂号卡片的详细信息
     * @param {Object} msg - 消息对象
     * @param {Number} index - 消息在列表中的索引（仅作为备用）
     * @param {Number} registerId - 挂号 ID
     */
    fetchRegisterCardInfo(msg, index, registerId) {
      let that = this;
      // 保存消息 ID，可能是数字或字符串
      const messageId = msg.id;
      
      // 调试日志：确认 msg.id 的实际值
      console.log('=== fetchRegisterCardInfo ===');
      console.log('msg:', msg);
      console.log('msg.id:', msg.id, typeof msg.id);
      console.log('registerId:', registerId);
      
      getChatRegisterInfoApi({
        id: registerId,
        message_id: messageId || '' // 确保传递，即使为空
      }).then((res) => {
        if (res.data && res.data.result) {
          // 使用消息 ID 重新查找索引，而不是使用传入的 index
          // 原因：异步请求期间消息列表可能发生变化，index 可能已失效
          let targetIndex = index;
          if (messageId) {
            const foundIndex = that.messages.findIndex(m => 
              m.id === messageId || String(m.id) === String(messageId)
            );
            if (foundIndex !== -1) {
              targetIndex = foundIndex;
            }
          }
          
          // 添加 _loaded 标识，避免重复请求
          const resultWithFlag = {
            ...res.data.result,
            _loaded: true
          };
          
          // 使用 $set 确保响应式更新
          that.$set(that.messages, targetIndex, {
            ...that.messages[targetIndex],
            message_content: JSON.stringify(resultWithFlag)
          });
        }
      }).catch((err) => {
        console.error('获取挂号信息失败:', err);
      });
    },
    
    /**
     * 异步获取挂号详情并更新消息内容（兼容旧调用方式）
     * 后端会更新数据库中的 message_content，下次直接使用缓存
     */
    getChatRegisterInfo(item, index) {
      const parsedContent = getParsedContent(item.message_type, item.message_content);
      
      // 如果已经加载过完整信息，直接返回不再请求
      if (parsedContent._loaded) {
        return item;
      }
      
      // 检查是否有 id
      if (!parsedContent.id) {
        return item;
      }
      
      this.fetchRegisterCardInfo(item, index, parsedContent.id);
      return item;
    },

    /**
     * 历史消息加载完毕回调
     */
    handNoMessageList() {
      this.isEmpty = true;
      this.hasMore = false;
    },

    /**
     * 历史消息加载成功回调
     * type: 1=滚动到底部, 0=保持位置
     * 修复：确保触顶加载时 type 始终为 0，只在初始化时传 type=1
     */
    handLoadMessageList(type = 0) {
      this.loading = false;
      if (typeof ChatManager !== 'undefined') {
        this.messages = ChatManager.roomMessages[this.doctorUserInfo.room_id] || [];
      }
      
      // 加载完消息后，检查是否有需要动态获取的挂号卡片
      this.loadRegisterCards();
      
      // 修复：如果是触顶加载（isLoadHistory=true），强制 type 为 0，保持滚动位置
      // 只有在初始化加载时才滚动到底部（type=1 且 isLoadHistory=false）
      const isHistoryLoad = this.isLoadHistory;
      this.isLoadHistory = false; // 重置标记
      
      const shouldScrollToBottom = type === 1 && !isHistoryLoad;
      
      if (shouldScrollToBottom) {
        this.scrollToBottom();
      } else {
        // 触顶加载时保持位置
        this.scrollToOldLastMessage();
      }
    },

    /**
     * 获取当前消息列表的总高度
     * 用于保持加载历史消息后的滚动位置
     * 在加载历史消息前调用，记录当前高度
     */
    getScrollHeight() {
      const query = uni.createSelectorQuery().in(this);
      query.select('.message-list-container').boundingClientRect(res => {
        if (res) {
          // 记录加载历史消息前的高度
          this.oldScrollViewHeight = res.height;
          this.scrollViewHeight = res.height;
        }
      }).exec();
    },

    /**
     * WebSocket 消息处理
     * 直接处理当前房间消息，不走 ChatManager 避免重复
     */
    handleSocketMessage(data) {
      // 只处理当前房间的消息
      if (data.room_id === this.doctorUserInfo.room_id) {
        this.addMessage(data);
        this.scrollToBottom();
      }
    },

    /**
     * 监听新消息事件
     */
    handleNewRoomMessage(message) {
      this.addMessage(message);
      this.scrollToBottom();
    },

    /**
     * 添加消息到列表并去重
     * 修复：优先检查临时消息，放宽时间戳匹配条件，改进内容匹配逻辑（处理 JSON 格式差异）
     */
    addMessage(message) {
      // 数据标准化
      if (!message.message_content && message.content) {
        message.message_content = message.content;
      }
      // 类型标准化
      if (message.message_type === undefined && message.type !== undefined) {
        const messageTypeMap = {
          text: 0, image: 1, audio: 2, video: 3, prescription: 4, file: 5,
          register: 10, 'patient-experience': 11, 'product-card': 12, 'end-consultation': 13
        };
        message.message_type = messageTypeMap[message.type] || 0;
      }

      // 检查是否已存在相同 ID 的消息
      const existingIndexById = this.messages.findIndex(m => m.id === message.id);
      if (existingIndexById !== -1) {
        // 如果 ID 相同，更新现有消息（服务器返回的消息替换临时消息）
        this.messages[existingIndexById] = message;
        return;
      }

      // 辅助函数：标准化消息内容用于比较（处理 JSON 字符串和对象的差异）
      const normalizeContent = (content) => {
        if (!content) return '';
        if (typeof content === 'string') {
          try {
            // 尝试解析 JSON 字符串
            const parsed = JSON.parse(content);
            return JSON.stringify(parsed);
          } catch (e) {
            // 不是 JSON，直接返回
            return content;
          }
        } else if (typeof content === 'object') {
          return JSON.stringify(content);
        }
        return String(content);
      };

      // 优先检查临时消息（isTemporary: true）
      // 如果当前消息是服务器返回的（没有 isTemporary 标记），查找临时消息并替换
      if (!message.isTemporary) {
        const tempMessageIndex = this.messages.findIndex(m => {
          // 查找临时消息，且发送者相同
          if (!m.isTemporary) return false;
          const mSenderId = m.sender_user_id || '';
          const messageSenderId = message.sender_user_id || '';
          if (mSenderId !== messageSenderId) return false;

          // 比较消息内容（标准化后比较）
          const mContent = normalizeContent(m.message_content || m.content || '');
          const messageContent = normalizeContent(message.message_content || message.content || '');
          if (mContent !== messageContent) return false;

          // 时间戳匹配（放宽到30秒，处理网络延迟）
          const mTime = m.created_at || m.timestamp || 0;
          const messageTime = message.created_at || message.timestamp || 0;
          return Math.abs(mTime - messageTime) < 30000; // 30秒
        });

        if (tempMessageIndex !== -1) {
          // 找到临时消息，用服务器返回的消息替换
          this.messages[tempMessageIndex] = message;
          return;
        }
      }

      // 检查是否已存在相似消息（内容+发送者+时间戳匹配，允许30秒内的误差）
      // 用于处理乐观更新和服务器返回消息的重复问题
      const messageContent = normalizeContent(message.message_content || message.content || '');
      const senderId = message.sender_user_id || '';
      const messageTime = message.created_at || message.timestamp || 0;

      const existingIndexByContent = this.messages.findIndex(m => {
        const mContent = normalizeContent(m.message_content || m.content || '');
        const mSenderId = m.sender_user_id || '';
        const mTime = m.created_at || m.timestamp || 0;

        // 内容相同（标准化后）、发送者相同、时间差在30秒内
        return mContent === messageContent &&
            mSenderId === senderId &&
            Math.abs(mTime - messageTime) < 30000; // 放宽到30秒
      });

      if (existingIndexByContent !== -1) {
        // 如果找到相似消息，用服务器返回的消息替换临时消息（保留服务器 ID）
        this.messages[existingIndexByContent] = message;
        return;
      }

      // 新消息，添加到列表
      this.messages.push(message);
      
      // 如果是挂号卡片消息，检查是否需要加载完整信息
      if (message.message_type === 10) {
        const parsedContent = getParsedContent(message.message_type, message.message_content);
        if (!parsedContent._loaded && parsedContent.id) {
          const newIndex = this.messages.length - 1;
          this.fetchRegisterCardInfo(message, newIndex, parsedContent.id);
        }
      }
      
      // 检测结束问诊消息，立即设置问诊结束状态
      if (message.message_type === 13) {
        this.isConsultationEnded = true;
      }
      
      // 收到任何消息后，都从 API 确认房间状态
      if (this.doctorUserInfo.room_id) {
        this.fetchRoomStatus(this.doctorUserInfo.room_id);
      }
    },

    toggleDoctorDetail() {
      this.showDoctorDetail = !this.showDoctorDetail;
    },

    /**
     * 初始化录音功能
     */
    initRecorder() {
      // 监听录音结束事件
      this.recorderManager.onStop(res => {
        // 防止误触：录音时间小于1秒不发送
        if (res.duration < 1000) {
          uni.showToast({ title: '录音时间太短', icon: 'none' });
          this.recording = false;
          this.showRecordingModal = false;
          clearInterval(this.recordingTimer);
          return;
        }

        // 上传录音文件
        this.uploadAudioFile(res.tempFilePath, Math.floor(res.duration / 1000));
        this.recording = false;
        this.showRecordingModal = false;
        clearInterval(this.recordingTimer);
      });

      // 监听录音错误
      this.recorderManager.onError((error) => {
        console.error('录音错误:', error);
        uni.showToast({ title: '录音失败，请重试', icon: 'none' });
        this.recording = false;
        this.showRecordingModal = false;
        clearInterval(this.recordingTimer);
      });
    },

    /**
     * 上传音频文件
     */
    async uploadAudioFile(tempFilePath, duration) {
      uni.showLoading({ title: '上传中...', mask: true });
      try {
        uni.uploadFile({
          url: `${uploadBaseUrl}/chat-friends/upload-chat-file`,
          filePath: tempFilePath,
          name: 'file',
          header: {
            "authorization": uni.getStorageSync('token')
          },
          formData: {}, // uni-app中uploadFile formData可以是对象
          success: (uploadFileRes) => {
            // 发送音频消息
            this.sendMessage({
              type: 'audio',
              content: JSON.parse(uploadFileRes.data).result.url,
              duration,
            });
          },
          fail: (res) => {
            console.error('上传失败', res);
          }
        });
      } catch (error) {
        console.error('音频上传失败:', error);
        uni.showToast({ title: '音频上传失败', icon: 'none' });
      } finally {
        uni.hideLoading();
      }
    },

    /**
     * 核心发送消息逻辑
     */
    sendMessage(message) {
      const now = new Date();
      const timeStr = this.formatTime(now);

      // 处理接收者ID：移除 -type- 后缀，确保 doctor- 前缀
      let receiverUserId = this.doctorId || '';
      if (receiverUserId.includes('-type-')) {
        receiverUserId = receiverUserId.split('-type-')[0];
      }
      if (receiverUserId && !receiverUserId.startsWith('doctor-')) {
        receiverUserId = 'doctor-' + receiverUserId;
      }

      const messageTypeMap = {
        text: 0, image: 1, audio: 2, video: 3, prescription: 4, file: 5,
        register: 10, 'patient-experience': 11, 'product-card': 12, 'end-consultation': 13
      };
      const messageType = messageTypeMap[message.type] || 0;

      // 构造完整消息对象 (本地立即显示)
      // 修复：标记为临时消息，用于后续去重
      const fullMessage = {
        ...message,
        id: Date.now().toString(),
        sender_user_id: this.currentUserId,
        receiver_user_id: receiverUserId,
        room_id: this.doctorUserInfo.room_id,
        created_at_text: timeStr,
        created_at: now.getTime(),
        timestamp: now.getTime(),
        message_content: message.message_content || message.content || '',
        message_type: messageType,
        isTemporary: true // 标记为临时消息，服务器返回后会替换
      };

      // Fix: Optimistic UI update - Add message locally immediately
      this.addMessage(fullMessage);
      
      // 发送消息后立即滚动到底部
      this.scrollToBottom();

      // 注意：不调用 ChatManager.addMessageToRoom，避免触发 new-room-message 事件导致消息重复
      // 服务器返回的消息会通过 WebSocket 触发 handleNewRoomMessage，由去重逻辑处理

      // 构造请求数据 (发送给服务器)
      const requestData = {
        room_id: this.doctorUserInfo.room_id,
        sender_user_id: `user-${this.currentUserId}`,
        receiver_user_id: receiverUserId,
        message_type: messageType,
        message_content: message.message_content || message.content || '',
        isFromHttp: true,
        duration: message.duration || 0
      };

      if (typeof sendToUserApi !== 'undefined') {
        sendToUserApi(requestData).then(res => {
          // Success (already scrolled by addMessage)
        }).catch(error => {
          console.error('发送失败:', error);
        });
      }
    },

    formatTime(date) {
      const hours = date.getHours().toString().padStart(2, '0');
      const minutes = date.getMinutes().toString().padStart(2, '0');
      return `${hours}:${minutes}`;
    },

    sendTextMessage() {
      if (!this.newMessage.trim()) return;

      this.sendMessage({
        type: 'text',
        content: this.newMessage
      });

      this.newMessage = '';
    },

    async startVoiceRecording() {
      if (this.recording) return;

      // 先检查隐私授权
      try {
        await this.checkPrivacyAuthorize();
      } catch (e) {
        return; // 用户拒绝隐私授权
      }

      // 检查录音权限
      uni.getSetting({
        success: (res) => {
          if (res.authSetting['scope.record']) {
            this.hasRecordPermission = true;
            this.startRecording();
          } else {
            uni.authorize({
              scope: 'scope.record',
              success: () => {
                this.hasRecordPermission = true;
                this.startRecording();
              },
              fail: () => {
                this.hasRecordPermission = false;
                this.voiceInputActive = false;
              }
            });
          }
        }
      });
    },

    startRecording() {
      this.recording = true;
      this.showRecordingModal = true;
      this.recordingTime = 0;

      this.recorderManager.start({
        format: 'mp3',
        sampleRate: 44100,
        numberOfChannels: 1,
        encodeBitRate: 192000
      });

      this.recordingTimer = setInterval(() => {
        this.recordingTime++;
        // 限制最长录音60秒
        if (this.recordingTime >= 60) {
          this.stopVoiceRecording();
        }
      }, 1000);
    },

    stopVoiceRecording() {
      if (!this.recording) return;
      this.recorderManager.stop();
    },

    playAudio(msg) {
      // TODO: 实际音频播放逻辑，可使用 uni.createInnerAudioContext
      // 设置 this.currentPlayingAudio = msg.id 实现动画效果
    },

    isPlaying(msg) {
      return this.currentPlayingAudio === msg.id;
    },

    async openMediaPicker(type) {
      this.showMoreActions = false;
      
      // 先检查隐私授权
      try {
        await this.checkPrivacyAuthorize();
      } catch (e) {
        return; // 用户拒绝隐私授权
      }
      
      if (type === 'image') {
        uni.chooseImage({
          count: 9,
          sourceType: ['album', 'camera'],
          success: res => {
            res.tempFilePaths.forEach(file => {
              this.uploadAndSendFile(file, 'image');
            });
          },
          fail: (e) => { console.error('选择图片错误', e); },
        });
      } else if (type === 'video') {
        // 选择视频
        uni.chooseVideo({
          sourceType: ['album', 'camera'],
          maxDuration: 60, // 最长60秒
          camera: 'back',
          success: res => {
            this.uploadAndSendFile(res.tempFilePath, 'video');
          },
          fail: (e) => { console.error('选择视频错误', e); },
        });
      }
    },
    
    /**
     * 通用文件上传并发送方法
     */
    uploadAndSendFile(filePath, type) {
      uni.showLoading({ title: '上传中...', mask: true });
      uni.uploadFile({
        url: `${uploadBaseUrl}/chat-friends/upload-chat-file`,
        filePath: filePath,
        name: 'file',
        header: { "authorization": uni.getStorageSync('token') },
        formData: {},
        success: (uploadFileRes) => {
          uni.hideLoading();
          try {
            const result = JSON.parse(uploadFileRes.data);
            if (result.code === 0 && result.result && result.result.url) {
              this.sendMessage({
                type: type,
                content: result.result.url
              });
            } else {
              uni.showToast({ title: result.message || '上传失败', icon: 'none' });
            }
          } catch (e) {
            console.error('解析上传结果失败:', e);
            uni.showToast({ title: '上传失败', icon: 'none' });
          }
        },
        fail: (res) => {
          uni.hideLoading();
          console.error('上传失败', res);
          uni.showToast({ title: '上传失败', icon: 'none' });
        }
      });
    },

    async openCamera() {
      // 先检查隐私授权
      try {
        await this.checkPrivacyAuthorize();
      } catch (e) {
        return; // 用户拒绝隐私授权
      }
      
      uni.chooseImage({
        count: 1,
        sourceType: ['camera'],
        success: res => {
          // 使用通用上传方法上传拍摄的照片
          this.uploadAndSendFile(res.tempFilePaths[0], 'image');
        }
      });
    },

    previewImage(url) {
      uni.previewImage({
        urls: [url],
        current: url
      });
    },

    async toggleVoiceInput() {
      if (!this.voiceInputActive) {
        // 先检查隐私授权
        try {
          await this.checkPrivacyAuthorize();
        } catch (e) {
          return; // 用户拒绝隐私授权
        }
        
        // 切换到语音前检查并请求录音权限
        uni.getSetting({
          success: (res) => {
            if (res.authSetting['scope.record']) {
              // 已有权限，直接切换
              this.hasRecordPermission = true;
              this.voiceInputActive = true;
            } else {
              // 没有权限，请求授权（和图片一样的授权方式）
              uni.authorize({
                scope: 'scope.record',
                success: () => {
                  this.hasRecordPermission = true;
                  this.voiceInputActive = true;
                },
                fail: () => {
                  // 用户拒绝了授权，这时才需要引导去设置
                  this.hasRecordPermission = false;
                  uni.showModal({
                    title: '录音权限',
                    content: '需要录音权限才能使用语音输入，请在设置中开启',
                    showCancel: true,
                    cancelText: '取消',
                    confirmText: '去设置',
                    success: (modalRes) => {
                      if (modalRes.confirm) {
                        uni.openSetting();
                      }
                    }
                  });
                }
              });
            }
          }
        });
      } else {
        this.voiceInputActive = false;
      }
    },

    /**
     * 滚动到底部
     * 修复：优化滚动逻辑，确保 scroll-into-view 正确设置，增加延时确保 DOM 完全更新
     * 根据文档，scroll-into-view 优先级高于 scroll-top，需要避免同时设置
     */
    scrollToBottom() {
      // 先清空 scroll-into-view，避免与 scroll-top 冲突
      // 注意：不清空 scrollTop，避免重置滚动位置
      this.scrollIntoView = '';

      this.$nextTick(() => {
        // 等待 DOM 更新
        setTimeout(() => {
          // 设置 scroll-into-view 为底部锚点，触发滚动
          this.scrollIntoView = 'chat-bottom-anchor';
          
          // 如果第一次设置失败，再次尝试（确保滚动成功）
          setTimeout(() => {
            if (this.scrollIntoView !== 'chat-bottom-anchor') {
              this.scrollIntoView = 'chat-bottom-anchor';
            }
          }, 100);
        }, 300); // 增加延时，确保 DOM 完全更新和图片等资源加载完成
      });
    },

    /**
     * 加载历史消息后保持滚动位置
     * 修复：先清空 scroll-into-view 避免冲突，使用 uni.createSelectorQuery 准确计算高度差
     */
    scrollToOldLastMessage() {
      // 先清空 scroll-into-view，避免优先级冲突
      this.scrollIntoView = '';

      this.$nextTick(() => {
        // 使用 uni.createSelectorQuery 获取准确的滚动高度
        const query = uni.createSelectorQuery().in(this);
        query.select('.message-list-container').boundingClientRect((rect) => {
          if (rect) {
            const newHeight = rect.height;
            // 计算新旧高度差，设置 scroll-top
            const heightDiff = newHeight - this.oldScrollViewHeight;
            if (heightDiff > 0) {
              this.scrollTop = heightDiff;
              // 更新旧高度记录
              this.oldScrollViewHeight = newHeight;
            }
          }
        }).exec();
      });
    },

    async loadHistory() {
      if (!this.hasMore || this.loading) return;

      // 在加载历史消息前，记录当前滚动高度
      this.getScrollHeight();

      // 标记为触顶加载
      this.isLoadHistory = true;

      this.loading = true;
      try {
        if (typeof ChatManager !== 'undefined') {
          ChatManager.loadHistory(this.doctorUserInfo.room_id);
        }
      } finally {
        // loading状态通常在回调中设置为false
        // 这里只是兜底，实际应在 load-room-message 事件中处理
      }
    },

    // 状态文案辅助函数
    getStatusText(status) {
      const map = { 0: '待审核', 1: '已审核', 2: '未通过' };
      return map[status] || '未知状态';
    },
    getRegisterStatusText(status) {
      const map = { 0: '待就诊', 1: '已缴费', 2: '已就诊', 3: '已取消', 4: '已退费' };
      return map[status] || '未知状态';
    },
    getPayStatusText(isPay) { return isPay === 1 ? '已支付' : '未支付'; },
    getSexText(sex) { return sex === 1 ? '男' : '女'; },

    // 页面跳转函数
    viewPrescription(id, orderNo) {
      uni.navigateTo({
        url: "/subPackages/my/myrecord-detail?id=" + id + '&no=' + orderNo
      });
    },
    goToOrderMedicine(id, orderNo) {
      uni.navigateTo({
        url: "/subPackages/my/my-drug/drug-info?id=" + id + '&order_type=prescription'
      });
    },
    viewRegister(id, orderNo) {
      uni.navigateTo({
        url: "/subPackages/register/register-detail?id=" + id + '&no=' + orderNo
      });
    }
  }
};
</script>

<style lang="scss" scoped>
// ================= 变量定义 =================
$primary-blue: #298dff; // 品牌主色
$primary-light: #e6f2ff; // 品牌浅色
$text-main: #1a1a1a; // 主文本色
$text-secondary: #666666; // 次级文本色
$text-placeholder: #999999; // 占位符色
$bg-page: #f2f4f7; // 页面背景 (更护眼的浅灰)
$bg-white: #ffffff; // 纯白
$radius-base: 12rpx; // 基础圆角
$radius-bubble: 20rpx; // 气泡圆角

// ================= 基础容器 =================
.chat-container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  background-color: $bg-page;
}

// ================= 头部导航栏 =================
.chat-header {
  background-color: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(10px); // 毛玻璃效果
  z-index: 100;
  position: relative;

  .header-main {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 24rpx 32rpx;
    border-bottom: 1rpx solid rgba(0,0,0,0.03);

    .doctor-info-group {
      display: flex;
      flex-direction: column;

      .doctor-name {
        font-size: 34rpx;
        font-weight: 600;
        color: $text-main;
        margin-bottom: 4rpx;
      }

      .doctor-badges {
        display: flex;
        align-items: center;

        .status-dot {
          width: 12rpx;
          height: 12rpx;
          background-color: #52c41a; // 在线绿
          border-radius: 50%;
          margin-right: 8rpx;
          box-shadow: 0 0 0 2rpx rgba(82, 196, 26, 0.2);
        }

        .sub-text {
          font-size: 24rpx;
          color: $text-secondary;
        }
      }
    }

    .header-action {
      display: flex;
      align-items: center;
      padding: 12rpx 20rpx;
      background: $primary-light;
      border-radius: 32rpx;

      .expand-tip {
        font-size: 24rpx;
        color: $primary-blue;
        margin-right: 6rpx;
      }
    }
  }
}

// ================= 医生详情下拉面板 =================
.doctor-detail-panel {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s cubic-bezier(0.25, 0.8, 0.5, 1); // 缓动动画
  background: $bg-white;

  &.panel-show {
    max-height: 300rpx;
    border-bottom: 1rpx solid #eee;
  }

  .detail-grid {
    padding: 24rpx 32rpx;
    display: flex;
    flex-wrap: wrap;
    gap: 24rpx;

    .detail-row {
      width: 45%;
      display: flex;
      flex-direction: column;

      &.full-width {
        width: 100%;
      }

      .label {
        font-size: 22rpx;
        color: $text-placeholder;
        margin-bottom: 8rpx;
      }

      .value {
        font-size: 26rpx;
        color: $text-main;
        line-height: 1.4;
      }
    }
  }
}

// ================= 消息滚动区域 =================
.chat-messages {
  flex: 1;
  background-color: $bg-page;
  box-sizing: border-box;
}

.message-list-container {
  padding: 30rpx 24rpx;
}

.empty-state {
  text-align: center;
  padding: 40rpx 0;

  text {
    font-size: 24rpx;
    color: #ccc;
    background: rgba(0,0,0,0.03);
    padding: 8rpx 20rpx;
    border-radius: 20rpx;
  }
}

.time-divider {
  text-align: center;
  margin: 30rpx 0;
  font-size: 22rpx;
  color: #b0b0b0;
}

// ================= 消息行核心布局 =================
.message-wrapper {
  margin-bottom: 40rpx; // 增加间距，提升呼吸感
}

.chat-row {
  display: flex;
  align-items: flex-start;
  max-width: 100%;

  .avatar {
    width: 80rpx;
    height: 80rpx;
    border-radius: 50%;
    border: 2rpx solid #fff;
    box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05);
    flex-shrink: 0;
  }

  .bubble-container {
    max-width: 72%;
    display: flex;
    flex-direction: column;
  }

  // 对方消息 (左侧)
  &.other {
    flex-direction: row;

    .avatar { margin-right: 20rpx; }

    .message-bubble {
      background-color: $bg-white;
      color: $text-main;
      // 左上角直角，引导视线
      border-radius: 4rpx $radius-bubble $radius-bubble $radius-bubble;
      box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.03);

      // 卡片类型特殊处理
      &.card-bubble {
        padding: 0;
        background: transparent;
        box-shadow: none;
      }
    }
  }

  // 我发送的消息 (右侧)
  &.mine {
    flex-direction: row-reverse;

    .avatar { margin-left: 20rpx; }

    .message-bubble {
      background-color: $primary-blue;
      color: #fff;
      // 右上角直角
      border-radius: $radius-bubble 4rpx $radius-bubble $radius-bubble;
      box-shadow: 0 4rpx 12rpx rgba(41, 141, 255, 0.25);

      // 如果我发送的是卡片
      &.card-bubble {
        background: transparent;
        box-shadow: none;
      }
    }
  }
}

// ================= 气泡内容样式 =================
.message-bubble {
  padding: 20rpx 28rpx;
  font-size: 30rpx;
  line-height: 1.6;
  position: relative;
  word-break: break-all;

  // 图片、视频、卡片类型去除默认气泡样式 (背景、阴影、内边距)
  &.type-1, &.type-3,
  &.type-4, &.type-9, &.type-10, &.type-11, &.type-12, &.type-13 {
    padding: 0;
    overflow: visible; // 允许卡片阴影溢出
    background: transparent;
    box-shadow: none;
    border: none;
    border-radius: 0; // 移除气泡圆角，由卡片自身控制
  }

  .media-content {
    border-radius: 16rpx;
    max-width: 100%;
    &.image { max-width: 320rpx; }
    &.video { width: 320rpx; height: 180rpx; }
  }
}

// ================= 音频消息 =================
.audio-content {
  display: flex;
  align-items: center;
  min-width: 120rpx;
  gap: 12rpx;

  &.mine-audio {
    flex-direction: row-reverse;
  }

  .duration { font-weight: bold; font-size: 28rpx; }

  // 声波图标占位
  .wifi-symbol {
    width: 30rpx; height: 30rpx;
    // 这里可以添加CSS动画实现播放效果
  }
}

// ================= 统一卡片系统 (Card System) =================
.card-bubble {
  background-color: $bg-white !important; // 强制白底
  border-radius: 24rpx !important;
  width: 500rpx;
  overflow: hidden;
  box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.06) !important;
  border: 1rpx solid rgba(0,0,0,0.02);
  // 【修复】强制重置文字颜色，防止继承父级气泡的白色文字
  color: $text-main !important;

  // 卡片头部
  .card-header {
    padding: 24rpx;
    border-bottom: 1rpx solid #f5f5f5;
    display: flex;
    justify-content: space-between;
    align-items: center;

    .title-group {
      display: flex;
      align-items: center;
      gap: 12rpx;
      .card-title {
        font-size: 30rpx;
        font-weight: 600;
        color: $text-main;
      }
    }

    .status-tag {
      font-size: 22rpx;
      padding: 4rpx 16rpx;
      border-radius: 8rpx;
      background: #f0f0f0;
      color: #666;

      &.status-1, &.blue { background: rgba(41, 141, 255, 0.1); color: $primary-blue; }
      &.status-0 { background: rgba(255, 153, 0, 0.1); color: #ff9900; }
    }
  }

  // 卡片主体
  .card-body {
    padding: 24rpx;
    display: flex;
    flex-direction: column;
    gap: 16rpx;

    .info-row {
      display: flex;
      justify-content: space-between;
      align-items: baseline;
      font-size: 26rpx;

      .label { color: $text-secondary; }
      .value { color: $text-main; text-align: right; max-width: 70%; }
      .price { color: #ff4d4f; font-weight: bold; font-size: 30rpx; }
      .small { font-size: 24rpx; color: #999; }
    }

    .text-block {
      background: #f9f9f9;
      padding: 16rpx;
      border-radius: 12rpx;

      .block-label { display: block; font-size: 22rpx; color: #999; margin-bottom: 8rpx; }
      .block-content { font-size: 26rpx; color: #333; line-height: 1.5; }
    }
  }

  // 卡片底部操作栏
  .card-footer {
    padding: 20rpx 24rpx;
    border-top: 1rpx solid #f5f5f5;

    .action-btn {
      background: #fff;
      border: 1rpx solid #eee;
      color: $text-secondary;
      font-size: 26rpx;
      border-radius: 40rpx;
      line-height: 2.2;

      &.primary {
        background: $primary-blue;
        color: #fff;
        border: none;
        box-shadow: 0 4rpx 12rpx rgba(41, 141, 255, 0.2);
      }

      &:active { opacity: 0.8; }
    }

    &.split {
      display: flex;
      justify-content: space-between;
      align-items: center;

      .link-btn { color: $primary-blue; font-size: 26rpx; padding: 10rpx; }
    }
  }
}

// ================= 底部输入与工具栏 (Footer) =================
.chat-footer {
  background-color: #fff;
  border-top: 1rpx solid #eee;
  padding: 16rpx 24rpx 40rpx 24rpx; // 底部增加 Padding 适配 iPhone 底部条
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 99;

  // 问诊已结束提示
  .consultation-ended-notice {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 30rpx;
    color: #999;
    font-size: 28rpx;
    gap: 12rpx;
  }

  .toolbar-area {
    margin-bottom: 20rpx;

    .action-grid {
      display: flex;
      gap: 40rpx;
      padding-left: 12rpx;

      .action-item {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 8rpx;

        .icon-circle {
          width: 80rpx;
          height: 80rpx;
          background: #f7f8fa;
          border-radius: 24rpx;
          display: flex;
          align-items: center;
          justify-content: center;
        }

        text { font-size: 22rpx; color: #666; }
      }
    }
  }

  .input-bar {
    display: flex;
    align-items: flex-end; // 底部对齐
    gap: 20rpx;

    .voice-switch {
      padding-bottom: 20rpx; // 图标居中对齐微调
    }

    .input-field-wrapper {
      flex: 1;
      background: #f2f4f7;
      border-radius: 40rpx;
      min-height: 80rpx;
      display: flex;
      align-items: center;
      padding: 0 8rpx;

      .main-input {
        flex: 1;
        height: 80rpx;
        padding: 0 24rpx;
        font-size: 30rpx;
        color: #333;
      }

      .voice-btn {
        flex: 1;
        height: 72rpx;
        margin: 4rpx;
        background: #fff;
        border-radius: 36rpx;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 28rpx;
        color: #333;
        font-weight: 500;
        box-shadow: 0 2rpx 4rpx rgba(0,0,0,0.05);

        &.recording {
          background: #ffecec;
          color: #ff4d4f;
        }
      }
    }

    .send-btn-wrapper {
      .send-btn {
        width: 80rpx;
        height: 80rpx;
        background: $primary-blue;
        border-radius: 50%;
        display: flex;
        align-items: center;
        justify-content: center;
        box-shadow: 0 4rpx 12rpx rgba(41, 141, 255, 0.3);
        transition: transform 0.1s;

        &:active { transform: scale(0.9); }
      }
    }
  }
}

// ================= 录音蒙层 =================
.recording-overlay {
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background: rgba(0,0,0,0.6);
  z-index: 999;
  display: flex;
  align-items: center;
  justify-content: center;

  .recording-box {
    width: 300rpx;
    height: 300rpx;
    background: rgba(0,0,0,0.8);
    border-radius: 32rpx;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: #fff;
    gap: 20rpx;

    .wave-animation {
      width: 100rpx;
      height: 4rpx;
      background: #298dff;
      border-radius: 20rpx;
      // 可添加 keyframes 动画
    }

    .tip { font-size: 24rpx; color: #ccc; }
  }
}

// 加载占位
.loading-spinner {
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 20rpx;
}

// 纯CSS加载动画 (替代u-loading-icon)
.spinner {
  width: 32rpx;
  height: 32rpx;
  border: 3rpx solid #e5e5e5;
  border-top-color: #999;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

// ================= 系统消息卡片样式 =================
.system-message-card {
  background: #fffbe6;
  border: 1px solid #ffe58f;
  border-radius: 12rpx;
  padding: 20rpx;
  margin: 20rpx auto;
  max-width: 80%;

  .price-update-notice {
    display: flex;
    align-items: center;
    gap: 10rpx;
    margin-bottom: 10rpx;

    .notice-text {
      color: #d48806;
      font-size: 28rpx;
      font-weight: 500;
    }
  }

  .price-detail {
    display: flex;
    flex-direction: column;
    gap: 6rpx;
    font-size: 24rpx;
    color: #666;
    padding-left: 28rpx;
  }
}

.system-message {
  text-align: center;
  color: #999;
  font-size: 24rpx;
  padding: 10rpx 0;
}
</style>