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

/// Synor Privacy SDK client for Flutter/Dart.
/// Confidential transactions, ring signatures, stealth addresses, and cryptographic commitments.
class PrivacyClient {
  static const String version = '0.1.0';

  final PrivacyConfig config;
  final http.Client _client;
  bool _closed = false;

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

  // ==================== Confidential Transactions ====================

  Future<ConfidentialTransaction> createConfidentialTx({
    required List<ConfidentialTxInput> inputs,
    required List<ConfidentialTxOutput> outputs,
  }) async {
    final body = {
      'inputs': inputs.map((i) => i.toJson()).toList(),
      'outputs': outputs.map((o) => o.toJson()).toList(),
    };
    final response = await _post('/privacy/confidential/create', body);
    return ConfidentialTransaction.fromJson(response);
  }

  Future<bool> verifyConfidentialTx(ConfidentialTransaction tx) async {
    final body = {'transaction': tx.toJson()};
    final response = await _post('/privacy/confidential/verify', body);
    return response['valid'] as bool? ?? false;
  }

  Future<Commitment> createCommitment({
    required String value,
    required String blindingFactor,
  }) async {
    final body = {
      'value': value,
      'blinding_factor': blindingFactor,
    };
    final response = await _post('/privacy/commitment/create', body);
    return Commitment.fromJson(response);
  }

  Future<bool> verifyCommitment({
    required String commitment,
    required String value,
    required String blindingFactor,
  }) async {
    final body = {
      'commitment': commitment,
      'value': value,
      'blinding_factor': blindingFactor,
    };
    final response = await _post('/privacy/commitment/verify', body);
    return response['valid'] as bool? ?? false;
  }

  Future<RangeProof> createRangeProof({
    required String value,
    required String blindingFactor,
    required int minValue,
    required int maxValue,
  }) async {
    final body = {
      'value': value,
      'blinding_factor': blindingFactor,
      'min_value': minValue,
      'max_value': maxValue,
    };
    final response = await _post('/privacy/range-proof/create', body);
    return RangeProof.fromJson(response);
  }

  Future<bool> verifyRangeProof(RangeProof proof) async {
    final body = {'proof': proof.toJson()};
    final response = await _post('/privacy/range-proof/verify', body);
    return response['valid'] as bool? ?? false;
  }

  // ==================== Ring Signatures ====================

  Future<RingSignature> createRingSignature({
    required String message,
    required List<String> ring,
    required int signerIndex,
    required String privateKey,
  }) async {
    final body = {
      'message': message,
      'ring': ring,
      'signer_index': signerIndex,
      'private_key': privateKey,
    };
    final response = await _post('/privacy/ring/sign', body);
    return RingSignature.fromJson(response);
  }

  Future<bool> verifyRingSignature(RingSignature signature, String message) async {
    final body = {
      'signature': signature.toJson(),
      'message': message,
    };
    final response = await _post('/privacy/ring/verify', body);
    return response['valid'] as bool? ?? false;
  }

  Future<List<String>> generateDecoys(int count, {String? excludeKey}) async {
    var path = '/privacy/ring/decoys?count=$count';
    if (excludeKey != null) {
      path += '&exclude=${Uri.encodeComponent(excludeKey)}';
    }
    final response = await _get(path);
    return (response as List).map((e) => e as String).toList();
  }

  Future<bool> checkKeyImage(String keyImage) async {
    final response = await _get('/privacy/ring/key-image/$keyImage');
    return response['spent'] as bool? ?? false;
  }

  // ==================== Stealth Addresses ====================

  Future<StealthKeyPair> generateStealthKeyPair() async {
    final response = await _post('/privacy/stealth/generate', {});
    return StealthKeyPair.fromJson(response);
  }

  Future<StealthAddress> deriveStealthAddress({
    required String spendPublicKey,
    required String viewPublicKey,
  }) async {
    final body = {
      'spend_public_key': spendPublicKey,
      'view_public_key': viewPublicKey,
    };
    final response = await _post('/privacy/stealth/derive', body);
    return StealthAddress.fromJson(response);
  }

  Future<String> recoverStealthPrivateKey({
    required String stealthAddress,
    required String viewPrivateKey,
    required String spendPrivateKey,
  }) async {
    final body = {
      'stealth_address': stealthAddress,
      'view_private_key': viewPrivateKey,
      'spend_private_key': spendPrivateKey,
    };
    final response = await _post('/privacy/stealth/recover', body);
    return response['private_key'] as String;
  }

  Future<List<StealthOutput>> scanOutputs({
    required String viewPrivateKey,
    required String spendPublicKey,
    required int fromBlock,
    int? toBlock,
  }) async {
    final body = {
      'view_private_key': viewPrivateKey,
      'spend_public_key': spendPublicKey,
      'from_block': fromBlock,
      if (toBlock != null) 'to_block': toBlock,
    };
    final response = await _post('/privacy/stealth/scan', body);
    return (response as List).map((e) => StealthOutput.fromJson(e)).toList();
  }

  // ==================== Blinding ====================

  Future<String> generateBlindingFactor() async {
    final response = await _post('/privacy/blinding/generate', {});
    return response['blinding_factor'] as String;
  }

  Future<String> blindValue(String value, String blindingFactor) async {
    final body = {
      'value': value,
      'blinding_factor': blindingFactor,
    };
    final response = await _post('/privacy/blinding/blind', body);
    return response['blinded_value'] as String;
  }

  Future<String> unblindValue(String blindedValue, String blindingFactor) async {
    final body = {
      'blinded_value': blindedValue,
      'blinding_factor': blindingFactor,
    };
    final response = await _post('/privacy/blinding/unblind', body);
    return response['value'] as String;
  }

  // ==================== Lifecycle ====================

  Future<bool> healthCheck() async {
    if (_closed) return false;
    try {
      final response = await _get('/health');
      return response['status'] == 'healthy';
    } catch (_) {
      return false;
    }
  }

  void close() {
    _closed = true;
    _client.close();
  }

  bool get isClosed => _closed;

  // ==================== HTTP Helpers ====================

  Future<dynamic> _get(String path) async {
    _checkClosed();
    return _executeWithRetry(() async {
      final response = await _client
          .get(
            Uri.parse('${config.endpoint}$path'),
            headers: _headers,
          )
          .timeout(Duration(milliseconds: config.timeoutMs));
      return _handleResponse(response);
    });
  }

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

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

  dynamic _handleResponse(http.Response response) {
    final body = jsonDecode(response.body);

    if (response.statusCode >= 200 && response.statusCode < 300) {
      return body;
    }

    final message = body['message'] ?? body['error'] ?? 'Unknown error';
    final code = body['code']?.toString();
    throw PrivacyException(
      message: message,
      statusCode: response.statusCode,
      code: code,
    );
  }

  Future<T> _executeWithRetry<T>(Future<T> Function() action) async {
    Object? lastError;
    for (var attempt = 0; attempt < config.retries; attempt++) {
      try {
        return await action();
      } catch (e) {
        lastError = e;
        if (attempt < config.retries - 1) {
          await Future.delayed(Duration(seconds: 1 << attempt));
        }
      }
    }
    throw lastError ?? PrivacyException(message: 'Request failed');
  }

  void _checkClosed() {
    if (_closed) {
      throw PrivacyException(message: 'Client has been closed');
    }
  }
}

/// Privacy SDK configuration.
class PrivacyConfig {
  final String apiKey;
  final String endpoint;
  final int timeoutMs;
  final int retries;

  PrivacyConfig({
    required this.apiKey,
    this.endpoint = 'https://privacy.synor.io',
    this.timeoutMs = 30000,
    this.retries = 3,
  });
}

/// Privacy SDK exception.
class PrivacyException implements Exception {
  final String message;
  final int? statusCode;
  final String? code;

  PrivacyException({required this.message, this.statusCode, this.code});

  @override
  String toString() => 'PrivacyException: $message (code: $code, status: $statusCode)';
}
