import 'dart:async';

import 'models/coupon_detail.dart';
import 'models/coupon_holding.dart';
import 'models/block_info.dart';
import 'models/transaction_info.dart';
import 'models/chain_stats.dart';
import 'models/chain_event.dart';
import 'models/event_filter.dart';
import 'models/address_balance.dart';
import 'rpc/json_rpc_client.dart';
import 'rpc/websocket_client.dart';
import 'contracts/contract_abis.dart';
import 'contracts/contract_addresses.dart';
import 'utils/formatters.dart';

/// Genex Chain SDK 主客户端
class GenexClient {
  final String rpcUrl;
  final int chainId;
  final JsonRpcClient _rpc;
  GenexWebSocketClient? _ws;

  GenexClient({
    required this.rpcUrl,
    this.chainId = 8888,
  }) : _rpc = JsonRpcClient(rpcUrl);

  // ─── 区块查询 ──────────────────────────────────────────

  /// 获取指定高度区块
  Future<BlockInfo> getBlock(int height) async {
    final result = await _rpc.call(
      'eth_getBlockByNumber',
      [Formatters.toHex(height), false],
    );
    return BlockInfo.fromRpcJson(result as Map<String, dynamic>);
  }

  /// 获取最新区块
  Future<BlockInfo> getLatestBlock() async {
    final result = await _rpc.call(
      'eth_getBlockByNumber',
      ['latest', false],
    );
    return BlockInfo.fromRpcJson(result as Map<String, dynamic>);
  }

  /// 获取当前区块高度
  Future<int> getBlockHeight() async {
    final result = await _rpc.call('eth_blockNumber', []);
    return Formatters.hexToInt(result as String);
  }

  // ─── 交易查询 ──────────────────────────────────────────

  /// 获取交易详情
  Future<TransactionInfo> getTransaction(String txHash) async {
    final txResult = await _rpc.call('eth_getTransactionByHash', [txHash]);
    final receiptResult =
        await _rpc.call('eth_getTransactionReceipt', [txHash]);
    return TransactionInfo.fromRpcJson(
      txResult as Map<String, dynamic>,
      receiptResult as Map<String, dynamic>,
    );
  }

  // ─── 地址查询 ──────────────────────────────────────────

  /// 查询地址余额
  Future<AddressBalance> getBalance(String address) async {
    final balanceHex =
        await _rpc.call('eth_getBalance', [address, 'latest']);
    final nonce =
        await _rpc.call('eth_getTransactionCount', [address, 'latest']);
    return AddressBalance(
      address: address,
      balance: Formatters.hexToBigInt(balanceHex as String),
      nonce: Formatters.hexToInt(nonce as String),
    );
  }

  // ─── 券查询 ────────────────────────────────────────────

  /// 查询券详情（调用 Coupon.getConfig）
  Future<CouponDetail> getCouponDetail(BigInt tokenId) async {
    final data = ContractAbis.encodeCouponGetConfig(tokenId);
    final result = await _rpc.call('eth_call', [
      {
        'to': ContractAddresses.coupon,
        'data': data,
      },
      'latest',
    ]);
    return CouponDetail.fromAbiResult(result as String, tokenId);
  }

  /// 查询地址持有的券 NFT
  Future<List<CouponHolding>> getCouponHoldings(String address) async {
    // balanceOf
    final balanceData = ContractAbis.encodeBalanceOf(address);
    final balanceHex = await _rpc.call('eth_call', [
      {'to': ContractAddresses.coupon, 'data': balanceData},
      'latest',
    ]);
    final balance = Formatters.hexToInt(balanceHex as String);

    final holdings = <CouponHolding>[];
    for (var i = 0; i < balance; i++) {
      final tokenData =
          ContractAbis.encodeTokenOfOwnerByIndex(address, i);
      final tokenIdHex = await _rpc.call('eth_call', [
        {'to': ContractAddresses.coupon, 'data': tokenData},
        'latest',
      ]);
      final tokenId = Formatters.hexToBigInt(tokenIdHex as String);
      holdings.add(CouponHolding(tokenId: tokenId, index: i));
    }
    return holdings;
  }

  // ─── 链统计 ────────────────────────────────────────────

  /// 获取链统计信息
  Future<ChainStats> getStats() async {
    final blockHex = await _rpc.call('eth_blockNumber', []);
    final peerCount = await _rpc.call('net_peerCount', []);
    return ChainStats(
      blockHeight: Formatters.hexToInt(blockHex as String),
      peerCount: Formatters.hexToInt(peerCount as String),
      chainId: chainId,
    );
  }

  // ─── 事件订阅 ──────────────────────────────────────────

  GenexWebSocketClient _ensureWs() {
    _ws ??= GenexWebSocketClient(rpcUrl.replaceFirst('http', 'ws'));
    return _ws!;
  }

  /// 统一事件订阅接口（匹配指南 §8.4 subscribeEvents 规范）
  ///
  /// [filter] 事件过滤器：
  ///   - type='newHeads' 订阅新区块
  ///   - type='logs' 订阅合约事件，可指定 address / topics
  Stream<ChainEvent> subscribeEvents(EventFilter filter) {
    return _ensureWs().subscribe(filter.type, filter.params);
  }

  /// 连接 WebSocket 并订阅新区块头
  Stream<ChainEvent> subscribeNewHeads() {
    return _ensureWs().subscribe('newHeads', {});
  }

  /// 订阅合约事件日志
  Stream<ChainEvent> subscribeLogs({
    String? address,
    List<String>? topics,
  }) {
    final params = <String, dynamic>{};
    if (address != null) params['address'] = address;
    if (topics != null) params['topics'] = topics;
    return _ensureWs().subscribe('logs', params);
  }

  // ─── 生命周期 ──────────────────────────────────────────

  /// 关闭客户端连接
  void close() {
    _rpc.close();
    _ws?.close();
  }
}
