import 'dart:async';
import 'dart:convert';
import 'dart:math';

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 输出（消息、连接状态、错误）
/// - 生命周期感知（前后台切换）
///
/// ## 使用方式
///
/// ```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': {...}});
///
/// // 断连
/// 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();

  // ── Stream Controllers ──
  final _messageController = StreamController<Map<String, dynamic>>.broadcast();
  final _rawMessageController = StreamController<String>.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;

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

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

  /// 发送原始字符串
  Future<bool> sendString(String message) async {
    if (!isConnected || _channel == null) {
      _emitError(SocketError.sendFailed('Not connected'));
      return false;
    }

    try {
      _channel!.sink.add(message);
      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<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 _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（拼接 token）
      final connectUri = _currentToken != null
          ? uri.replace(
        queryParameters: {
          ...uri.queryParameters,
          'token': _currentToken!,
        },
      )
          : uri;

      // 创建 WebSocket 连接
      _channel = IOWebSocketChannel.connect(
        connectUri,
        pingInterval: config.pingInterval,
      );

      // 等待连接就绪
      await _channel!.ready.timeout(config.connectTimeout);

      _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! String) {
      // 非字符串消息（如二进制），走 rawMessageStream
      _rawMessageController.add(data.toString());
      return;
    }

    // 检查 pong 心跳回复
    if (data == 'pong') {
      _onPongReceived();
      return;
    }

    // 尝试 JSON 解析
    try {
      final json = jsonDecode(data) as Map<String, dynamic>;
      _messageController.add(json);
    } catch (_) {
      // JSON 解析失败，走原始消息流
      _rawMessageController.add(data);
    }
  }

  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;
    _channel?.sink.add('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() {
    _waitingForPong = false;
    _pongTimeoutTimer?.cancel();
    _pongTimeoutTimer = null;
  }

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

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

    if (_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);

    _log('Reconnecting in ${delay.inMilliseconds}ms '
        '(attempt $_reconnectAttempts/${config.maxReconnectAttempts})');

    _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;
        }
      }

      _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');
  }
}
