import { defineStore } from "pinia";
import { ref } from "vue";
import { ElMessage } from "element-plus";
import TUIChatEngine, { TUIChatService, TUIStore, StoreName } from "@tencentcloud/chat-uikit-engine";
import { TUILogin } from "@tencentcloud/tui-core";
import { getMrtcConfig, notifyVideoRoomCreated, notifyVideoRoomEnded, saveMrtcRecordEvent } from "@/api/hlw";

const VIDEO_SIGNAL = {
  INVITE: "VIDEO_CONSULT_INVITE",
  CANCEL: "VIDEO_CONSULT_CANCEL",
  REJECT: "VIDEO_CONSULT_REJECT",
  ACCEPT: "VIDEO_CONSULT_ACCEPT",
  HANGUP: "VIDEO_CONSULT_HANGUP",
  BUSY: "VIDEO_CONSULT_BUSY",
};

// 默认走后端签名配置
const USE_MRTC_DEMO_CONFIG = false;
// 云端自动录制已开启时，不需要再由前端调用 StartRemoteRecord
// 如需启用“前端触发的服务端录制（远端录制）”，请在 PC 环境变量中设置 VITE_MRTC_REMOTE_RECORD=true
const ENABLE_MRTC_REMOTE_RECORD = import.meta.env.VITE_MRTC_REMOTE_RECORD === "true";
// 摄像头分辨率/码率建议不要强约束，否则部分设备会触发 getUserMedia OverconstrainedError（5101）
const PREFERRED_LOCAL_VIDEO_WIDTH = Number(import.meta.env.VITE_MRTC_LOCAL_VIDEO_WIDTH || 0);
const PREFERRED_LOCAL_VIDEO_HEIGHT = Number(import.meta.env.VITE_MRTC_LOCAL_VIDEO_HEIGHT || 0);
const PREFERRED_LOCAL_VIDEO_FRAME = Number(import.meta.env.VITE_MRTC_LOCAL_VIDEO_FRAME || 0);
const PREFERRED_LOCAL_VIDEO_BITRATE = Number(import.meta.env.VITE_MRTC_LOCAL_VIDEO_BITRATE || 0);

export const useVideoConsultStore = defineStore("videoConsult", () => {
  const controller = ref(null);
  const status = ref("idle"); // idle | connecting | inRoom | error
  const activeOrderId = ref("");
  const currentOrder = ref(null);
  // UI 相关：将视频弹窗/小窗提升为全局常驻组件后，用该状态控制显示与“待发起订单”
  const uiVisible = ref(false);
  const uiOrder = ref(null);
  // 远端首次成功订阅的时间戳，用于计算通话时长（即使远端随后离房也保留）
  const callConnectedAtMs = ref(0);

  const roomId = ref("");
  const rtoken = ref("");

  // 服务端（云端）录制相关状态
  const recordId = ref("");
  const recordThirdId = ref("");
  const remoteRecordState = ref("idle"); // idle | requested | init | recording | stopping | stopped | error
  const leaveAfterStop = ref(false);
  let leaveAfterStopTimer = null;

  const connectConfig = ref(null);
  const roomConfig = ref(null);

  const domIds = ref({
    localVideoId: "",
    localStreamIdInputId: "",
    remoteVideoId: "",
    remoteAudioId: "",
    remoteStreamIdInputId: "",
    remoteFeedIdInputId: "",
  });

  const publishSid = ref(null);
  const remoteSids = ref([]);

  const localAudioEnabled = ref(true);
  const localVideoEnabled = ref(true);

  const connState = ref(0);
  const roomState = ref(0);

  const errorMessage = ref("");
  // leaveRoom 被“对端信令/异常”触发时，避免在 OnLeaveRoom 再向对端二次发送结束信令（防止重复/回环）
  const skipSendSignalOnLeave = ref(false);
  const leaveReason = ref(""); // user_hangup | no_answer_timeout | peer_signal | ...

  // 呼叫超时（无人接听）保护：到时未接通则自动挂断
  const INVITE_TIMEOUT_MS = Number(import.meta.env.VITE_VIDEO_INVITE_TIMEOUT_MS || 60000);
  let inviteTimeoutTimer = null;

  function setDomIds(ids) {
    domIds.value = { ...domIds.value, ...ids };
  }

  function openUI(order) {
    uiVisible.value = true;
    // 优先展示当前会话订单，其次使用入参（用于首次发起）
    uiOrder.value = order || currentOrder.value || uiOrder.value || null;
  }

  function closeUI() {
    uiVisible.value = false;
    uiOrder.value = null;
  }

  function formatDuration(totalSeconds) {
    const sec = Math.max(0, Number(totalSeconds) || 0);
    const m = String(Math.floor(sec / 60)).padStart(2, "0");
    const s = String(sec % 60).padStart(2, "0");
    return `${m}:${s}`;
  }

  function getCallDurationSec(nowMs = Date.now()) {
    if (!callConnectedAtMs.value) return 0;
    return Math.max(0, Math.floor((nowMs - callConnectedAtMs.value) / 1000));
  }

  function getCallDurationText(nowMs = Date.now()) {
    const sec = getCallDurationSec(nowMs);
    return `通话时长${formatDuration(sec)}`;
  }

  function buildVideoRecordExtension({ type, doctorText, patientText, durationSec }) {
    const payload = {
      v: 1,
      t: type,
      doctorText: doctorText || "",
      patientText: patientText || "",
    };
    if (Number.isFinite(Number(durationSec))) {
      payload.durationSec = Number(durationSec);
    }
    return JSON.stringify(payload);
  }

  function clearInviteTimeout() {
    if (inviteTimeoutTimer) {
      clearTimeout(inviteTimeoutTimer);
      inviteTimeoutTimer = null;
    }
  }

  function startInviteTimeout() {
    clearInviteTimeout();
    // 0/负数：认为不启用
    if (!Number.isFinite(INVITE_TIMEOUT_MS) || INVITE_TIMEOUT_MS <= 0) return;
    inviteTimeoutTimer = setTimeout(() => {
      // 仍处于等待接听状态时才触发
      if (status.value !== "idle" && getCallDurationSec() === 0 && (!remoteSids.value || remoteSids.value.length === 0)) {
        console.warn("[MRTC][PC] invite timeout, auto hangup");
        ElMessage.warning("患者超时未接听，视频问诊已取消");
        leaveRoom({ reason: "no_answer_timeout" });
      }
    }, INVITE_TIMEOUT_MS);
  }

  function getDoctorCodeSafe() {
    const order = currentOrder.value || {};
    return order.doctorCode || sessionStorage.getItem("doctorNo") || "";
  }

  async function reportRecordEvent(event, extra = {}) {
    const orderId = getActiveOrderId();
    if (!orderId) return;
    const payload = {
      orderId,
      doctorCode: getDoctorCodeSafe(),
      roomId: roomId.value,
      recordId: extra.recordId || recordId.value,
      recordThirdId: extra.recordThirdId || recordThirdId.value,
      event,
      ...extra,
    };
    try {
      await saveMrtcRecordEvent(payload);
    } catch (e) {
      console.warn("[MRTC][PC] saveMrtcRecordEvent failed", event, e);
    }
  }

  function startRemoteRecordIfNeeded(ctrl) {
    if (!ENABLE_MRTC_REMOTE_RECORD) return;
    if (!ctrl || remoteRecordState.value !== "idle") return;
    if (typeof ctrl.StartRemoteRecord !== "function") {
      console.warn("[MRTC][PC] StartRemoteRecord not supported by SDK");
      return;
    }
    const orderId = getActiveOrderId();
    if (!orderId) return;

    const thirdId = `${orderId}_${Date.now()}`;
    recordThirdId.value = thirdId;
    remoteRecordState.value = "requested";
    reportRecordEvent("REQUESTED", {
      recordThirdId: thirdId,
      startAt: Date.now(),
    });

    const recordParam = {
      // 至少两路流（医生+患者）才开始真正录制，用于过滤“未接通”场景
      recordTotalStream: 2,
      // 房间销毁时结束录制
      endType: 1,
    };
    try {
      // 该 SDK（remote_record.js）签名为：StartRemoteRecord(file_path, record_param, record_third_id)
      // file_path 可为空，走服务端默认路径
      ctrl.StartRemoteRecord("", recordParam, thirdId);
    } catch (e) {
      remoteRecordState.value = "error";
      reportRecordEvent("REQUEST_FAILED", {
        recordThirdId: thirdId,
        errMsg: e && e.message ? e.message : "StartRemoteRecord exception",
      });
    }
  }

  function requestStopRemoteRecord(ctrl, reason) {
    if (!ctrl) return false;
    if (!recordId.value) return false;
    if (remoteRecordState.value === "stopping" || remoteRecordState.value === "stopped") return false;
    if (typeof ctrl.StopRemoteRecord !== "function") return false;
    try {
      remoteRecordState.value = "stopping";
      reportRecordEvent("STOP_REQUESTED", { reason, endAt: Date.now() });
      ctrl.StopRemoteRecord(recordId.value);
      return true;
    } catch (e) {
      remoteRecordState.value = "error";
      reportRecordEvent("STOP_REQUEST_FAILED", {
        reason,
        errMsg: e && e.message ? e.message : "StopRemoteRecord exception",
        endAt: Date.now(),
      });
      return false;
    }
  }

  function callGetRecordInfo(ctrl, rid) {
    if (!ctrl || typeof ctrl.GetRecordInfo !== "function" || !rid) return;
    const sign = (connectConfig.value || {}).sign;
    try {
      // 该 SDK（remote_record.js）签名为：GetRecordInfo(record_id, room_id, media_type, sign)
      ctrl.GetRecordInfo(rid, roomId.value, 0, sign);
    } catch (e) {
      console.warn("[MRTC][PC] GetRecordInfo exception", e);
    }
  }

  async function start(order) {
    if (!order || !order.orderId) {
      ElMessage.error("未找到当前问诊订单，无法发起视频问诊");
      return;
    }
    // 门店平板终端暂不支持视频问诊
    if (order.orderSource === "MATEPAD") {
      ElMessage.warning("门店平板端暂不支持视频问诊功能");
      return;
    }
    if (status.value !== "idle" && activeOrderId.value && activeOrderId.value !== order.orderId) {
      ElMessage.warning("当前已有其他问诊订单的视频问诊进行中，请先结束当前视频问诊");
      return;
    }
    if (!domIds.value.localVideoId || !domIds.value.remoteVideoId || !domIds.value.remoteAudioId) {
      return;
    }

    status.value = "connecting";
    errorMessage.value = "";
    currentOrder.value = order;
    // 记录当前视频会话绑定的订单号，用于后续所有信令发送与后端通知
    activeOrderId.value = order.orderId;

    try {
      const doctorCode = order.doctorCode || sessionStorage.getItem("doctorNo") || "";
      if (!doctorCode) {
        status.value = "error";
        errorMessage.value = "医生编号缺失，无法生成音视频签名";
        ElMessage.error(errorMessage.value);
        return;
      }

      let connectParam;
      if (USE_MRTC_DEMO_CONFIG) {
        connectParam = {
          log: true,
          uid: doctorCode || `demo_doctor_${Date.now()}`,
          biz_name: "demo",
          sub_biz: "default",
          workspaceId: "default",
          room_server_url: "wss://mrtc.mpaas.cn-hangzhou.aliyuncs.com/ws",
          sign: "signature",
          network_check_timeout: 120 * 1000,
          logCacheMaxLines: 30,
          autoUploadLog: true,
        };
      } else {
        const { success, data, message } = await getMrtcConfig({
          orderId: order.orderId,
          doctorCode,
        });
        if (!success) {
          status.value = "error";
          errorMessage.value = message || "获取音视频配置失败";
          ElMessage.error(errorMessage.value);
          return;
        }
        const raw = data && typeof data === "object" ? data : {};
        const unwrapData = (obj) => {
          let cur = obj;
          const maxDepth = 3;
          for (let i = 0; i < maxDepth; i++) {
            if (cur && typeof cur === "object" && cur.data && typeof cur.data === "object") {
              cur = cur.data;
            } else {
              break;
            }
          }
          return cur || {};
        };
        const cfg = unwrapData(raw);
        connectParam = {
          log: true,
          uid: cfg.uid || doctorCode,
          biz_name: cfg.bizName || cfg.biz_name,
          sub_biz: cfg.subBiz || cfg.sub_biz,
          workspaceId: cfg.workspaceId || cfg.workspace_id,
          room_server_url: cfg.roomServerUrl || cfg.room_server_url,
          sign: cfg.sign || cfg.signature,
          network_check_timeout: cfg.networkCheckTimeout || 120 * 1000,
          logCacheMaxLines: cfg.logCacheMaxLines || 30,
          autoUploadLog: typeof cfg.autoUploadLog === "boolean" ? cfg.autoUploadLog : true,
        };
        console.log("[MRTC] parsed cfg:", cfg);
        console.log("[MRTC] computed connectParam:", connectParam);
      }

      if (
        !connectParam.biz_name ||
        !connectParam.sub_biz ||
        !connectParam.workspaceId ||
        !connectParam.room_server_url ||
        !connectParam.sign
      ) {
        status.value = "error";
        errorMessage.value = "音视频通话配置不完整，请联系管理员配置后再试";
        console.error("[MRTC] config missing field", connectParam);
        ElMessage.error(errorMessage.value);
        return;
      }

      connectConfig.value = connectParam;
      const MeetingCtor = typeof window !== "undefined" ? window.MeetingController : null;
      if (!MeetingCtor) {
        status.value = "error";
        errorMessage.value = "音视频 SDK 未正确加载，请刷新页面或联系管理员";
        ElMessage.error(errorMessage.value);
        return;
      }
      const ctrl = new MeetingCtor();
      controller.value = ctrl;
      setupControllerCallbacks(ctrl);
      ctrl.Connect(connectParam);
    } catch (e) {
      status.value = "error";
      errorMessage.value = e && e.message ? e.message : "发起视频问诊失败";
      ElMessage.error(errorMessage.value);
    }
  }

  function setupControllerCallbacks(ctrl) {
    ctrl.OnConnectFailed = function (_code, msg) {
      connState.value = 0;
      status.value = "error";
      errorMessage.value = msg || "建立音视频连接失败";
      ElMessage.error(errorMessage.value);
    };
    ctrl.OnConnectOK = function () {
      connState.value = 1;
      initRoomConfig(ctrl);
    };
    ctrl.OnInitRoomConfigOK = function () {
      joinOrCreateRoom(ctrl);
    };
    ctrl.OnCreateRoomSucc = function (rid, token) {
      roomId.value = rid;
      rtoken.value = token;
      roomState.value = 1;
      status.value = "inRoom";
      notifyRoomCreatedIfNeeded();
      startRemoteRecordIfNeeded(ctrl);
      sendVideoInviteMessage(); // 通知患者端显示来电
      startInviteTimeout(); // 等待接听超时保护
    };
    ctrl.OnJoinRoomSucc = function () {
      roomState.value = 1;
      status.value = "inRoom";
      startRemoteRecordIfNeeded(ctrl);
      startInviteTimeout(); // 加入房间后同样启动超时保护（兼容 JoinRoom 场景）
    };
    ctrl.OnLeaveRoom = function () {
      handleLeave();
    };
    // 远端与会者退出房间（例如患者小程序异常退出、断网等），MRTC demo 日志中会打印
    // ~~~~~~~~~~~~~ user ${uid} left room! exitType=${exitType}
    // 这里接入回调，用于在没有收到 IM 信令的情况下也能关闭医生端通话
    ctrl.OnParticipantLeaveRoom = function (uid, exitType) {
      console.warn("[MRTC][PC] OnParticipantLeaveRoom", {
        uid,
        exitType,
        status: status.value,
        roomState: roomState.value,
      });
      handleRemoteLeave(uid, exitType);
    };
    ctrl.OnPublishSucc = function (sid) {
      publishSid.value = sid;
      localAudioEnabled.value = true;
      localVideoEnabled.value = true;
    };
    ctrl.OnUnPublishSucc = function () {
      publishSid.value = null;
    };
    ctrl.OnSubscribeSucc = function (_feedId, sid) {
      if (!callConnectedAtMs.value) {
        callConnectedAtMs.value = Date.now();
      }
      clearInviteTimeout();
      remoteSids.value = [...remoteSids.value, sid];
    };
    ctrl.OnUnSubscribeSucc = function (sid) {
      remoteSids.value = remoteSids.value.filter((i) => i !== sid);
    };

    // ===== 服务端录制（远端录制）回调 =====
    ctrl.OnInitRemoteRecordSucc = function (rid, thirdId) {
      recordId.value = rid || "";
      if (thirdId) recordThirdId.value = thirdId;
      remoteRecordState.value = "init";
      reportRecordEvent("INIT_SUCC", {
        recordId: rid,
        recordThirdId: thirdId,
        startAt: Date.now(),
      });
    };
    ctrl.OnStartRemoteRecordSucc = function (rid, thirdId) {
      recordId.value = rid || recordId.value;
      if (thirdId) recordThirdId.value = thirdId;
      remoteRecordState.value = "recording";
      reportRecordEvent("START_SUCC", {
        recordId: rid,
        recordThirdId: thirdId,
      });
    };
    ctrl.OnStartRemoteRecordFailed = function (rid, errCode, errMsg, thirdId) {
      recordId.value = rid || recordId.value;
      if (thirdId) recordThirdId.value = thirdId;
      remoteRecordState.value = "error";
      reportRecordEvent("START_FAILED", {
        recordId: rid,
        recordThirdId: thirdId,
        errCode,
        errMsg,
        endAt: Date.now(),
      });
    };
    ctrl.OnStopRecordSucc = function (rid) {
      remoteRecordState.value = "stopped";
      reportRecordEvent("STOP_SUCC", {
        recordId: rid,
        endAt: Date.now(),
      });
      callGetRecordInfo(ctrl, rid || recordId.value);
      if (leaveAfterStop.value) {
        leaveAfterStop.value = false;
        try {
          ctrl.LeaveRoom();
        } catch (e) {}
      }
    };
    ctrl.OnStopRecordFailed = function (rid, errCode, errMsg) {
      remoteRecordState.value = "error";
      reportRecordEvent("STOP_FAILED", {
        recordId: rid,
        errCode,
        errMsg,
        endAt: Date.now(),
      });
      if (leaveAfterStop.value) {
        leaveAfterStop.value = false;
        try {
          ctrl.LeaveRoom();
        } catch (e) {}
      }
    };
    ctrl.OnRemoteRecordInfoSucc = function (info) {
      const recordInfo = info && typeof info === "object" ? info : {};
      const rid = recordInfo.recordId || recordInfo.record_id || recordId.value;
      const st = recordInfo.status;
      const fType = recordInfo.fileType || recordInfo.file_type;
      const fPath = recordInfo.filePath || recordInfo.file_path;
      reportRecordEvent("RECORD_INFO", {
        recordId: rid,
        status: st,
        fileType: fType,
        filePath: fPath,
        payload: recordInfo,
      });
      // 若仍在录制/上传中，继续轮询
      if (st === 0 || st === 1) {
        setTimeout(() => callGetRecordInfo(ctrl, rid), 3000);
      }
    };
    ctrl.OnRemoteRecordInfoFailed = function (rid, errCode, errMsg) {
      reportRecordEvent("RECORD_INFO_FAILED", {
        recordId: rid,
        errCode,
        errMsg,
      });
    };
  }

  function initRoomConfig(ctrl) {
    const config = {
      third_id: currentOrder.value ? currentOrder.value.orderId : "",
      auto_publish_subscribe: 3,
      enableVideo: true,
      enableAudio: true,
      enableDatachannel: false,
      subscribeEnableVideo: true,
      subscribeEnableAudio: true,
      subscribeEnableDatachannel: false,
      publish_device: 1,
      initPublish: [
        {
          publish_video_id: domIds.value.localVideoId,
          publish_streamId_id: domIds.value.localStreamIdInputId,
          publish_tag: "VIDEO_SOURCE_CAMERA",
        },
      ],
      initSubscribe: [
        {
          subscribe_video_id: domIds.value.remoteVideoId,
          subscribe_audio_id: domIds.value.remoteAudioId,
          subscribe_streamId_id: domIds.value.remoteStreamIdInputId,
          feedId_id: domIds.value.remoteFeedIdInputId,
        },
      ],
      echoCancellation: true,
      noiseSuppression: true,
      autoGainControl: true,
      localVideoSwitch: true,
      // 完全不指定任何分辨率约束，让浏览器/摄像头自动选择
    };
    // 注释掉所有分辨率约束，避免 OverconstrainedError
    // if (Number.isFinite(PREFERRED_LOCAL_VIDEO_WIDTH) && PREFERRED_LOCAL_VIDEO_WIDTH > 0) {
    //   config.local_video_width = PREFERRED_LOCAL_VIDEO_WIDTH;
    // }
    // if (Number.isFinite(PREFERRED_LOCAL_VIDEO_HEIGHT) && PREFERRED_LOCAL_VIDEO_HEIGHT > 0) {
    //   config.local_video_height = PREFERRED_LOCAL_VIDEO_HEIGHT;
    // }
    // if (Number.isFinite(PREFERRED_LOCAL_VIDEO_FRAME) && PREFERRED_LOCAL_VIDEO_FRAME > 0) {
    //   config.local_video_frame = PREFERRED_LOCAL_VIDEO_FRAME;
    // }
    // if (Number.isFinite(PREFERRED_LOCAL_VIDEO_BITRATE) && PREFERRED_LOCAL_VIDEO_BITRATE > 0) {
    //   config.local_video_bitrate = PREFERRED_LOCAL_VIDEO_BITRATE;
    // }
    roomConfig.value = config;
    ctrl.InitRoomConfig(config);
  }

  function joinOrCreateRoom(ctrl) {
    const cfg = connectConfig.value || {};
    if (roomId.value && rtoken.value) {
      ctrl.JoinRoom(roomId.value, rtoken.value, cfg.sign);
    } else {
      ctrl.CreateRoom(cfg.sign);
    }
  }

  function getActiveOrderId() {
    // 优先使用启动视频时记录的订单号，避免在聊天窗口切换会话后串线
    if (activeOrderId.value) return activeOrderId.value;
    if (currentOrder.value && currentOrder.value.orderId) return currentOrder.value.orderId;
    return "";
  }

  async function notifyRoomCreatedIfNeeded() {
    const orderId = getActiveOrderId();
    if (!orderId || !roomId.value) return;
    try {
      await notifyVideoRoomCreated({
        orderId,
        roomId: roomId.value,
        rtoken: rtoken.value,
      });
    } catch (e) {}
  }

  async function notifyRoomEndedIfNeeded() {
    const orderId = getActiveOrderId();
    if (!orderId || !roomId.value) return;
    try {
      await notifyVideoRoomEnded({
        orderId,
        roomId: roomId.value,
      });
    } catch (e) {}
  }

 function getEndCallSignal() {
  const hasRemote = Array.isArray(remoteSids.value) && remoteSids.value.length > 0;
  const everConnected = !!callConnectedAtMs.value;
  console.log("[MRTC][PC] getEndCallSignal", {
    hasRemote,
    everConnected,
    remoteSids: remoteSids.value,
  });
  return hasRemote || everConnected ? VIDEO_SIGNAL.HANGUP : VIDEO_SIGNAL.CANCEL;
}

async function leaveRoom(options = {}) {
  const opts = options && typeof options === "object" ? options : {};
  skipSendSignalOnLeave.value = !!opts.skipSendSignal;
  leaveReason.value = typeof opts.reason === "string" ? opts.reason : "";
  const ctrl = controller.value;
  const endSignal = getEndCallSignal();
  console.log("[MRTC][PC] leaveRoom called", {
    hasController: !!ctrl,
    endSignal,
    status: status.value,
    activeOrderId: activeOrderId.value,
  });
  if (!ctrl) {
    if (!skipSendSignalOnLeave.value) {
      await sendVideoSignal(endSignal, {
        reason: leaveReason.value,
        durationSec: endSignal === VIDEO_SIGNAL.HANGUP ? getCallDurationSec() : 0,
      });
    }
    resetState();
    return;
  }
  // 优先停止服务端录制，确保能拿到录制结果/状态回调
  if (requestStopRemoteRecord(ctrl, "LEAVE_ROOM")) {
    leaveAfterStop.value = true;
    if (leaveAfterStopTimer) clearTimeout(leaveAfterStopTimer);
    leaveAfterStopTimer = setTimeout(() => {
      leaveAfterStop.value = false;
      try {
        if (controller.value) controller.value.LeaveRoom();
      } catch (e) {
        if (!skipSendSignalOnLeave.value) {
          sendVideoSignal(endSignal, {
            reason: leaveReason.value,
            durationSec: endSignal === VIDEO_SIGNAL.HANGUP ? getCallDurationSec() : 0,
          });
        }
        resetState();
      }
    }, 6000);
    return;
  }
  try {
    ctrl.LeaveRoom();
  } catch (e) {
    console.error("[MRTC][PC] ctrl.LeaveRoom error", e);
    if (!skipSendSignalOnLeave.value) {
      await sendVideoSignal(endSignal, {
        reason: leaveReason.value,
        durationSec: endSignal === VIDEO_SIGNAL.HANGUP ? getCallDurationSec() : 0,
      });
    }
    resetState();
  }
}

function handleLeave() {
  // 离房回调触发时，若仍在录制中则尝试补发停止请求（不等待回调）
  if (controller.value) {
    requestStopRemoteRecord(controller.value, "ON_LEAVE_ROOM");
  }
  const endSignal = getEndCallSignal();
  const shouldSkip = skipSendSignalOnLeave.value;
  const reason = leaveReason.value;
  skipSendSignalOnLeave.value = false;
  leaveReason.value = "";
  console.log("[MRTC][PC] handleLeave (OnLeaveRoom)", {
    endSignal,
    status: status.value,
    activeOrderId: activeOrderId.value,
  });
  if (!shouldSkip) {
    sendVideoSignal(endSignal, {
      reason,
      durationSec: endSignal === VIDEO_SIGNAL.HANGUP ? getCallDurationSec() : 0,
    });
  }
  notifyRoomEndedIfNeeded();
  resetState();
}

  // 远端离开房间（患者端退出/掉线）时的处理：不再向对端发送结束信令，只做本地清理与提示。
  function handleRemoteLeave(uid, exitType) {
    console.log("[MRTC][PC] handleRemoteLeave (OnParticipantLeaveRoom)", {
      uid,
      exitType,
      status: status.value,
      roomState: roomState.value,
    });
    // 已经 idle 或不在房间中时，忽略重复回调
    if (status.value === "idle" || roomState.value === 0) {
      return;
    }
    // 远端退出时也尝试停止服务端录制
    if (controller.value) {
      requestStopRemoteRecord(controller.value, "REMOTE_LEFT");
    }
    // 远端结束通话：由医生端统一发送通话记录（通话时长），并走正常离房流程
    leaveRoom({ reason: "peer_hangup" });
    ElMessage.warning("对方已结束本次通话");
  }


  function resetState() {
    try {
      if (controller.value && controller.value.Disconnect) {
        controller.value.Disconnect();
      }
    } catch (e) {}
    controller.value = null;
    skipSendSignalOnLeave.value = false;
    leaveReason.value = "";
    connState.value = 0;
    roomState.value = 0;
    status.value = "idle";
    activeOrderId.value = "";
    currentOrder.value = null;
    callConnectedAtMs.value = 0;
    roomId.value = "";
    rtoken.value = "";
    clearInviteTimeout();
    if (leaveAfterStopTimer) {
      clearTimeout(leaveAfterStopTimer);
      leaveAfterStopTimer = null;
    }
    leaveAfterStop.value = false;
    recordId.value = "";
    recordThirdId.value = "";
    remoteRecordState.value = "idle";
    publishSid.value = null;
    remoteSids.value = [];
    localAudioEnabled.value = true;
    localVideoEnabled.value = true;
    errorMessage.value = "";
  }

  function toggleAudio() {
    if (!controller.value || !publishSid.value) return;
    const sid = Number(publishSid.value);
    const next = !localAudioEnabled.value;
    controller.value.SetLocalAudioEnable(next ? 1 : 0, sid);
    localAudioEnabled.value = next;
  }

  function toggleVideo() {
    if (!controller.value || !publishSid.value) return;
    const sid = Number(publishSid.value);
    const next = !localVideoEnabled.value;
    controller.value.SetLocalVideoEnable(next ? 1 : 0, sid);
    localVideoEnabled.value = next;
  }

  async function sendVideoInviteMessage() {
    try {
      const orderId = getActiveOrderId();
      if (!orderId) return;
      const to = getPeerImUserId({ orderId });
      if (!to) {
        console.error("[MRTC] 无法确定患者 IM 用户ID，邀请未发送");
        return;
      }
      // 邀请信令仅用于驱动来电弹窗，不在聊天列表中展示
      const extensionText = "";
      await TUIChatService.sendCustomMessage({
        to,
        conversationType: TUIChatEngine.TYPES.CONV_C2C,
        payload: {
          data: VIDEO_SIGNAL.INVITE,
          description: "VIDEO_CONSULT",
          extension: extensionText,
        },
      });
    } catch (e) {
      console.error("[MRTC] 发送视频问诊邀请消息失败", e);
    }
  }

async function sendVideoSignal(signal, meta = {}) {
  try {
    const orderId = getActiveOrderId();
    if (!orderId) {
      console.warn("[MRTC][PC] sendVideoSignal no activeOrderId", signal);
      return;
    }
    const to = getPeerImUserId({ orderId });
    if (!to) {
      console.warn("[MRTC][PC] sendVideoSignal no peer userId", {
        signal,
        orderId,
      });
      return;
    }
    const m = meta && typeof meta === "object" ? meta : {};
    const reason = typeof m.reason === "string" ? m.reason : "";
    const durationSec = Number.isFinite(Number(m.durationSec)) ? Number(m.durationSec) : 0;

    let extensionText = "";
    if (signal === VIDEO_SIGNAL.CANCEL) {
      if (reason === "no_answer_timeout") {
        extensionText = buildVideoRecordExtension({
          type: "missed",
          doctorText: "对方未接听",
          patientText: "未接听",
        });
      } else if (reason === "peer_reject") {
        extensionText = buildVideoRecordExtension({
          type: "missed",
          doctorText: "对方已拒绝",
          patientText: "未接听",
        });
      } else {
        extensionText = buildVideoRecordExtension({
          type: "cancel",
          doctorText: "已取消",
          patientText: "已取消",
        });
      }
    } else if (signal === VIDEO_SIGNAL.HANGUP) {
      const now = Date.now();
      const durationText = getCallDurationText(now) || `通话时长${formatDuration(durationSec)}`;
      extensionText = buildVideoRecordExtension({
        type: "duration",
        doctorText: durationText,
        patientText: durationText,
        durationSec: getCallDurationSec(now) || durationSec,
      });
      // 兜底：若 connectedAt 不存在（极端场景），用传入的 durationSec
      if (!callConnectedAtMs.value && durationSec >= 0) {
        const fallback = `通话时长${formatDuration(durationSec)}`;
        extensionText = buildVideoRecordExtension({
          type: "duration",
          doctorText: fallback,
          patientText: fallback,
          durationSec,
        });
      }
    }
    console.log("[MRTC][PC] sendVideoSignal", {
      signal,
      to,
      orderId,
      extensionText,
    });
    await TUIChatService.sendCustomMessage({
      to,
      conversationType: TUIChatEngine.TYPES.CONV_C2C,
      payload: {
        data: signal,
        description: "VIDEO_CONSULT",
        extension: extensionText,
      },
    });
  } catch (e) {
    console.error("[MRTC][PC] sendVideoSignal error", signal, e);
  }
}


  function getPeerImUserId(orderParam) {
    const order = orderParam || currentOrder.value || {};
    // 严格按订单号作为患者 IM userId，不再回退到其他标识，避免多订单串线
    if (order && order.orderId) {
      return order.orderId;
    }
    console.error("[MRTC] 无法获取视频问诊对应的订单号，信令发送已中止", order);
    return "";
  }

  // ========== 全局信令监听（解决医生切换页面后收不到患者拒绝等信令的问题） ==========
  // 用于去重，防止同一条消息多次处理
  const lastHandledGlobalSignalId = ref("");
  let globalWatchRegistered = false;

  /**
   * 全局处理视频问诊信令消息
   * @param {Object} message - TIM 消息对象
   */
  function handleGlobalVideoSignal(message) {
    // 只处理 TIMCustomElem 且 description 为 VIDEO_CONSULT 的消息
    if (
      !message ||
      message.type !== "TIMCustomElem" ||
      !message.payload ||
      message.payload.description !== "VIDEO_CONSULT"
    ) {
      return;
    }
    // 消息去重
    const msgId = message && message.ID ? String(message.ID) : "";
    if (msgId && lastHandledGlobalSignalId.value === msgId) return;
    if (msgId) lastHandledGlobalSignalId.value = msgId;
    // 只处理来自对端（患者）的消息
    const isIncoming = !!(message && message.flow === "in");
    if (!isIncoming) return;

    const signal = message.payload.data;
    console.log("[VIDEO][PC][GLOBAL] handleGlobalVideoSignal", {
      signal,
      msgId,
      status: status.value,
      activeOrderId: activeOrderId.value,
    });

    if (
      signal === VIDEO_SIGNAL.HANGUP ||
      signal === VIDEO_SIGNAL.CANCEL ||
      signal === VIDEO_SIGNAL.REJECT
    ) {
      const leaveOptions =
        signal === VIDEO_SIGNAL.HANGUP
          ? { skipSendSignal: false, reason: "peer_hangup" }
          : signal === VIDEO_SIGNAL.REJECT
          ? { skipSendSignal: false, reason: "peer_reject" }
          : { skipSendSignal: true, reason: "peer_cancel" };
      if (status.value !== "idle") {
        console.log("[VIDEO][PC][GLOBAL] signal triggers leaveRoom", {
          signal,
          status: status.value,
        });
        leaveRoom(leaveOptions);
      }
      // 无论当前房间状态如何，收到结束类信令都应关闭视频弹窗
      closeUI();
      if (signal === VIDEO_SIGNAL.REJECT) {
        ElMessage.warning("患者已拒绝本次视频问诊");
      }
    } else if (signal === VIDEO_SIGNAL.BUSY) {
      if (status.value !== "idle") {
        console.log("[VIDEO][PC][GLOBAL] BUSY signal triggers leaveRoom", {
          status: status.value,
        });
        leaveRoom({ skipSendSignal: true, reason: "peer_busy" });
      }
      closeUI();
      ElMessage.warning("患者当前正在进行其他视频问诊，请稍后再试");
    }
  }

  /**
   * TIM SDK 原生消息接收回调（用于全局监听所有 C2C 消息）
   * @param {Object} event - TIM SDK MESSAGE_RECEIVED 事件对象
   */
  function onTIMMessageReceived(event) {
    const messageList = event && event.data ? event.data : [];
    if (!Array.isArray(messageList) || !messageList.length) return;
    // 处理所有收到的消息（可能一次收到多条）
    for (const message of messageList) {
      handleGlobalVideoSignal(message);
    }
  }

  /**
   * 启动全局视频信令监听（在 App.vue onMounted 中调用）
   * 使用 TIM SDK 原生 MESSAGE_RECEIVED 事件，确保在任何页面都能收到消息
   */
  function startGlobalSignalWatch() {
    if (globalWatchRegistered) {
      console.log("[VIDEO][PC][GLOBAL] globalSignalWatch already registered, skip");
      return;
    }
    const context = TUILogin.getContext();
    const chat = context && context.chat;
    if (!chat) {
      console.warn("[VIDEO][PC][GLOBAL] startGlobalSignalWatch: chat instance not available yet");
      return;
    }
    // TIM SDK Web 版本的事件名是 TIM.EVENT.MESSAGE_RECEIVED
    // chat 实例应该有 on 方法，事件名通常是全大写或者驼峰式
    // 尝试使用腾讯云 IM Web SDK 的标准事件名
    const TIM_EVENT = chat.TIM?.EVENT || {};
    const messageReceivedEvent = TIM_EVENT.MESSAGE_RECEIVED || "onMessageReceived";
    console.log("[VIDEO][PC][GLOBAL] startGlobalSignalWatch using event:", messageReceivedEvent, "TIM_EVENT:", TIM_EVENT);
    // 使用 TIM SDK 原生事件监听所有收到的消息
    chat.on(messageReceivedEvent, onTIMMessageReceived);
    globalWatchRegistered = true;
  }

  /**
   * 停止全局视频信令监听（在 App.vue onUnmounted 中调用）
   */
  function stopGlobalSignalWatch() {
    if (!globalWatchRegistered) return;
    const context = TUILogin.getContext();
    const chat = context && context.chat;
    if (!chat) {
      globalWatchRegistered = false;
      return;
    }
    const TIM_EVENT = chat.TIM?.EVENT || {};
    const messageReceivedEvent = TIM_EVENT.MESSAGE_RECEIVED || "onMessageReceived";
    console.log("[VIDEO][PC][GLOBAL] stopGlobalSignalWatch");
    chat.off(messageReceivedEvent, onTIMMessageReceived);
    globalWatchRegistered = false;
  }

  return {
    status,
    activeOrderId,
    currentOrder,
    uiVisible,
    uiOrder,
    roomId,
    rtoken,
    remoteSids,
    localAudioEnabled,
    localVideoEnabled,
    errorMessage,
    setDomIds,
    openUI,
    closeUI,
    start,
    leaveRoom,
    toggleAudio,
    toggleVideo,
    getCallDurationText,
    // 全局信令监听
    startGlobalSignalWatch,
    stopGlobalSignalWatch,
  };
});
