import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;
import 'dart:math';
import 'dart:typed_data';

import 'package:networks_sdk/networks_sdk.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';

/// WebSocket 长连接客户端
///
/// 提供：
/// - 连接 / 断连 / 自动重连（指数退避，支持无限重连）
/// - 双层心跳（底层 ping + 应用层 heartbeat）
/// - Stream 输出（JSON 消息、原始字符串、二进制、连接状态、错误）
/// - 生命周期感知（前后台切换）
/// - Token 热更新（不断连）
/// - 消息加密/解密钩子（预留给 cipher_guard_sdk，ping/pong 走明文不加密）
///
/// ## 使用方式
///
/// ```dart
/// final client = SocketClient(config: socketConfig);
///
/// // 连接
/// await client.connect('wss://api.example.com/ws', token: 'xxx');
///
/// // 收消息
/// client.messageStream.listen((msg) => print(msg));
///
/// // 发消息
/// await client.send({'type': 'chat', 'data': {...}});
///
/// // Token 热更新（不断连，下次重连自动使用新 token）
/// client.updateToken('new_token');
///
/// // 断连
/// await client.disconnect();
/// ```
class SocketClient {
  final SocketConfig config;

  // ── 内部状态 ──
  WebSocketChannel? _channel;
  StreamSubscription? _channelSubscription;
  SocketConnectionState _connectionState = SocketConnectionState.disconnected;
  String? _currentUrl;
  String? _currentToken;
  bool _manualDisconnect = false;
  bool _isBackground = false;

  // ── 心跳 ──
  Timer? _heartbeatTimer;
  Timer? _pongTimeoutTimer;
  bool _waitingForPong = false;

  // ── 重连 ──
  int _reconnectAttempts = 0;
  Timer? _reconnectTimer;
  final _random = Random();

  // ── 消息处理 ──

  /// 异步消息处理链，保证解密场景下消息按到达顺序处理
  ///
  /// 无解密回调时不使用（同步处理，天然有序）。
  /// 有解密回调时，每条消息的处理链在前一条之后执行，
  /// 即使解密耗时不同也不会乱序。
  Future<void>? _messageProcessingChain;

  // ── Stream Controllers ──
  final _messageController = StreamController<Map<String, dynamic>>.broadcast();
  final _rawMessageController = StreamController<String>.broadcast();
  final _binaryMessageController = StreamController<Uint8List>.broadcast();
  final _connectionStateController =
      StreamController<SocketConnectionState>.broadcast();
  final _errorController = StreamController<SocketError>.broadcast();

  SocketClient({required this.config});

  // ══════════════════════════════════════════════════════════════════════════
  //  公开 API — 连接
  // ══════════════════════════════════════════════════════════════════════════

  /// 连接到 WebSocket 服务器
  ///
  /// [url] — 完整的 WebSocket URL（如 `wss://api.example.com/ws`）
  /// [token] — 可选，拼接到 URL query 参数
  Future<bool> connect(String url, {String? token}) async {
    if (_connectionState == SocketConnectionState.connected ||
        _connectionState == SocketConnectionState.connecting) {
      _log('Already connected or connecting, skip');
      return _connectionState == SocketConnectionState.connected;
    }

    _currentUrl = url;
    _currentToken = token;
    _manualDisconnect = false;

    return _doConnect();
  }

  /// 断开连接
  ///
  /// 手动断连不触发自动重连。
  Future<void> disconnect() async {
    _manualDisconnect = true;
    await _doDisconnect(reason: 'Manual disconnect');
  }

  /// 当前是否已连接
  bool get isConnected => _connectionState == SocketConnectionState.connected;

  /// 当前连接状态
  SocketConnectionState get connectionState => _connectionState;

  /// Token 热更新（不断开连接）
  ///
  /// 仅更新内部持有的 token，下次重连时自动使用新 token。
  /// 适用于 token 刷新后同步到 WebSocket 的场景。
  void updateToken(String token) {
    _currentToken = token;
    _log('Token updated (no reconnect)');
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  公开 API — 发送
  // ══════════════════════════════════════════════════════════════════════════

  /// 发送 JSON 消息
  Future<bool> send(Map<String, dynamic> message) {
    return sendString(jsonEncode(message));
  }

  /// 发送原始字符串
  ///
  /// 如果配置了 [SocketConfig.onEncryptMessage]，发送前自动加密。
  Future<bool> sendString(String message) async {
    if (!isConnected || _channel == null) {
      _emitError(SocketError.sendFailed('Not connected'));
      return false;
    }

    try {
      String payload = message;
      if (config.onEncryptMessage != null) {
        payload = await config.onEncryptMessage!(message);
      }
      _channel!.sink.add(payload);
      return true;
    } catch (e) {
      _emitError(SocketError.sendFailed(e.toString()));
      return false;
    }
  }

  /// 发送二进制数据
  Future<bool> sendBytes(List<int> bytes) async {
    if (!isConnected || _channel == null) {
      _emitError(SocketError.sendFailed('Not connected'));
      return false;
    }

    try {
      _channel!.sink.add(bytes);
      return true;
    } catch (e) {
      _emitError(SocketError.sendFailed(e.toString()));
      return false;
    }
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  公开 API — Stream 输出
  // ══════════════════════════════════════════════════════════════════════════

  /// 已解析的 JSON 消息流
  Stream<Map<String, dynamic>> get messageStream => _messageController.stream;

  /// 原始字符串消息流（JSON 解析失败的也走这里）
  Stream<String> get rawMessageStream => _rawMessageController.stream;

  /// 二进制消息流
  Stream<Uint8List> get binaryMessageStream => _binaryMessageController.stream;

  /// 连接状态变化流
  Stream<SocketConnectionState> get connectionStateStream =>
      _connectionStateController.stream;

  /// 错误流
  Stream<SocketError> get errorStream => _errorController.stream;

  // ══════════════════════════════════════════════════════════════════════════
  //  公开 API — 生命周期
  // ══════════════════════════════════════════════════════════════════════════

  /// App 回前台 → 检查连接，断了就重连
  void onEnterForeground() {
    _isBackground = false;
    _log('Enter foreground');

    if (!isConnected && !_manualDisconnect && _currentUrl != null) {
      _log('Connection lost while in background, reconnecting...');
      _startReconnect();
    } else if (isConnected) {
      _startHeartbeat();
    }
  }

  /// App 进后台 → 停止心跳（保持连接）
  void onEnterBackground() {
    _isBackground = true;
    _log('Enter background, pausing heartbeat');
    _stopHeartbeat();
  }

  /// 释放所有资源
  Future<void> dispose() async {
    _manualDisconnect = true;
    await _doDisconnect(reason: 'Dispose');
    await _messageController.close();
    await _rawMessageController.close();
    await _binaryMessageController.close();
    await _connectionStateController.close();
    await _errorController.close();
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  内部 — 连接
  // ══════════════════════════════════════════════════════════════════════════

  Future<bool> _doConnect() async {
    final url = _currentUrl;
    if (url == null || url.isEmpty) {
      _emitError(SocketError.invalidURL(url ?? ''));
      return false;
    }

    // 验证 URL
    final uri = Uri.tryParse(url);
    if (uri == null || (!uri.isScheme('ws') && !uri.isScheme('wss'))) {
      _emitError(SocketError.invalidURL(url));
      return false;
    }

    _updateConnectionState(SocketConnectionState.connecting);
    _log('Connecting to $url');

    try {
      // 构建最终连接 URL
      //
      // 有 onBuildConnectUrl 回调时，App 层完全控制 URL（路径加密、
      // token 加密、添加 cipher 参数等）。
      // 无回调时使用默认行为：URL 后追加 ?token=xxx。
      final String connectUrlString;

      if (config.onBuildConnectUrl != null) {
        connectUrlString = config.onBuildConnectUrl!(url, _currentToken);
      } else {
        final connectUri = _currentToken != null
            ? uri.replace(
                queryParameters: {
                  ...uri.queryParameters,
                  'token': _currentToken!,
                },
              )
            : uri;
        connectUrlString = connectUri.toString();
      }

      // 创建 WebSocket 连接（通过 dart:io 支持压缩选项）
      final rawSocket = await io.WebSocket.connect(
        connectUrlString,
        compression: config.enableCompression
            ? io.CompressionOptions.compressionDefault
            : io.CompressionOptions.compressionOff,
      ).timeout(config.connectTimeout);
      rawSocket.pingInterval = config.pingInterval;
      _channel = IOWebSocketChannel(rawSocket);

      _log('Connected');
      _updateConnectionState(SocketConnectionState.connected);
      _reconnectAttempts = 0;

      // 开始监听消息
      _channelSubscription = _channel!.stream.listen(
        _handleMessage,
        onError: _handleError,
        onDone: _handleDone,
      );

      // 启动心跳
      _startHeartbeat();

      return true;
    } on TimeoutException {
      _log('Connection timeout');
      _emitError(const SocketError.connectionTimeout());
      await _doDisconnect(reason: 'Timeout');
      _startReconnect();
      return false;
    } catch (e) {
      _log('Connection failed: $e');
      _emitError(SocketError.connectionFailed(e.toString()));
      await _doDisconnect(reason: e.toString());
      _startReconnect();
      return false;
    }
  }

  Future<void> _doDisconnect({String? reason}) async {
    _stopHeartbeat();
    _stopReconnectTimer();

    await _channelSubscription?.cancel();
    _channelSubscription = null;

    try {
      await _channel?.sink.close();
    } catch (_) {
      // 忽略关闭错误
    }
    _channel = null;

    if (_connectionState != SocketConnectionState.disconnected) {
      _log('Disconnected${reason != null ? ': $reason' : ''}');
      _updateConnectionState(SocketConnectionState.disconnected);
    }
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  内部 — 消息处理
  // ══════════════════════════════════════════════════════════════════════════

  void _handleMessage(dynamic data) {
    // 二进制消息不需要解密，直接分发
    if (data is List<int>) {
      if (!_binaryMessageController.isClosed) {
        _binaryMessageController.add(
          data is Uint8List ? data : Uint8List.fromList(data),
        );
      }
      return;
    }

    if (data is! String) {
      if (!_rawMessageController.isClosed) {
        _rawMessageController.add(data.toString());
      }
      return;
    }

    // pong 是传输层心跳，不经过业务加解密，直接匹配
    if (data == 'pong') {
      _onPongReceived();
      return;
    }

    if (config.onDecryptMessage != null) {
      // 有解密回调 → 链式异步处理，保证消息按到达顺序分发
      // 避免解密耗时不同导致后到的消息先完成解密、先分发
      final previous = _messageProcessingChain ?? Future.value();
      _messageProcessingChain = previous.then((_) => _processTextMessage(data));
    } else {
      // 无解密回调 → 同步处理，天然有序
      _dispatchTextMessage(data);
    }
  }

  /// 异步处理文本消息（解密 → 分发）
  Future<void> _processTextMessage(String data) async {
    // dispose 期间可能有残留的链式任务，直接跳过
    if (_messageController.isClosed) return;

    String text;
    try {
      text = await config.onDecryptMessage!(data);
    } catch (e) {
      _log('Message decryption failed: $e');
      if (!_rawMessageController.isClosed) {
        _rawMessageController.add(data);
      }
      return;
    }
    _dispatchTextMessage(text);
  }

  /// 分发文本消息（JSON 解析 → 投递到对应 stream）
  ///
  /// pong 已在 `_handleMessage` 中提前拦截，不会到这里。
  void _dispatchTextMessage(String text) {
    try {
      final json = jsonDecode(text) as Map<String, dynamic>;
      if (!_messageController.isClosed) {
        _messageController.add(json);
      }
    } catch (_) {
      // JSON 解析失败，走原始消息流
      if (!_rawMessageController.isClosed) {
        _rawMessageController.add(text);
      }
    }
  }

  void _handleError(Object error) {
    _log('Stream error: $error');
    _emitError(SocketError.connectionFailed(error.toString()));
  }

  void _handleDone() async {
    _log('Stream closed');
    await _doDisconnect(reason: 'Stream closed');

    if (!_manualDisconnect) {
      _startReconnect();
    }
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  内部 — 心跳
  // ══════════════════════════════════════════════════════════════════════════

  void _startHeartbeat() {
    _stopHeartbeat();

    _heartbeatTimer = Timer.periodic(config.heartbeatInterval, (_) {
      if (!isConnected || _isBackground) return;
      _sendPing();
    });
  }

  void _stopHeartbeat() {
    _heartbeatTimer?.cancel();
    _heartbeatTimer = null;
    _pongTimeoutTimer?.cancel();
    _pongTimeoutTimer = null;
    _waitingForPong = false;
  }

  void _sendPing() {
    if (_waitingForPong) return;

    _waitingForPong = true;

    // ping/pong 是传输层心跳，不经过业务加解密
    // 保证即使加密密钥过期/轮换失败，心跳仍然正常工作
    _channel?.sink.add('ping');
    _log('♥️ ping');

    // 启动 pong 超时计时器
    _pongTimeoutTimer = Timer(config.pongTimeout, () {
      if (_waitingForPong) {
        _log('♥️ pong timeout, reconnecting...');
        _waitingForPong = false;
        _emitError(const SocketError.pingTimeout());
        _doDisconnect(reason: 'Pong timeout');
        _startReconnect();
      }
    });
  }

  void _onPongReceived() {
    _log('♥️ pong');
    _waitingForPong = false;
    _pongTimeoutTimer?.cancel();
    _pongTimeoutTimer = null;
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  内部 — 重连（指数退避）
  // ══════════════════════════════════════════════════════════════════════════

  void _startReconnect() {
    if (_manualDisconnect || !config.autoReconnect || _isBackground) return;
    if (_connectionState == SocketConnectionState.reconnecting) return;

    // 非无限重连模式下检查重连次数上限
    if (!config.unlimitedReconnect &&
        _reconnectAttempts >= config.maxReconnectAttempts) {
      _log('Max reconnect attempts reached ($_reconnectAttempts)');
      _reconnectAttempts = 0;
      return;
    }

    _updateConnectionState(SocketConnectionState.reconnecting);
    _reconnectAttempts++;

    // 指数退避 + jitter：min(2^attempt * 1s, maxReconnectDelay) + random jitter
    // jitter 防止多设备同时重连导致服务器瞬间过载（thundering herd）
    final baseDelayMs = min(
      pow(2, _reconnectAttempts).toInt() * 1000,
      config.maxReconnectDelay.inMilliseconds,
    );
    final jitterMs = _random.nextInt(
      (baseDelayMs * 0.25).toInt().clamp(1, 7500),
    );
    final delay = Duration(milliseconds: baseDelayMs + jitterMs);

    final attemptsInfo = config.unlimitedReconnect
        ? 'attempt $_reconnectAttempts/unlimited'
        : 'attempt $_reconnectAttempts/${config.maxReconnectAttempts}';

    _log('Reconnecting in ${delay.inMilliseconds}ms ($attemptsInfo)');

    _reconnectTimer = Timer(delay, () async {
      // 重连前检查网络
      if (config.onCheckNetworkAvailable != null) {
        final available = await config.onCheckNetworkAvailable!();
        if (!available) {
          _log('Network unavailable, skip reconnect');
          _emitError(const SocketError.networkUnavailable());
          _updateConnectionState(SocketConnectionState.disconnected);
          return;
        }
      }

      // 重连前回调（App 层刷新即将过期的 token 等）
      if (config.onBeforeReconnect != null) {
        try {
          await config.onBeforeReconnect!();
        } catch (e) {
          _log('onBeforeReconnect failed: $e, schedule next reconnect');
          // 重置状态以允许下次 _startReconnect 进入（防止卡死在 reconnecting）
          _updateConnectionState(SocketConnectionState.disconnected);
          _startReconnect();
          return;
        }
      }

      _doConnect();
    });
  }

  void _stopReconnectTimer() {
    _reconnectTimer?.cancel();
    _reconnectTimer = null;
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  内部 — 辅助
  // ══════════════════════════════════════════════════════════════════════════

  void _updateConnectionState(SocketConnectionState newState) {
    if (_connectionState == newState) return;
    _connectionState = newState;
    _connectionStateController.add(newState);
  }

  void _emitError(SocketError error) {
    _errorController.add(error);
  }

  void _log(String message) {
    config.onLog?.call(message, tag: 'Socket');
  }
}
