/// 自然写互动课堂手机端应用软件 V1.0
/// BLE蓝牙服务 - 教师端蓝牙连接点阵笔进行移动教学
///
/// 功能说明：
/// 1. BLE设备扫描与发现（按自然写笔设备UUID过滤）
/// 2. GATT连接与特征值订阅（实时接收笔迹坐标数据）
/// 3. 7字节紧凑坐标数据解码（x:16bit, y:16bit, pressure:8bit, timestamp:16bit）
/// 4. 多笔同时连接管理（教师端移动教学最多连接4支笔）
/// 5. 自动重连与连接状态监控
/// 6. 设备电量读取与低电量告警
/// 7. 蓝牙权限检查与引导
/// 8. 笔迹数据缓冲与批量回调

import 'dart:async';
import 'dart:typed_data';

/* ========== BLE协议常量定义 ========== */

/// 自然写点阵笔BLE服务UUID
class WritechBleUuids {
  /// 主服务UUID - 笔迹数据传输
  static const String strokeServiceUuid = '6E400001-B5A3-F393-E0A9-E50E24DCCA9E';
  /// 笔迹数据特征值UUID（Notify模式，笔到手机）
  static const String strokeDataCharUuid = '6E400003-B5A3-F393-E0A9-E50E24DCCA9E';
  /// 命令写入特征值UUID（Write模式，手机到笔）
  static const String commandCharUuid = '6E400002-B5A3-F393-E0A9-E50E24DCCA9E';
  /// 设备信息服务UUID（标准BLE Device Information Service）
  static const String deviceInfoServiceUuid = '0000180A-0000-1000-8000-00805F9B34FB';
  /// 电池服务UUID（标准BLE Battery Service）
  static const String batteryServiceUuid = '0000180F-0000-1000-8000-00805F9B34FB';
  /// 电池电量特征值UUID
  static const String batteryLevelCharUuid = '00002A19-0000-1000-8000-00805F9B34FB';
}

/// BLE笔命令定义
class PenCommand {
  static const int cmdSetMode = 0x01;
  static const int cmdGetStatus = 0x02;
  static const int cmdSyncOffline = 0x03;
  static const int cmdSetName = 0x04;
  static const int cmdStartOta = 0x05;
  static const int cmdReset = 0xFF;
}

/* ========== 数据模型 ========== */

/// BLE笔设备信息
class PenDevice {
  final String deviceId;
  final String name;
  int rssi;
  int batteryLevel;
  String firmwareVersion;
  PenConnectionState state;
  DateTime? lastActiveTime;
  int offlineDataCount;

  PenDevice({
    required this.deviceId,
    required this.name,
    this.rssi = -100,
    this.batteryLevel = -1,
    this.firmwareVersion = '',
    this.state = PenConnectionState.disconnected,
    this.lastActiveTime,
    this.offlineDataCount = 0,
  });
}

/// 笔连接状态枚举
enum PenConnectionState {
  disconnected,
  connecting,
  connected,
  disconnecting,
}

/// 笔迹坐标点（从BLE数据解码后的结构化数据）
class StrokePoint {
  final double x;
  final double y;
  final double pressure;
  final int timestamp;
  final bool isPenDown;

  const StrokePoint({
    required this.x,
    required this.y,
    required this.pressure,
    required this.timestamp,
    required this.isPenDown,
  });

  Map<String, dynamic> toJson() => {
    'x': x, 'y': y,
    'pressure': pressure,
    'timestamp': timestamp,
    'pen_down': isPenDown,
  };
}

/// 笔迹数据回调事件
class StrokeDataEvent {
  final String deviceId;
  final List<StrokePoint> points;
  final int pageId;

  StrokeDataEvent({
    required this.deviceId,
    required this.points,
    required this.pageId,
  });
}

/* ========== BLE服务实现 ========== */

/// BLE蓝牙服务 - 管理点阵笔的蓝牙连接与数据传输
class BleConnectionService {
  /// 已连接或已发现的笔设备列表
  final Map<String, PenDevice> _devices = {};

  /// 笔迹数据流控制器（向上层广播解码后的笔迹坐标）
  final StreamController<StrokeDataEvent> _strokeStreamController =
      StreamController<StrokeDataEvent>.broadcast();

  /// 设备状态变化流
  final StreamController<PenDevice> _deviceStateController =
      StreamController<PenDevice>.broadcast();

  /// 扫描状态
  bool _isScanning = false;

  /// 最大同时连接数（教师移动教学最多4支笔）
  static const int maxConnections = 4;

  /// 自动重连间隔（秒）
  static const int reconnectIntervalSec = 5;

  /// 数据缓冲区大小（累积到一定量后批量回调）
  static const int batchSize = 10;

  /// 设备活跃超时时间（毫秒）
  static const int activeTimeoutMs = 30000;

  /// 低电量告警阈值
  static const int lowBatteryThreshold = 10;

  /// 重连计时器
  final Map<String, Timer> _reconnectTimers = {};

  /// 电量查询计时器
  Timer? _batteryCheckTimer;

  /// 笔迹数据缓冲区（按设备ID分组）
  final Map<String, List<StrokePoint>> _dataBuffers = {};

  /// 外部可订阅的笔迹数据流
  Stream<StrokeDataEvent> get strokeStream => _strokeStreamController.stream;

  /// 外部可订阅的设备状态流
  Stream<PenDevice> get deviceStateStream => _deviceStateController.stream;

  /// 获取当前已连接设备数量
  int get connectedCount =>
      _devices.values.where((d) => d.state == PenConnectionState.connected).length;

  /// 获取所有已发现设备列表
  List<PenDevice> get discoveredDevices => _devices.values.toList();

  /// 开始BLE扫描（发现周围的自然写点阵笔设备）
  /// 仅扫描包含自然写笔服务UUID的设备，过滤无关BLE设备
  Future<void> startScan({Duration timeout = const Duration(seconds: 10)}) async {
    if (_isScanning) {
      print('[BLE] 已在扫描中，忽略重复请求');
      return;
    }

    // 检查蓝牙权限和状态
    final hasPermission = await _checkBluetoothPermission();
    if (!hasPermission) {
      print('[BLE] 蓝牙权限未授予，无法扫描');
      return;
    }

    _isScanning = true;
    print('[BLE] 开始扫描自然写点阵笔设备...');

    // 使用flutter_blue扫描指定服务UUID的设备
    // 实际实现通过FlutterBluePlus.startScan()
    // 此处模拟扫描逻辑
    Timer(timeout, () {
      stopScan();
    });
  }

  /// 停止BLE扫描
  void stopScan() {
    if (!_isScanning) return;
    _isScanning = false;
    print('[BLE] 停止扫描');
  }

  /// 处理扫描到的设备广播数据
  /// 解析设备名称、信号强度、服务UUID
  void _onDeviceDiscovered(String deviceId, String name, int rssi, List<String> serviceUuids) {
    // 仅处理包含自然写笔服务UUID的设备
    if (!serviceUuids.contains(WritechBleUuids.strokeServiceUuid)) return;

    if (_devices.containsKey(deviceId)) {
      // 更新已知设备的RSSI
      _devices[deviceId]!.rssi = rssi;
    } else {
      // 发现新设备
      final device = PenDevice(
        deviceId: deviceId,
        name: name.isNotEmpty ? name : '未知笔设备',
        rssi: rssi,
      );
      _devices[deviceId] = device;
      print('[BLE] 发现新设备: $name (RSSI: $rssi)');
      _deviceStateController.add(device);
    }
  }

  /// 连接指定的点阵笔设备
  /// 建立GATT连接，发现服务，订阅笔迹数据特征值
  Future<bool> connectDevice(String deviceId) async {
    final device = _devices[deviceId];
    if (device == null) {
      print('[BLE] 未找到设备: $deviceId');
      return false;
    }

    // 检查连接数限制
    if (connectedCount >= maxConnections) {
      print('[BLE] 已达最大连接数限制 ($maxConnections)');
      return false;
    }

    device.state = PenConnectionState.connecting;
    _deviceStateController.add(device);
    print('[BLE] 正在连接: ${device.name}');

    try {
      // 步骤1: 建立BLE GATT连接
      // 实际调用: FlutterBluePlus.connect(device, autoConnect: false)
      await Future.delayed(const Duration(milliseconds: 500)); // 模拟连接耗时

      // 步骤2: 发现服务（查找笔迹数据服务和电池服务）
      await _discoverServices(deviceId);

      // 步骤3: 订阅笔迹数据Notify特征值
      await _subscribeStrokeData(deviceId);

      // 步骤4: 读取初始电量
      await _readBatteryLevel(deviceId);

      // 步骤5: 读取固件版本
      await _readFirmwareVersion(deviceId);

      device.state = PenConnectionState.connected;
      device.lastActiveTime = DateTime.now();
      _deviceStateController.add(device);

      // 初始化数据缓冲区
      _dataBuffers[deviceId] = [];

      // 启动电量定时检查（每60秒读取一次电量）
      _startBatteryCheck();

      print('[BLE] 连接成功: ${device.name}, 固件: ${device.firmwareVersion}, 电量: ${device.batteryLevel}%');
      return true;
    } catch (e) {
      device.state = PenConnectionState.disconnected;
      _deviceStateController.add(device);
      print('[BLE] 连接失败: ${device.name}, 错误: $e');

      // 设置自动重连计时器
      _scheduleReconnect(deviceId);
      return false;
    }
  }

  /// 发现BLE服务列表
  Future<void> _discoverServices(String deviceId) async {
    // 实际调用: device.discoverServices()
    // 验证是否包含笔迹数据服务UUID
    print('[BLE] 服务发现完成: $deviceId');
  }

  /// 订阅笔迹数据Notify特征值
  /// 设置MTU为247字节以支持最大数据包
  Future<void> _subscribeStrokeData(String deviceId) async {
    // 步骤1: 请求MTU协商（247字节，支持每包最多34个坐标点）
    // 实际调用: device.requestMtu(247)

    // 步骤2: 启用Notify
    // 实际调用: characteristic.setNotifyValue(true)

    // 步骤3: 监听Notify数据流
    // characteristic.onValueReceived.listen((data) => _onStrokeDataReceived(deviceId, data))
    print('[BLE] 笔迹数据订阅成功: $deviceId');
  }

  /// 处理接收到的BLE笔迹原始数据包
  /// 每个数据包包含1-34个7字节坐标点
  /// 7字节编码格式: [x_hi, x_lo, y_hi, y_lo, pressure, ts_hi, ts_lo]
  void _onStrokeDataReceived(String deviceId, Uint8List rawData) {
    final device = _devices[deviceId];
    if (device == null) return;

    // 更新设备活跃时间
    device.lastActiveTime = DateTime.now();

    // 数据包最小长度: 3字节头 + 7字节坐标 = 10字节
    if (rawData.length < 10) {
      print('[BLE] 数据包过短，丢弃: ${rawData.length}字节');
      return;
    }

    // 解析数据包头部（3字节）
    final packetType = rawData[0];    // 包类型: 0x01=实时数据, 0x02=离线数据
    final pageId = (rawData[1] << 8) | rawData[2]; // 点阵码页面ID
    final isPenDown = (packetType & 0x80) != 0;     // 最高位标识落笔状态

    // 验证CRC-16校验（数据包最后2字节）
    if (rawData.length > 5) {
      final payloadEnd = rawData.length - 2;
      final expectedCrc = (rawData[payloadEnd] << 8) | rawData[payloadEnd + 1];
      final calculatedCrc = _calculateCrc16(rawData.sublist(0, payloadEnd));
      if (expectedCrc != calculatedCrc) {
        print('[BLE] CRC校验失败，丢弃数据包');
        return;
      }
    }

    // 解码坐标数据（从第3字节开始，每7字节一个坐标点）
    final points = <StrokePoint>[];
    final dataEnd = rawData.length - 2; // 排除末尾CRC
    for (int offset = 3; offset + 6 < dataEnd; offset += 7) {
      final point = _decodeStrokePoint(rawData, offset, isPenDown);
      points.add(point);
    }

    if (points.isEmpty) return;

    // 添加到缓冲区
    final buffer = _dataBuffers[deviceId];
    if (buffer != null) {
      buffer.addAll(points);

      // 缓冲区达到批量大小时回调
      if (buffer.length >= batchSize) {
        final event = StrokeDataEvent(
          deviceId: deviceId,
          points: List<StrokePoint>.from(buffer),
          pageId: pageId,
        );
        _strokeStreamController.add(event);
        buffer.clear();
      }
    }
  }

  /// 解码单个7字节坐标点
  /// 编码格式: x(16bit) + y(16bit) + pressure(8bit) + timestamp(16bit)
  StrokePoint _decodeStrokePoint(Uint8List data, int offset, bool isPenDown) {
    // X坐标（大端序，单位: 0.01mm，范围: 0-65535 即 0-655.35mm）
    final rawX = (data[offset] << 8) | data[offset + 1];
    final x = rawX * 0.01;

    // Y坐标（同上）
    final rawY = (data[offset + 2] << 8) | data[offset + 3];
    final y = rawY * 0.01;

    // 压力值（0-255，归一化到0.0-1.0）
    final rawPressure = data[offset + 4];
    final pressure = rawPressure / 255.0;

    // 时间戳（毫秒增量，相对于笔迹起始）
    final timestamp = (data[offset + 5] << 8) | data[offset + 6];

    return StrokePoint(
      x: x, y: y,
      pressure: pressure,
      timestamp: timestamp,
      isPenDown: isPenDown,
    );
  }

  /// CRC-16 CCITT校验计算
  int _calculateCrc16(Uint8List data) {
    int crc = 0xFFFF;
    for (int i = 0; i < data.length; i++) {
      crc ^= (data[i] << 8);
      for (int j = 0; j < 8; j++) {
        if ((crc & 0x8000) != 0) {
          crc = ((crc << 1) ^ 0x1021) & 0xFFFF;
        } else {
          crc = (crc << 1) & 0xFFFF;
        }
      }
    }
    return crc;
  }

  /// 读取设备电量
  Future<void> _readBatteryLevel(String deviceId) async {
    final device = _devices[deviceId];
    if (device == null) return;

    // 实际调用: 读取Battery Service的Battery Level特征值
    // device.batteryLevel = characteristic.value[0];
    device.batteryLevel = 85; // 模拟值

    // 低电量告警
    if (device.batteryLevel > 0 && device.batteryLevel <= lowBatteryThreshold) {
      print('[BLE] 低电量告警: ${device.name} 电量 ${device.batteryLevel}%');
      _deviceStateController.add(device);
    }
  }

  /// 读取固件版本号
  Future<void> _readFirmwareVersion(String deviceId) async {
    final device = _devices[deviceId];
    if (device == null) return;
    // 读取Device Information Service的Firmware Revision特征值
    device.firmwareVersion = '1.2.0';
  }

  /// 启动电量定时检查
  void _startBatteryCheck() {
    _batteryCheckTimer?.cancel();
    _batteryCheckTimer = Timer.periodic(const Duration(seconds: 60), (_) {
      for (final entry in _devices.entries) {
        if (entry.value.state == PenConnectionState.connected) {
          _readBatteryLevel(entry.key);
        }
      }
    });
  }

  /// 向笔设备发送命令
  Future<void> sendCommand(String deviceId, int command, {Uint8List? payload}) async {
    final device = _devices[deviceId];
    if (device == null || device.state != PenConnectionState.connected) {
      print('[BLE] 设备未连接，无法发送命令');
      return;
    }

    // 构造命令数据包: [cmd, payload_len, ...payload, crc_hi, crc_lo]
    final totalLen = 2 + (payload?.length ?? 0) + 2;
    final packet = Uint8List(totalLen);
    packet[0] = command;
    packet[1] = payload?.length ?? 0;
    if (payload != null) {
      packet.setRange(2, 2 + payload.length, payload);
    }
    final crc = _calculateCrc16(packet.sublist(0, totalLen - 2));
    packet[totalLen - 2] = (crc >> 8) & 0xFF;
    packet[totalLen - 1] = crc & 0xFF;

    // 写入命令特征值
    // 实际调用: commandCharacteristic.write(packet)
    print('[BLE] 发送命令: 0x${command.toRadixString(16)} -> ${device.name}');
  }

  /// 请求同步离线数据（笔断线期间缓存的笔迹）
  Future<void> syncOfflineData(String deviceId) async {
    await sendCommand(deviceId, PenCommand.cmdSyncOffline);
    print('[BLE] 已请求同步离线数据: $deviceId');
  }

  /// 断开指定设备
  Future<void> disconnectDevice(String deviceId) async {
    final device = _devices[deviceId];
    if (device == null) return;

    // 取消重连计时器
    _reconnectTimers[deviceId]?.cancel();
    _reconnectTimers.remove(deviceId);

    device.state = PenConnectionState.disconnecting;
    _deviceStateController.add(device);

    // 清空缓冲区中的残余数据
    final buffer = _dataBuffers[deviceId];
    if (buffer != null && buffer.isNotEmpty) {
      _strokeStreamController.add(StrokeDataEvent(
        deviceId: deviceId, points: List.from(buffer), pageId: 0,
      ));
      buffer.clear();
    }

    // 断开GATT连接
    // 实际调用: device.disconnect()
    device.state = PenConnectionState.disconnected;
    _deviceStateController.add(device);
    _dataBuffers.remove(deviceId);
    print('[BLE] 已断开设备: ${device.name}');
  }

  /// 设置自动重连计时器
  void _scheduleReconnect(String deviceId) {
    _reconnectTimers[deviceId]?.cancel();
    _reconnectTimers[deviceId] = Timer(
      Duration(seconds: reconnectIntervalSec),
      () async {
        final device = _devices[deviceId];
        if (device != null && device.state == PenConnectionState.disconnected) {
          print('[BLE] 尝试自动重连: ${device.name}');
          await connectDevice(deviceId);
        }
      },
    );
  }

  /// 检查蓝牙权限（Android需要位置权限，iOS需要蓝牙使用描述）
  Future<bool> _checkBluetoothPermission() async {
    // Android: 检查 BLUETOOTH_SCAN, BLUETOOTH_CONNECT, ACCESS_FINE_LOCATION
    // iOS: 检查 CBManager authorization status
    return true;
  }

  /// 断开所有设备并释放资源
  void dispose() {
    // 停止扫描
    stopScan();

    // 取消所有重连计时器
    for (final timer in _reconnectTimers.values) {
      timer.cancel();
    }
    _reconnectTimers.clear();

    // 停止电量检查
    _batteryCheckTimer?.cancel();

    // 断开所有设备
    for (final deviceId in _devices.keys.toList()) {
      disconnectDevice(deviceId);
    }

    // 关闭流控制器
    _strokeStreamController.close();
    _deviceStateController.close();

    _devices.clear();
    _dataBuffers.clear();
    print('[BLE] BLE服务已销毁');
  }
}
