/// Synor Crypto SDK Client for Flutter/Dart
///
/// Quantum-resistant cryptographic primitives for the Synor blockchain.
library synor_crypto;

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

export 'types.dart';

/// Main Synor Crypto client
class SynorCrypto {
  final CryptoConfig _config;
  final http.Client _client;
  bool _closed = false;

  late final MnemonicClient mnemonic;
  late final KeypairClient keypairs;
  late final SigningClient signing;
  late final FalconClient falcon;
  late final SphincsClient sphincs;
  late final KdfClient kdf;
  late final HashClient hash;
  late final NegotiationClient negotiation;

  SynorCrypto(this._config) : _client = http.Client() {
    mnemonic = MnemonicClient(this);
    keypairs = KeypairClient(this);
    signing = SigningClient(this);
    falcon = FalconClient(this);
    sphincs = SphincsClient(this);
    kdf = KdfClient(this);
    hash = HashClient(this);
    negotiation = NegotiationClient(this);
  }

  /// Returns the default network
  Network get defaultNetwork => _config.defaultNetwork;

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

  /// Gets service info
  Future<Map<String, dynamic>> getInfo() => _get('/info');

  /// Closes the client
  void close() {
    _closed = true;
    _client.close();
  }

  /// Returns true if closed
  bool get isClosed => _closed;

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

  Future<Map<String, dynamic>> _post(String path, [Map<String, dynamic>? body]) async {
    _checkClosed();
    final response = await _client.post(
      Uri.parse('${_config.endpoint}$path'),
      headers: _headers(),
      body: body != null ? jsonEncode(body) : null,
    );
    return _handleResponse(response);
  }

  Map<String, String> _headers() => {
        'Authorization': 'Bearer ${_config.apiKey}',
        'Content-Type': 'application/json',
        'X-SDK-Version': 'dart/0.1.0',
      };

  Map<String, dynamic> _handleResponse(http.Response response) {
    final json = jsonDecode(response.body) as Map<String, dynamic>;
    if (response.statusCode >= 400) {
      throw CryptoException(
        json['message'] as String? ?? 'HTTP ${response.statusCode}',
        json['code'] as String?,
      );
    }
    return json;
  }

  void _checkClosed() {
    if (_closed) {
      throw const CryptoException('Client has been closed', 'CLIENT_CLOSED');
    }
  }
}

/// Mnemonic operations
class MnemonicClient {
  final SynorCrypto _crypto;
  MnemonicClient(this._crypto);

  /// Generates a new random mnemonic
  Future<Mnemonic> generate([int wordCount = 24]) async {
    final result = await _crypto._post('/mnemonic/generate', {'word_count': wordCount});
    return Mnemonic.fromJson(result);
  }

  /// Creates a mnemonic from a phrase
  Future<Mnemonic> fromPhrase(String phrase) async {
    final result = await _crypto._post('/mnemonic/from-phrase', {'phrase': phrase});
    return Mnemonic.fromJson(result);
  }

  /// Creates a mnemonic from entropy
  Future<Mnemonic> fromEntropy(Uint8List entropy) async {
    final result = await _crypto._post('/mnemonic/from-entropy', {
      'entropy': base64Encode(entropy),
    });
    return Mnemonic.fromJson(result);
  }

  /// Validates a mnemonic phrase
  Future<MnemonicValidation> validate(String phrase) async {
    final result = await _crypto._post('/mnemonic/validate', {'phrase': phrase});
    return MnemonicValidation.fromJson(result);
  }

  /// Derives a seed from a mnemonic
  Future<Uint8List> toSeed(String phrase, [String passphrase = '']) async {
    final result = await _crypto._post('/mnemonic/to-seed', {
      'phrase': phrase,
      'passphrase': passphrase,
    });
    return base64Decode(result['seed'] as String);
  }

  /// Suggests word completions
  Future<List<String>> suggestWords(String partial, [int limit = 10]) async {
    final result = await _crypto._post('/mnemonic/suggest', {
      'partial': partial,
      'limit': limit,
    });
    return List<String>.from(result['suggestions'] as List);
  }
}

/// Keypair operations
class KeypairClient {
  final SynorCrypto _crypto;
  KeypairClient(this._crypto);

  /// Generates a new random keypair
  Future<HybridKeypair> generate() async {
    final result = await _crypto._post('/keypair/generate');
    return HybridKeypair.fromJson(result);
  }

  /// Creates a keypair from a mnemonic
  Future<HybridKeypair> fromMnemonic(String phrase, [String passphrase = '']) async {
    final result = await _crypto._post('/keypair/from-mnemonic', {
      'phrase': phrase,
      'passphrase': passphrase,
    });
    return HybridKeypair.fromJson(result);
  }

  /// Creates a keypair from a seed
  Future<HybridKeypair> fromSeed(Uint8List seed) async {
    final result = await _crypto._post('/keypair/from-seed', {
      'seed': base64Encode(seed),
    });
    return HybridKeypair.fromJson(result);
  }

  /// Derives a child keypair
  Future<HybridKeypair> derive(HybridKeypair parent, DerivationPath path) async {
    final result = await _crypto._post('/keypair/derive', {
      'public_key': parent.publicKey.toJson(),
      'path': path.toJson(),
    });
    return HybridKeypair.fromJson(result);
  }

  /// Gets the address for a public key
  Future<Address> getAddress(HybridPublicKey publicKey, Network network) async {
    final result = await _crypto._post('/keypair/address', {
      'public_key': publicKey.toJson(),
      'network': network.toJson(),
    });
    return Address.fromJson(result);
  }
}

/// Signing operations
class SigningClient {
  final SynorCrypto _crypto;
  SigningClient(this._crypto);

  /// Signs a message with a hybrid keypair
  Future<HybridSignature> sign(HybridKeypair keypair, Uint8List message) async {
    final result = await _crypto._post('/sign/hybrid', {
      'secret_key': {
        'ed25519_seed': base64Encode(keypair.secretKey.ed25519Seed),
        'master_seed': base64Encode(keypair.secretKey.masterSeed),
      },
      'message': base64Encode(message),
    });
    return HybridSignature.fromJson(result);
  }

  /// Verifies a hybrid signature
  Future<bool> verify(
    HybridPublicKey publicKey,
    Uint8List message,
    HybridSignature signature,
  ) async {
    final result = await _crypto._post('/sign/verify', {
      'public_key': publicKey.toJson(),
      'message': base64Encode(message),
      'signature': signature.toJson(),
    });
    return result['valid'] as bool;
  }

  /// Signs with Ed25519 only
  Future<Uint8List> signEd25519(Uint8List secretKey, Uint8List message) async {
    final result = await _crypto._post('/sign/ed25519', {
      'secret_key': base64Encode(secretKey),
      'message': base64Encode(message),
    });
    return base64Decode(result['signature'] as String);
  }

  /// Verifies an Ed25519 signature
  Future<bool> verifyEd25519(
    Uint8List publicKey,
    Uint8List message,
    Uint8List signature,
  ) async {
    final result = await _crypto._post('/sign/verify-ed25519', {
      'public_key': base64Encode(publicKey),
      'message': base64Encode(message),
      'signature': base64Encode(signature),
    });
    return result['valid'] as bool;
  }
}

/// Falcon operations
class FalconClient {
  final SynorCrypto _crypto;
  FalconClient(this._crypto);

  /// Generates a Falcon keypair
  Future<FalconKeypair> generate([FalconVariant variant = FalconVariant.falcon512]) async {
    final result = await _crypto._post('/falcon/generate', {'variant': variant.toJson()});
    return FalconKeypair.fromJson(result);
  }

  /// Signs with Falcon
  Future<FalconSignature> sign(FalconKeypair keypair, Uint8List message) async {
    final result = await _crypto._post('/falcon/sign', {
      'variant': keypair.variant.toJson(),
      'secret_key': base64Encode(keypair.secretKey.bytes),
      'message': base64Encode(message),
    });
    return FalconSignature.fromJson(result);
  }

  /// Verifies a Falcon signature
  Future<bool> verify(
    Uint8List publicKey,
    Uint8List message,
    FalconSignature signature,
  ) async {
    final result = await _crypto._post('/falcon/verify', {
      'variant': signature.variant.toJson(),
      'public_key': base64Encode(publicKey),
      'message': base64Encode(message),
      'signature': base64Encode(signature.bytes),
    });
    return result['valid'] as bool;
  }
}

/// SPHINCS+ operations
class SphincsClient {
  final SynorCrypto _crypto;
  SphincsClient(this._crypto);

  /// Generates a SPHINCS+ keypair
  Future<SphincsKeypair> generate([SphincsVariant variant = SphincsVariant.shake128s]) async {
    final result = await _crypto._post('/sphincs/generate', {'variant': variant.toJson()});
    return SphincsKeypair.fromJson(result);
  }

  /// Signs with SPHINCS+
  Future<SphincsSignature> sign(SphincsKeypair keypair, Uint8List message) async {
    final result = await _crypto._post('/sphincs/sign', {
      'variant': keypair.variant.toJson(),
      'secret_key': base64Encode(keypair.secretKey.bytes),
      'message': base64Encode(message),
    });
    return SphincsSignature.fromJson(result);
  }

  /// Verifies a SPHINCS+ signature
  Future<bool> verify(
    Uint8List publicKey,
    Uint8List message,
    SphincsSignature signature,
  ) async {
    final result = await _crypto._post('/sphincs/verify', {
      'variant': signature.variant.toJson(),
      'public_key': base64Encode(publicKey),
      'message': base64Encode(message),
      'signature': base64Encode(signature.bytes),
    });
    return result['valid'] as bool;
  }
}

/// Key derivation operations
class KdfClient {
  final SynorCrypto _crypto;
  KdfClient(this._crypto);

  /// Derives a key using HKDF
  Future<Uint8List> deriveKey(Uint8List seed, [DerivationConfig? config]) async {
    config ??= const DerivationConfig();
    final body = <String, dynamic>{
      'seed': base64Encode(seed),
      'output_length': config.outputLength,
    };
    if (config.salt != null) body['salt'] = base64Encode(config.salt!);
    if (config.info != null) body['info'] = base64Encode(config.info!);

    final result = await _crypto._post('/kdf/hkdf', body);
    return base64Decode(result['key'] as String);
  }

  /// Derives a key from a password
  Future<Uint8List> deriveFromPassword(
    String password,
    PasswordDerivationConfig config,
  ) async {
    final result = await _crypto._post('/kdf/pbkdf2', {
      'password': base64Encode(utf8.encode(password)),
      'salt': base64Encode(config.salt),
      'iterations': config.iterations,
      'output_length': config.outputLength,
    });
    return base64Decode(result['key'] as String);
  }

  /// Derives a child key
  Future<({Uint8List key, Uint8List chainCode})> deriveChildKey(
    Uint8List parentKey,
    Uint8List chainCode,
    int index,
  ) async {
    final result = await _crypto._post('/kdf/child', {
      'parent_key': base64Encode(parentKey),
      'chain_code': base64Encode(chainCode),
      'index': index,
    });
    return (
      key: base64Decode(result['key'] as String),
      chainCode: base64Decode(result['chain_code'] as String),
    );
  }
}

/// Hashing operations
class HashClient {
  final SynorCrypto _crypto;
  HashClient(this._crypto);

  /// Computes SHA3-256 hash
  Future<Hash256> sha3_256(Uint8List data) async {
    final result = await _crypto._post('/hash/sha3-256', {
      'data': base64Encode(data),
    });
    return Hash256.fromJson(result);
  }

  /// Computes BLAKE3 hash
  Future<Hash256> blake3(Uint8List data) async {
    final result = await _crypto._post('/hash/blake3', {
      'data': base64Encode(data),
    });
    return Hash256.fromJson(result);
  }

  /// Computes Keccak-256 hash
  Future<Hash256> keccak256(Uint8List data) async {
    final result = await _crypto._post('/hash/keccak256', {
      'data': base64Encode(data),
    });
    return Hash256.fromJson(result);
  }

  /// Combines multiple hashes
  Future<Hash256> combine(List<Uint8List> hashes) async {
    final result = await _crypto._post('/hash/combine', {
      'hashes': hashes.map(base64Encode).toList(),
    });
    return Hash256.fromJson(result);
  }
}

/// Algorithm negotiation operations
class NegotiationClient {
  final SynorCrypto _crypto;
  NegotiationClient(this._crypto);

  /// Gets local capabilities
  Future<AlgorithmCapabilities> getCapabilities() async {
    final result = await _crypto._get('/negotiation/capabilities');
    return AlgorithmCapabilities.fromJson(result);
  }

  /// Negotiates with a peer
  Future<NegotiationResult> negotiate(
    AlgorithmCapabilities peerCapabilities,
    NegotiationPolicy policy,
  ) async {
    final result = await _crypto._post('/negotiation/negotiate', {
      'peer_capabilities': {
        'pq_algorithms': peerCapabilities.pqAlgorithms.map((e) => e.toJson()).toList(),
        'classical': peerCapabilities.classical,
        'hybrid': peerCapabilities.hybrid,
        'preferred': peerCapabilities.preferred?.toJson(),
      },
      'policy': policy.toJson(),
    });
    return NegotiationResult.fromJson(result);
  }

  /// Establishes session parameters
  Future<SessionParams> establishSession(
    NegotiationResult result,
    Uint8List peerPublicKey,
  ) async {
    final resp = await _crypto._post('/negotiation/session', {
      'negotiation_result': {
        'algorithm': result.algorithm.toJson(),
        'security_level': result.securityLevel,
        'family': result.family.toJson(),
        'hybrid': result.hybrid,
      },
      'peer_public_key': base64Encode(peerPublicKey),
    });
    return SessionParams.fromJson(resp);
  }
}
