import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:laki_icu_app/enums/video_stream_mode_enum.dart';
import 'package:laki_icu_app/models/bo/cabin_detail_response.dart';
import 'package:laki_icu_app/models/bo/monitoring_bo.dart';
import 'package:laki_icu_app/models/bo/camera_alarm_bo.dart';
import 'package:laki_icu_app/services/cabin_service.dart';
import 'package:laki_icu_app/services/monitoring_service.dart';
import 'package:laki_icu_app/services/p2p_video_service.dart';
import 'package:laki_icu_app/services/webrtc_service.dart';
import 'package:laki_icu_app/models/bo/bluetooth_read_model.dart';
import 'package:laki_icu_app/utils/bluetooth/index.dart';
import 'package:laki_icu_app/utils/event_bus.dart';
import 'package:laki_icu_app/utils/logger.dart';
import 'package:laki_icu_app/utils/mqtt/index.dart';
import 'package:laki_icu_app/utils/storage/storage_service.dart';

import 'monitoring_index_state.dart';

/// 设备凭据 —— 从舱详情接口获取 cameraSn 和 wifiPwd

class MonitoringIndexCubit extends Cubit<MonitoringIndexState> {
  final MonitoringService _monitoringService;
  final CabinService _cabinService;
  final WebrtcService _webrtcService;
  final P2pVideoService _p2pVideoService;
  final StorageService _storageService;
  final BleBluetoothManager _bluetoothManager;
  final McuReportFrameDecoder _mcuReportDecoder;
  LakiMqttClient? _mqttClient;

  StreamSubscription<WebrtcConnectionState>? _webrtcStateSub;
  StreamSubscription<P2pServiceState>? _p2pStateSub;
  StreamSubscription<List<BluetoothScanDevice>>? _bluetoothScanSub;
  StreamSubscription<BluetoothConnectionStatus>? _bluetoothStateSub;
  StreamSubscription<String>? _bluetoothMessageSub;
  StreamSubscription<BluetoothDataPacket>? _bluetoothDataSub;
  StreamSubscription<BluetoothReadInfoChangedEvent>? _bluetoothReadInfoSub;
  StreamSubscription<McuReportChangedEvent>? _mcuReportChangedSub;
  StreamSubscription<LakiMqttConnectionState>? _mqttStateSub;
  StreamSubscription<LakiMqttMessage>? _mqttMessageSub;
  StreamSubscription<String>? _mqttErrorSub;
  Timer? _bluetoothReconnectTimer;
  Timer? _mqttReconnectTimer;
  bool _isAutoReconnectEnabled = false;
  bool _isAutoConnectingBluetooth = false;
  bool _isManualUnbindingBluetooth = false;
  int _bluetoothReconnectAttempts = 0;
  bool _isMqttReconnectEnabled = false;
  bool _isMqttConnecting = false;
  String? _mqttDeviceSn;

  /// 已收到的摄像头告警 eventId 集合，用于去重
  final Set<String> _seenAlarmEventIds = {};

  /// 记录已请求过舱详情的 SN，避免每个 BLE 数据帧都重复请求接口
  String? _lastFetchedCabinSn;
  bool _isFetchingCabinDetail = false;

  /// 连接代际计数器 —— 每次切换模式时 +1，
  /// 防止旧连接的异步结果污染当前模式的状态。
  int _switchGen = 0;

  MonitoringIndexCubit()
      : _monitoringService = MonitoringService(),
        _cabinService = CabinService(),
        _webrtcService = WebrtcService(),
        _p2pVideoService = P2pVideoService(),
        _storageService = StorageService(),
        _bluetoothManager = appBluetoothManager,
        _mcuReportDecoder = McuReportFrameDecoder(),
        super(const MonitoringIndexState()) {
    _listenWebrtcState();
    _listenP2pState();
    _listenBluetoothState();
    _listenBluetoothReadInfo();
    _listenMcuReportChanged();
    _loadBoundBluetoothDevice();
    loadData();
  }

  /// 监听 WebRTC 连接状态变化并同步到 State
  void _listenWebrtcState() {
    _webrtcStateSub = _webrtcService.connectionStateStream.listen((state) {
      if (isClosed) return;
      emit(this.state.copyWith(videoConnectionState: state));
    });
  }

  /// 监听 P2P 视频服务状态变化并同步到 State
  void _listenP2pState() {
    _p2pStateSub = _p2pVideoService.stateStream.listen((state) {
      if (isClosed) return;
      emit(this.state.copyWith(
            isP2pConnected: state == P2pServiceState.connected,
          ));
    });
  }

  /// 监听蓝牙扫描、连接和数据状态
  void _listenBluetoothState() {
    _bluetoothScanSub = _bluetoothManager.scanDevicesStream.listen((devices) {
      if (isClosed) return;
      emit(state.copyWith(bluetoothDevices: devices));
    });

    _bluetoothStateSub = _bluetoothManager.stateStream.listen((status) {
      if (isClosed) return;
      emit(state.copyWith(bluetoothConnectionStatus: status));
      if (status == BluetoothConnectionStatus.disconnected) {
        _scheduleBoundBluetoothReconnect();
      }
    });

    _bluetoothMessageSub = _bluetoothManager.messageStream.listen((message) {
      if (isClosed) return;
      emit(state.copyWith(bluetoothMessage: message));
    });

    _bluetoothDataSub = _bluetoothManager.dataStream.listen((packet) async {
      if (isClosed) return;
      try {
        Log.warn(
          '🔥🔥🔥 BLE_READ_DEBUG index_packet len=${packet.bytes.length} '
          'hex=${packet.rawHex}',
        );
        final reports = _mcuReportDecoder.addBytes(packet.bytes);
        final latestReport = reports.isEmpty ? null : reports.last;
        Log.warn(
          '🔥🔥🔥 BLE_READ_DEBUG index_parse reports=${reports.length} '
          'buffered=${_mcuReportDecoder.bufferedBytes} '
          'bufferHex=${_mcuReportDecoder.bufferedHexPreview} '
          'hasLatest=${latestReport != null}',
        );
        if (latestReport != null) {
          Log.warn(
            '🔥🔥🔥 BLE_READ_DEBUG index_report '
            'frameLength=${latestReport.length} '
            'temp=${latestReport.mainTemperature} '
            'tempSet=${latestReport.temperatureSetting} '
            'humidity=${latestReport.humidity} '
            'humiditySet=${latestReport.humiditySetting} '
            'o2=${latestReport.oxygenConcentration} '
            'o2Set=${latestReport.oxygenSetting} '
            'co2=${latestReport.co2Measurement} '
            'co2Set=${latestReport.co2Setting} '
            'sn=${latestReport.sn.isEmpty ? '<EMPTY>' : latestReport.sn}',
          );
          final reportSn = _appendBluetoothSideToSn(latestReport.sn);
          final bluetoothReadInfo = BluetoothReadModel.fromMcuReport(
            latestReport,
          ).copyWith(sn: reportSn.isEmpty ? null : reportSn);
          if (bluetoothReadInfo.hasSn) {
            await _storageService.saveBluetoothReadSn(bluetoothReadInfo.sn!);
            await _connectMqttForDeviceSn(bluetoothReadInfo.sn!);
            await _fetchCabinDetailAndConnectVideo(bluetoothReadInfo.sn!);
          }
          eventBus.emit(
            BluetoothReadInfoChangedEvent(bluetoothReadInfo),
          );
          _publishMqttMonitorReport(latestReport);
          if (isClosed) return;
        }
        emit(state.copyWith(
          latestBluetoothRawHex: packet.rawHex,
          latestMcuReport: latestReport,
          metrics: latestReport == null
              ? state.metrics
              : _metricsFromBluetoothReport(latestReport),
          bluetoothMessage: latestReport == null
              ? state.bluetoothMessage
              : '蓝牙数据解析成功: ${latestReport.sn.isEmpty ? latestReport.head : latestReport.sn}',
        ));
      } catch (e) {
        emit(state.copyWith(
          latestBluetoothRawHex: packet.rawHex,
          bluetoothMessage: '蓝牙数据解析失败: $e',
        ));
      }
    });
  }

  void _listenMcuReportChanged() {
    _mcuReportChangedSub = eventBus.on<McuReportChangedEvent>().listen((event) {
      if (isClosed) return;
      final metrics = _metricsFromBluetoothReport(event.report);
      Log.warn(
        '🔥🔥🔥 BLE_READ_DEBUG index_metrics_from_report '
        '${_metricsLogText(metrics)}',
      );
      emit(state.copyWith(
        latestMcuReport: event.report,
        metrics: metrics,
      ));
    });
  }

  void _listenBluetoothReadInfo() {
    _bluetoothReadInfoSub =
        eventBus.on<BluetoothReadInfoChangedEvent>().listen((event) {
      if (isClosed) return;
      final metrics = _metricsFromBluetoothReadInfo(event.info);
      if (metrics == null) {
        Log.warn(
          '🔥🔥🔥 BLE_READ_DEBUG index_read_info_no_metrics '
          'sn=${event.info.sn} temp=${event.info.mainTemperature} '
          'humidity=${event.info.humidity} o2=${event.info.oxygenConcentration} '
          'co2=${event.info.co2Measurement}',
        );
        return;
      }
      Log.warn(
        '🔥🔥🔥 BLE_READ_DEBUG index_metrics_from_read_info '
        '${_metricsLogText(metrics)}',
      );
      emit(state.copyWith(metrics: metrics));
    });
  }

  String _appendBluetoothSideToSn(String sn) {
    final cleanSn = sn.trim();
    if (cleanSn.isEmpty) return '';

    final deviceName = state.boundBluetoothDeviceName?.trim();
    if (deviceName == null || deviceName.isEmpty) return cleanSn;

    final side = deviceName[deviceName.length - 1].toUpperCase();
    if (side != 'L' && side != 'R') return cleanSn;

    return cleanSn.toUpperCase().endsWith(side) ? cleanSn : '$cleanSn$side';
  }

  Future<void> _loadBoundBluetoothDevice() async {
    final boundDevice = await _storageService.getBoundBluetoothDevice();
    if (isClosed) return;
    final deviceId = boundDevice['deviceId'];
    final bluetoothReadInfo = await _storageService.getBluetoothReadInfo();
    if (isClosed) return;
    debugPrint(
      '🔥🔥🔥 BLE_CONNECT_DEBUG load_bound_device '
      'deviceId=$deviceId deviceName=${boundDevice['deviceName']} '
      'hasSn=${bluetoothReadInfo.hasSn}',
    );
    emit(state.copyWith(
      boundBluetoothDeviceId:
          deviceId == null || deviceId.isEmpty ? null : deviceId,
      boundBluetoothDeviceName: boundDevice['deviceName'],
    ));
    if (bluetoothReadInfo.hasSn) {
      final sn = _appendBluetoothSideToSn(bluetoothReadInfo.sn!);
      if (sn.isNotEmpty) {
        if (sn != bluetoothReadInfo.sn) {
          await _storageService.saveBluetoothReadSn(sn);
          eventBus.emit(
            BluetoothReadInfoChangedEvent(bluetoothReadInfo.copyWith(sn: sn)),
          );
        }
        _connectMqttForDeviceSn(sn);
        _fetchCabinDetailAndConnectVideo(sn);
      }
    }
    if (deviceId != null && deviceId.isNotEmpty) {
      _isAutoReconnectEnabled = true;
      _connectBoundBluetoothDevice();
    }
  }

  Future<void> loadData() async {
    emit(state.copyWith(
      status: MonitoringIndexStatus.loading,
      isLoading: true,
      error: null,
    ));

    try {
      // final metrics = await _monitoringService.getMetrics();
      // if (isClosed) return;
      final patientInfo = await _monitoringService.getPatientInfo();
      if (isClosed) return;
      final alerts = await _monitoringService.getAlerts();
      if (isClosed) return;

      emit(state.copyWith(
        status: MonitoringIndexStatus.success,
        isLoading: false,
        patientInfo: patientInfo,
        alerts: alerts,
        error: null,
      ));

      // 数据加载完成后，按当前模式初始化视频连接
      _initVideoByMode();
    } catch (e) {
      if (isClosed) return;
      emit(state.copyWith(
        status: MonitoringIndexStatus.failure,
        isLoading: false,
        error: e.toString(),
      ));
    }
  }

  /// 依据当前 [VideoStreamMode] 启动对应的视频连接
  void _initVideoByMode() {
    if (state.videoStreamMode == VideoStreamMode.p2p) {
      _initP2p();
    } else {
      _initWebrtc();
    }
  }

  /// 初始化 WebRTC 视频连接
  ///
  /// 使用从舱详情接口获取的 [cabinCameraSn] 和 [cabinWifiPwd]，
  /// 凭证未就绪时跳过连接。
  Future<void> _initWebrtc() async {
    final cameraSn = state.cabinCameraSn;
    final wifiPwd = state.cabinWifiPwd;

    if (cameraSn == null || cameraSn.isEmpty ||
        wifiPwd == null || wifiPwd.isEmpty) {
      debugPrint('[MonitoringIndexCubit] WebRTC 凭证未就绪，跳过连接');
      return;
    }

    await _webrtcService.connect(
      deviceNo: cameraSn,
      password: wifiPwd,
    );
  }

  /// 手动重试 WebRTC
  Future<void> retryWebrtc() async {
    await _initWebrtc();
  }

  /// 初始化 P2P 视频连接
  ///
  /// 使用从舱详情接口获取的 [cabinCameraSn] 和 [cabinWifiPwd]，
  /// 凭证未就绪时跳过连接。
  Future<void> _initP2p() async {
    final cameraSn = state.cabinCameraSn;
    final wifiPwd = state.cabinWifiPwd;

    if (cameraSn == null || cameraSn.isEmpty ||
        wifiPwd == null || wifiPwd.isEmpty) {
      debugPrint('[MonitoringIndexCubit] P2P 凭证未就绪，跳过连接');
      return;
    }

    await _p2pVideoService.connect(
      deviceId: cameraSn,
      username: 'admin',
      password: wifiPwd,
    );
  }

  /// 根据舱 SN 获取舱详情，并将摄像头凭证和宠物信息同步到 State
  ///
  /// 成功后自动触发视频连接（按当前 [VideoStreamMode] 方式）。
  /// 同一 SN 只会请求一次，避免 BLE 数据帧高频回调导致重复调用接口。
  Future<void> _fetchCabinDetailAndConnectVideo(String carbinSn) async {
    // 相同 SN 已请求过则跳过，避免每个 BLE 数据帧都触发一次接口调用
    if (_lastFetchedCabinSn == carbinSn) return;
    // 上一次请求尚未完成则跳过
    if (_isFetchingCabinDetail) return;

    _isFetchingCabinDetail = true;
    _lastFetchedCabinSn = carbinSn;

    try {
      final detail = await _cabinService.getCabinDetail(carbinSn);
      if (isClosed) return;

      emit(state.copyWith(
        cabinCameraSn: detail.cameraSn,
        cabinWifiPwd: detail.wifiPwd,
        patientInfo: detail.petInfo != null
            ? _mapPetInfoToPatientInfo(detail.petInfo!)
            : state.patientInfo,
      ));

      // 凭证就绪后发起视频连接
      _initVideoByMode();
    } catch (e) {
      if (isClosed) return;
      debugPrint('[MonitoringIndexCubit] 获取舱详情失败: $e');
    } finally {
      _isFetchingCabinDetail = false;
    }
  }

  /// 将 [PetInfoResponse] 映射为 [PatientInfoBO]
  PatientInfoBO _mapPetInfoToPatientInfo(PetInfoResponse pet) {
    return PatientInfoBO(
      name: (pet.petName ?? pet.name) ?? '',
      type: (pet.petType ?? pet.type) ?? '',
      phone: pet.ownerPhone ?? '',
      breed: pet.breedTag ?? '',
      doctor: '',
      avatarUrl: pet.avatar ?? '',
      disease: pet.disease ?? '',
      checkInDate: '',
      careLevel: '',
    );
  }

  // ==================== 模式切换 ====================

  /// 切换视频传输模式
  ///
  /// 先停止当前模式 → 更新状态 → 启动新模式。
  /// 使用 [_switchGen] 代际计数器防止旧连接的异步结果污染当前状态。
  Future<void> switchStreamMode(VideoStreamMode mode) async {
    if (state.isSwitchingMode) return;
    if (state.videoStreamMode == mode) return;

    final currentGen = ++_switchGen;
    debugPrint(
        '[MonitoringIndexCubit] 切换模式: ${state.videoStreamMode} → $mode (gen=$currentGen)');

    // 1. 标记切换中
    emit(state.copyWith(isSwitchingMode: true));

    // 2. 停止当前模式（用 stop 停连接，保留 StreamController 可复用）
    if (state.videoStreamMode == VideoStreamMode.p2p) {
      await _p2pVideoService.stop();
    } else {
      await _webrtcService.stop();
    }

    // 3. 竞态检查：如果在清理期间又触发了一次切换则放弃本次
    if (currentGen != _switchGen || isClosed) return;

    // 4. 切换到新模式
    emit(state.copyWith(
      videoStreamMode: mode,
      isSwitchingMode: false,
      isP2pConnected: false,
      videoConnectionState: WebrtcConnectionState.disconnected,
    ));

    // 5. 启动新模式视频连接
    if (mode == VideoStreamMode.p2p) {
      await _initP2p();
    } else {
      await _initWebrtc();
    }
  }

  /// 手动重试当前模式的视频连接
  Future<void> retryCurrentMode() async {
    if (state.videoStreamMode == VideoStreamMode.p2p) {
      await _p2pVideoService.stop();
      await _initP2p();
    } else {
      await _webrtcService.stop();
      await _initWebrtc();
    }
  }

  Future<void> refreshData() async {
    try {
      // final metrics = await _monitoringService.getMetrics();
      // if (isClosed) return;
      final patientInfo = await _monitoringService.getPatientInfo();
      if (isClosed) return;
      final alerts = await _monitoringService.getAlerts();
      if (isClosed) return;

      emit(state.copyWith(
        status: MonitoringIndexStatus.success,
        patientInfo: patientInfo,
        alerts: alerts,
        error: null,
      ));
    } catch (e) {
      if (isClosed) return;
      emit(state.copyWith(
        status: MonitoringIndexStatus.failure,
        error: e.toString(),
      ));
    }
  }

  // ==================== 蓝牙扫描/绑定 ====================

  Future<void> scanBluetoothDevices() async {
    if (state.isBluetoothScanning) return;

    emit(state.copyWith(
      isBluetoothScanning: true,
      bluetoothDevices: const [],
      bluetoothMessage: '开始扫描蓝牙设备',
    ));

    try {
      await _bluetoothManager.scanForDevices(
        namePrefixes: const [],
        timeout: const Duration(seconds: 10),
      );
    } catch (e) {
      if (isClosed) return;
      emit(state.copyWith(bluetoothMessage: '蓝牙扫描失败: $e'));
    } finally {
      if (!isClosed) {
        emit(state.copyWith(isBluetoothScanning: false));
      }
    }
  }

  Future<void> bindBluetoothDevice(BluetoothScanDevice device) async {
    if (state.isBluetoothBinding) return;

    _mcuReportDecoder.clear();
    emit(state.copyWith(
      isBluetoothBinding: true,
      bindingBluetoothDeviceId: device.remoteId,
      bluetoothMessage: '正在绑定蓝牙设备: ${device.name}',
    ));

    try {
      await _bluetoothManager.connectToDevice(device);
      if (!_bluetoothManager.isConnected) {
        throw StateError('蓝牙连接未就绪');
      }
      await _storageService.saveBoundBluetoothDevice(
        deviceId: device.remoteId,
        deviceName: device.name,
      );
      _isAutoReconnectEnabled = true;
      if (isClosed) return;
      emit(state.copyWith(
        isBluetoothBinding: false,
        bindingBluetoothDeviceId: '',
        boundBluetoothDeviceId: device.remoteId,
        boundBluetoothDeviceName: device.name,
        bluetoothMessage: '蓝牙设备已绑定: ${device.name}',
      ));
    } catch (e) {
      if (isClosed) return;
      emit(state.copyWith(
        isBluetoothBinding: false,
        bindingBluetoothDeviceId: '',
        bluetoothMessage: '蓝牙绑定失败: $e',
      ));
    }
  }

  Future<void> unbindBluetoothDevice() async {
    _isManualUnbindingBluetooth = true;
    _isAutoReconnectEnabled = false;
    _cancelBluetoothReconnect();
    await _bluetoothManager.disconnect();
    await _storageService.deleteBoundBluetoothDevice();
    await _storageService.deleteBluetoothReadInfo();
    _isMqttReconnectEnabled = false;
    _mqttDeviceSn = null;
    _lastFetchedCabinSn = null;
    await _disposeMqttClient();
    eventBus.emit(const BluetoothReadInfoClearedEvent());
    _mcuReportDecoder.clear();
    if (isClosed) return;
    emit(state.copyWith(
      clearBoundBluetoothDevice: true,
      clearLatestMcuReport: true,
      clearMqttDeviceSn: true,
      mqttConnectionState: LakiMqttConnectionState.disconnected,
      mqttMessage: 'MQTT 已断开',
      bluetoothMessage: '蓝牙设备已解绑',
      latestBluetoothRawHex: '',
      cameraAlerts: const [],
    ));
    _seenAlarmEventIds.clear();
    _isManualUnbindingBluetooth = false;
  }

  void _scheduleBoundBluetoothReconnect() {
    if (!_isAutoReconnectEnabled ||
        _isManualUnbindingBluetooth ||
        _isAutoConnectingBluetooth ||
        state.boundBluetoothDeviceId == null ||
        state.boundBluetoothDeviceId!.isEmpty ||
        state.isBluetoothBinding) {
      return;
    }

    final delaySeconds = ((_bluetoothReconnectAttempts + 1) * 8).clamp(8, 40);
    debugPrint(
      '🔥🔥🔥 BLE_CONNECT_DEBUG auto_reconnect_scheduled '
      'delay=${delaySeconds}s attempts=$_bluetoothReconnectAttempts',
    );
    _bluetoothReconnectTimer ??= Timer(
      Duration(seconds: delaySeconds),
      () {
        _bluetoothReconnectTimer = null;
        _connectBoundBluetoothDevice();
      },
    );
  }

  Future<void> _connectBoundBluetoothDevice() async {
    final deviceId = state.boundBluetoothDeviceId;
    if (!_isAutoReconnectEnabled ||
        _isManualUnbindingBluetooth ||
        _isAutoConnectingBluetooth ||
        deviceId == null ||
        deviceId.isEmpty ||
        _bluetoothManager.isConnected) {
      return;
    }

    _cancelBluetoothReconnect();
    _isAutoConnectingBluetooth = true;
    if (!isClosed) {
      emit(state.copyWith(bluetoothMessage: '正在自动连接蓝牙设备'));
    }
    debugPrint(
      '🔥🔥🔥 BLE_CONNECT_DEBUG auto_connect_start '
      'deviceId=$deviceId isConnected=${_bluetoothManager.isConnected}',
    );

    try {
      await _bluetoothManager.connectToDeviceId(deviceId, prescan: true);
      debugPrint(
        '🔥🔥🔥 BLE_CONNECT_DEBUG auto_connect_success '
        'deviceId=$deviceId isConnected=${_bluetoothManager.isConnected}',
      );
      if (!isClosed) {
        emit(state.copyWith(
          bluetoothMessage: _bluetoothManager.isConnected
              ? '蓝牙设备已自动连接'
              : '蓝牙自动连接未就绪，等待重试',
        ));
      }
      if (_bluetoothManager.isConnected) {
        _bluetoothReconnectAttempts = 0;
      }
    } catch (e) {
      _bluetoothReconnectAttempts++;
      debugPrint(
          '🔥🔥🔥 BLE_CONNECT_DEBUG auto_connect_error deviceId=$deviceId error=$e');
      if (!isClosed) {
        emit(state.copyWith(bluetoothMessage: '蓝牙自动连接失败: $e'));
      }
    } finally {
      _isAutoConnectingBluetooth = false;
      if (!isClosed && !_bluetoothManager.isConnected) {
        _scheduleBoundBluetoothReconnect();
      }
    }
  }

  void _cancelBluetoothReconnect() {
    _bluetoothReconnectTimer?.cancel();
    _bluetoothReconnectTimer = null;
  }

  // ==================== MQTT 自动连接/上报 ====================

  Future<void> _connectMqttForDeviceSn(String deviceSn) async {
    if (deviceSn.isEmpty || isClosed) return;
    if (_isMqttConnecting) return;
    if (_mqttDeviceSn == deviceSn && (_mqttClient?.isConnected ?? false)) {
      return;
    }

    _isMqttReconnectEnabled = true;
    _isMqttConnecting = true;
    _cancelMqttReconnect();
    var shouldScheduleReconnect = false;

    try {
      if (_mqttDeviceSn != deviceSn) {
        await _disposeMqttClient();
        _mqttDeviceSn = deviceSn;
        _mqttClient = LakiMqttClient(
          config: LakiMqttConfig.tencentTdmq(
            deviceSn: deviceSn,
            logging: kDebugMode,
          ),
        );
        _listenMqttState(_mqttClient!);
      }

      if (!isClosed) {
        emit(state.copyWith(
          mqttConnectionState: LakiMqttConnectionState.connecting,
          mqttMessage: '正在连接 MQTT: $deviceSn',
          mqttDeviceSn: deviceSn,
        ));
      }

      await _mqttClient!.connect();
      _mqttClient!
        ..subscribeIcuCommands(deviceSn)
        ..subscribePetBind(deviceSn)
        ..subscribe(LakiMqttTopics.cameraTo(deviceSn));

      if (!isClosed) {
        emit(state.copyWith(
          mqttConnectionState: LakiMqttConnectionState.connected,
          mqttMessage: 'MQTT 已连接并订阅: $deviceSn',
          mqttDeviceSn: deviceSn,
        ));
      }
    } catch (e) {
      shouldScheduleReconnect = true;
      if (!isClosed) {
        emit(state.copyWith(
          mqttConnectionState: LakiMqttConnectionState.failed,
          mqttMessage: 'MQTT 自动连接失败: $e',
          mqttDeviceSn: deviceSn,
        ));
      }
    } finally {
      _isMqttConnecting = false;
      if (shouldScheduleReconnect) {
        _scheduleMqttReconnect();
      }
    }
  }

  void _listenMqttState(LakiMqttClient client) {
    _mqttStateSub = client.stateStream.listen((status) {
      if (isClosed) return;
      emit(state.copyWith(mqttConnectionState: status));
      if (status == LakiMqttConnectionState.disconnected &&
          _isMqttReconnectEnabled) {
        _scheduleMqttReconnect();
      }
    });

    _mqttMessageSub = client.messageStream.listen((message) {
      if (isClosed) return;
      final json = message.json;
      if (json != null &&
          message.topic == LakiMqttTopics.cameraTo(_mqttDeviceSn ?? '')) {
        _handleCameraAlarmMessage(json);
        return;
      }
      emit(state.copyWith(
        mqttMessage: 'MQTT 收到 ${message.topic}: ${message.payload}',
      ));
    });

    _mqttErrorSub = client.errorStream.listen((message) {
      if (isClosed) return;
      emit(state.copyWith(mqttMessage: message));
    });
  }

  /// 处理摄像头告警消息（与 Android qisuanfa_0621 逻辑保持一致）
  ///
  /// 仅处理 type == "abnormal_behavior" 的消息，
  /// 按 behaviorCode 映射标题：2→异常液体、3→检测到排泄物、其他→未知异常
  void _handleCameraAlarmMessage(Map<String, dynamic> json) {
    if (json['type'] != 'abnormal_behavior') return;

    final alarm = CameraAlarmBO.fromJson(json);

    // 按 eventId 去重
    if (_seenAlarmEventIds.contains(alarm.eventId)) return;
    _seenAlarmEventIds.add(alarm.eventId);

    final timeStr = _formatTimestamp(alarm.timestamp);
    final title = alarm.titleByBehaviorCode;

    final alert = AlertInfoBO(
      title: title,
      description: title,
      time: timeStr,
      status: '待处理',
    );

    emit(state.copyWith(
      cameraAlerts: [alert, ...state.cameraAlerts],
    ));
  }

  /// 毫秒时间戳 → HH:mm:ss 格式（与 Android 一致）
  String _formatTimestamp(int milliseconds) {
    final dateTime =
        DateTime.fromMillisecondsSinceEpoch(milliseconds, isUtc: true);
    final local = dateTime.toLocal();
    return '${local.hour.toString().padLeft(2, '0')}:'
        '${local.minute.toString().padLeft(2, '0')}:'
        '${local.second.toString().padLeft(2, '0')}';
  }

  void _publishMqttMonitorReport(McuReport report) {
    final client = _mqttClient;
    if (client == null || !client.isConnected || report.sn.isEmpty) {
      return;
    }

    try {
      client.publishMonitorReport(report);
      if (!isClosed) {
        emit(state.copyWith(mqttMessage: 'MQTT 已上报: ${report.sn}'));
      }
    } catch (e) {
      if (!isClosed) {
        emit(state.copyWith(mqttMessage: 'MQTT 上报失败: $e'));
      }
    }
  }

  void _scheduleMqttReconnect() {
    if (!_isMqttReconnectEnabled ||
        _isMqttConnecting ||
        _mqttDeviceSn == null ||
        _mqttDeviceSn!.isEmpty) {
      return;
    }

    _mqttReconnectTimer ??= Timer(
      const Duration(seconds: 5),
      () {
        _mqttReconnectTimer = null;
        final deviceSn = _mqttDeviceSn;
        if (deviceSn != null && deviceSn.isNotEmpty) {
          _connectMqttForDeviceSn(deviceSn);
        }
      },
    );
  }

  void _cancelMqttReconnect() {
    _mqttReconnectTimer?.cancel();
    _mqttReconnectTimer = null;
  }

  Future<void> _disposeMqttClient() async {
    _cancelMqttReconnect();
    await _mqttStateSub?.cancel();
    await _mqttMessageSub?.cancel();
    await _mqttErrorSub?.cancel();
    _mqttStateSub = null;
    _mqttMessageSub = null;
    _mqttErrorSub = null;
    await _mqttClient?.dispose();
    _mqttClient = null;
  }

  List<MonitoringMetricBO> _metricsFromBluetoothReport(
    McuReport report, {
    List<MonitoringMetricBO>? baseMetrics,
  }) {
    final metrics = baseMetrics ?? state.metrics;
    final replacements = <String, MonitoringMetricBO>{
      '舱内温度': _replaceMetricValue(
        '舱内温度',
        _formatReportTemperatureOrKeep(report.mainTemperature, '舱内温度'),
        fallbackUnit: '℃',
        baseMetrics: metrics,
      ),
      '舱内湿度': _replaceMetricValue(
        '舱内湿度',
        _formatReportIntOrKeep(report.humidity, '舱内湿度'),
        fallbackUnit: 'RH',
        baseMetrics: metrics,
      ),
      '氧气浓度': _replaceMetricValue(
        '氧气浓度',
        _formatReportIntOrKeep(report.oxygenConcentration, '氧气浓度'),
        fallbackUnit: '%',
        baseMetrics: metrics,
      ),
      '二氧化碳': _replaceMetricValue(
        '二氧化碳',
        _formatReportIntOrKeep(report.co2Measurement, '二氧化碳'),
        fallbackUnit: 'ppm',
        baseMetrics: metrics,
      ),
    };

    if (metrics.isEmpty) {
      return replacements.values.toList();
    }

    return [
      for (final metric in metrics) replacements[metric.label] ?? metric,
    ];
  }

  List<MonitoringMetricBO>? _metricsFromBluetoothReadInfo(
    BluetoothReadModel info,
  ) {
    final hasMetric = info.mainTemperature != null ||
        info.humidity != null ||
        info.oxygenConcentration != null ||
        info.co2Measurement != null;
    if (!hasMetric) return null;

    final metrics = state.metrics;
    final replacements = <String, MonitoringMetricBO>{
      '舱内温度': _replaceMetricValue(
        '舱内温度',
        info.mainTemperature == null
            ? _existingMetricValue('舱内温度') ?? '--'
            : _formatReportTemperature(info.mainTemperature!),
        fallbackUnit: '℃',
        baseMetrics: metrics,
      ),
      '舱内湿度': _replaceMetricValue(
        '舱内湿度',
        info.humidity == null
            ? _existingMetricValue('舱内湿度') ?? '--'
            : '${info.humidity}',
        fallbackUnit: 'RH',
        baseMetrics: metrics,
      ),
      '氧气浓度': _replaceMetricValue(
        '氧气浓度',
        info.oxygenConcentration == null
            ? _existingMetricValue('氧气浓度') ?? '--'
            : '${info.oxygenConcentration}',
        fallbackUnit: '%',
        baseMetrics: metrics,
      ),
      '二氧化碳': _replaceMetricValue(
        '二氧化碳',
        info.co2Measurement == null
            ? _existingMetricValue('二氧化碳') ?? '--'
            : '${info.co2Measurement}',
        fallbackUnit: 'ppm',
        baseMetrics: metrics,
      ),
    };

    if (metrics.isEmpty) {
      return replacements.values.toList();
    }

    return [
      for (final metric in metrics) replacements[metric.label] ?? metric,
    ];
  }

  MonitoringMetricBO _replaceMetricValue(
    String label,
    String value, {
    required String fallbackUnit,
    List<MonitoringMetricBO>? baseMetrics,
  }) {
    final metrics = baseMetrics ?? state.metrics;
    MonitoringMetricBO? existing;
    for (final metric in metrics) {
      if (metric.label == label) {
        existing = metric;
        break;
      }
    }
    if (existing == null) {
      return MonitoringMetricBO(
        value: value,
        unit: fallbackUnit,
        label: label,
      );
    }
    return MonitoringMetricBO(
      value: value,
      unit: existing.unit.isEmpty ? fallbackUnit : existing.unit,
      label: existing.label,
      borderColorValue: existing.borderColorValue,
      backgroundColorValue: existing.backgroundColorValue,
    );
  }

  String _formatReportTemperature(double value) {
    if (value.isNaN) return '--';
    final fixed = value.toStringAsFixed(1);
    return fixed.endsWith('.0') ? fixed.substring(0, fixed.length - 2) : fixed;
  }

  String _formatReportTemperatureOrKeep(double value, String label) {
    if (!value.isNaN) return _formatReportTemperature(value);
    return _existingMetricValue(label) ?? '--';
  }

  String _formatReportIntOrKeep(int value, String label) {
    if (value >= 0) return '$value';
    return _existingMetricValue(label) ?? '--';
  }

  String? _existingMetricValue(String label) {
    for (final metric in state.metrics) {
      if (metric.label == label && metric.value != '--') {
        return metric.value;
      }
    }
    return null;
  }

  String _metricsLogText(List<MonitoringMetricBO> metrics) {
    return metrics
        .map((item) => '${item.label}=${item.value}${item.unit}')
        .join(' ');
  }

  /// 清空宠物信息（出舱）
  void clearPatientInfo() {
    if (isClosed) return;
    emit(state.copyWith(clearPatientInfo: true));
  }

  /// 设置测试宠物信息（绑定宠物）
  void setTestPatientInfo() {
    if (isClosed) return;
    const testPatientInfo = PatientInfoBO(
      name: '妮蔻',
      type: '猫',
      phone: '130-1111-3333',
      breed: '长毛三花',
      doctor: '张医生',
      disease: '胃炎',
      checkInDate: '2025-6-15',
      careLevel: '特级',
      avatarUrl: '',
    );
    emit(state.copyWith(patientInfo: testPatientInfo));
  }

  /// 获取 WebRTC 渲染器供 UI 层使用
  WebrtcService get webrtcService => _webrtcService;

  /// 获取 P2P 视频播放控制器供 UI 层使用
  P2pVideoService get p2pVideoService => _p2pVideoService;

  /// 选中告警（用于打开详情弹窗）
  void selectAlert(AlertInfoBO alert) {
    if (isClosed) return;
    emit(state.copyWith(selectedAlert: alert));
  }

  /// 清除选中的告警（用于关闭弹窗）
  void clearSelectedAlert() {
    if (isClosed) return;
    emit(state.copyWith(clearSelectedAlert: true));
  }

  /// 标记告警为已处理
  void markAlertAsProcessed(AlertInfoBO alert) {
    if (isClosed) return;
    final updatedAlerts = state.alerts.map((item) {
      if (item.title == alert.title && item.time == alert.time) {
        return AlertInfoBO(
          title: item.title,
          description: item.description,
          time: item.time,
          status: '已处理',
        );
      }
      return item;
    }).toList();
    emit(state.copyWith(alerts: updatedAlerts, selectedAlert: null));
  }

  /// 标记告警为误报
  void markAlertAsFalse(AlertInfoBO alert) {
    if (isClosed) return;
    final updatedAlerts = state.alerts.map((item) {
      if (item.title == alert.title && item.time == alert.time) {
        return AlertInfoBO(
          title: item.title,
          description: item.description,
          time: item.time,
          status: '误报',
        );
      }
      return item;
    }).toList();
    emit(state.copyWith(alerts: updatedAlerts, selectedAlert: null));
  }

  @override
  Future<void> close() {
    _cancelBluetoothReconnect();
    _isMqttReconnectEnabled = false;
    _cancelMqttReconnect();
    _webrtcStateSub?.cancel();
    _p2pStateSub?.cancel();
    _bluetoothScanSub?.cancel();
    _bluetoothStateSub?.cancel();
    _bluetoothMessageSub?.cancel();
    _bluetoothDataSub?.cancel();
    _bluetoothReadInfoSub?.cancel();
    _mcuReportChangedSub?.cancel();
    _webrtcService.dispose();
    _p2pVideoService.dispose();
    return _disposeMqttClient().then((_) => super.close());
  }
}
