/// Synor Wallet SDK client for Flutter/Dart.
library synor_wallet;

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

export 'types.dart';

/// Synor Wallet SDK client for Flutter/Dart.
///
/// Provides key management, transaction signing, and balance queries
/// for the Synor blockchain.
///
/// Example:
/// ```dart
/// final wallet = SynorWallet(WalletConfig(apiKey: 'your-api-key'));
///
/// // Create a new wallet
/// final result = await wallet.createWallet(type: WalletType.standard);
/// print('Wallet ID: ${result.wallet.id}');
/// print('Mnemonic: ${result.mnemonic}');
///
/// // Get balance
/// final balance = await wallet.getBalance(result.wallet.addresses[0].address);
/// print('Balance: ${balance.total}');
///
/// wallet.close();
/// ```
class SynorWallet {
  final WalletConfig config;
  final http.Client _client;

  SynorWallet(this.config) : _client = http.Client();

  /// Create a new wallet.
  Future<CreateWalletResult> createWallet({
    WalletType type = WalletType.standard,
  }) async {
    final body = {
      'type': type.toJson(),
      'network': config.network.toJson(),
    };
    return _post('/wallets', body, CreateWalletResult.fromJson);
  }

  /// Import a wallet from a mnemonic phrase.
  Future<Wallet> importWallet(String mnemonic, {String? passphrase}) async {
    final body = {
      'mnemonic': mnemonic,
      'passphrase': passphrase,
      'network': config.network.toJson(),
    };
    return _post('/wallets/import', body, Wallet.fromJson);
  }

  /// Get a wallet by ID.
  Future<Wallet> getWallet(String walletId) async {
    return _get('/wallets/$walletId', Wallet.fromJson);
  }

  /// Generate a new address for a wallet.
  Future<Address> generateAddress(String walletId, {bool isChange = false}) async {
    final body = {'is_change': isChange};
    return _post('/wallets/$walletId/addresses', body, Address.fromJson);
  }

  /// Get a stealth address for privacy transactions.
  Future<StealthAddress> getStealthAddress(String walletId) async {
    return _get('/wallets/$walletId/stealth-address', StealthAddress.fromJson);
  }

  /// Sign a transaction.
  Future<SignedTransaction> signTransaction(
    String walletId,
    Transaction transaction,
  ) async {
    final body = {
      'wallet_id': walletId,
      'transaction': transaction.toJson(),
    };
    return _post('/transactions/sign', body, SignedTransaction.fromJson);
  }

  /// Sign a message with a wallet address.
  Future<Signature> signMessage(
    String walletId,
    String message, {
    int addressIndex = 0,
  }) async {
    final body = {
      'wallet_id': walletId,
      'message': message,
      'address_index': addressIndex,
    };
    return _post('/messages/sign', body, Signature.fromJson);
  }

  /// Verify a message signature.
  Future<bool> verifyMessage(
    String message,
    String signature,
    String address,
  ) async {
    final body = {
      'message': message,
      'signature': signature,
      'address': address,
    };
    final response = await _post<Map<String, dynamic>>(
      '/messages/verify',
      body,
      (json) => json,
    );
    return response['valid'] as bool? ?? false;
  }

  /// Get the balance for an address.
  Future<Balance> getBalance(String address) async {
    return _get('/addresses/$address/balance', Balance.fromJson);
  }

  /// Get UTXOs for an address.
  Future<List<UTXO>> getUTXOs(String address, {int minConfirmations = 1}) async {
    return _getList(
      '/addresses/$address/utxos?min_confirmations=$minConfirmations',
      UTXO.fromJson,
    );
  }

  /// Estimate transaction fee.
  Future<int> estimateFee({Priority priority = Priority.medium}) async {
    final response = await _get<Map<String, dynamic>>(
      '/fees/estimate?priority=${priority.toJson()}',
      (json) => json,
    );
    return response['fee_per_byte'] as int? ?? 0;
  }

  // HTTP helper methods

  Future<T> _get<T>(String path, T Function(Map<String, dynamic>) fromJson) async {
    return _executeWithRetry(() async {
      final response = await _client
          .get(
            Uri.parse('${config.endpoint}$path'),
            headers: _headers,
          )
          .timeout(config.timeout);

      return _handleResponse(response, fromJson);
    });
  }

  Future<List<T>> _getList<T>(
    String path,
    T Function(Map<String, dynamic>) fromJson,
  ) async {
    return _executeWithRetry(() async {
      final response = await _client
          .get(
            Uri.parse('${config.endpoint}$path'),
            headers: _headers,
          )
          .timeout(config.timeout);

      if (response.statusCode >= 200 && response.statusCode < 300) {
        final List<dynamic> jsonList = jsonDecode(response.body);
        return jsonList
            .map((e) => fromJson(e as Map<String, dynamic>))
            .toList();
      }

      throw WalletException(
        'HTTP error: ${response.body}',
        statusCode: response.statusCode,
      );
    });
  }

  Future<T> _post<T>(
    String path,
    Map<String, dynamic> body,
    T Function(Map<String, dynamic>) fromJson,
  ) async {
    return _executeWithRetry(() async {
      final response = await _client
          .post(
            Uri.parse('${config.endpoint}$path'),
            headers: _headers,
            body: jsonEncode(body),
          )
          .timeout(config.timeout);

      return _handleResponse(response, fromJson);
    });
  }

  Map<String, String> get _headers => {
        'Authorization': 'Bearer ${config.apiKey}',
        'Content-Type': 'application/json',
      };

  T _handleResponse<T>(
    http.Response response,
    T Function(Map<String, dynamic>) fromJson,
  ) {
    if (response.statusCode >= 200 && response.statusCode < 300) {
      final json = jsonDecode(response.body) as Map<String, dynamic>;
      return fromJson(json);
    }

    throw WalletException(
      'HTTP error: ${response.body}',
      statusCode: response.statusCode,
    );
  }

  Future<T> _executeWithRetry<T>(Future<T> Function() operation) async {
    Exception? lastError;

    for (var attempt = 0; attempt < config.retries; attempt++) {
      try {
        return await operation();
      } catch (e) {
        lastError = e as Exception;
        if (config.debug) {
          print('Attempt ${attempt + 1} failed: $e');
        }
        if (attempt < config.retries - 1) {
          await Future.delayed(Duration(seconds: attempt + 1));
        }
      }
    }

    throw lastError ?? WalletException('Unknown error after ${config.retries} retries');
  }

  /// Close the client and release resources.
  void close() {
    _client.close();
  }
}
