import 'dart:convert';
import 'package:http/http.dart' as http;

/// JSON-RPC 2.0 HTTP 客户端
class JsonRpcClient {
  final String url;
  final http.Client _httpClient;
  int _requestId = 0;

  JsonRpcClient(this.url) : _httpClient = http.Client();

  /// 发送 JSON-RPC 请求
  Future<dynamic> call(String method, List<dynamic> params) async {
    _requestId++;
    final body = jsonEncode({
      'jsonrpc': '2.0',
      'id': _requestId,
      'method': method,
      'params': params,
    });

    final response = await _httpClient.post(
      Uri.parse(url),
      headers: {'Content-Type': 'application/json'},
      body: body,
    );

    if (response.statusCode != 200) {
      throw RpcException(
        -1,
        'HTTP ${response.statusCode}: ${response.body}',
      );
    }

    final json = jsonDecode(response.body) as Map<String, dynamic>;

    if (json.containsKey('error') && json['error'] != null) {
      final error = json['error'] as Map<String, dynamic>;
      throw RpcException(
        error['code'] as int? ?? -1,
        error['message'] as String? ?? 'Unknown RPC error',
      );
    }

    return json['result'];
  }

  /// 批量请求
  Future<List<dynamic>> callBatch(
      List<MapEntry<String, List<dynamic>>> requests) async {
    final batch = requests.map((entry) {
      _requestId++;
      return {
        'jsonrpc': '2.0',
        'id': _requestId,
        'method': entry.key,
        'params': entry.value,
      };
    }).toList();

    final response = await _httpClient.post(
      Uri.parse(url),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode(batch),
    );

    if (response.statusCode != 200) {
      throw RpcException(-1, 'HTTP ${response.statusCode}');
    }

    final results = jsonDecode(response.body) as List;
    return results.map((r) {
      final json = r as Map<String, dynamic>;
      if (json.containsKey('error') && json['error'] != null) {
        return null;
      }
      return json['result'];
    }).toList();
  }

  void close() {
    _httpClient.close();
  }
}

/// RPC 异常
class RpcException implements Exception {
  final int code;
  final String message;

  RpcException(this.code, this.message);

  @override
  String toString() => 'RpcException($code): $message';
}
