/// Synor Crypto SDK Types for Flutter/Dart
///
/// Quantum-resistant cryptographic types for the Synor blockchain.
library synor_crypto_types;

import 'dart:convert';
import 'dart:typed_data';

// ============================================================================
// Enumerations
// ============================================================================

/// Network type for address generation
enum Network {
  mainnet,
  testnet,
  devnet;

  String toJson() => name;
  static Network fromJson(String json) => Network.values.byName(json);
}

/// Falcon variant selection
enum FalconVariant {
  falcon512,
  falcon1024;

  String toJson() => name;
  static FalconVariant fromJson(String json) => FalconVariant.values.byName(json);

  /// Returns the signature size for this variant
  int get signatureSize => this == falcon512 ? 690 : 1330;

  /// Returns the public key size for this variant
  int get publicKeySize => this == falcon512 ? 897 : 1793;

  /// Returns the security level in bits
  int get securityLevel => this == falcon512 ? 128 : 256;
}

/// SPHINCS+ variant selection
enum SphincsVariant {
  shake128s,
  shake192s,
  shake256s;

  String toJson() => name;
  static SphincsVariant fromJson(String json) => SphincsVariant.values.byName(json);

  /// Returns the signature size for this variant
  int get signatureSize {
    switch (this) {
      case shake128s:
        return 7856;
      case shake192s:
        return 16224;
      case shake256s:
        return 29792;
    }
  }

  /// Returns the security level in bits
  int get securityLevel {
    switch (this) {
      case shake128s:
        return 128;
      case shake192s:
        return 192;
      case shake256s:
        return 256;
    }
  }
}

/// Post-quantum algorithm selection
enum PqAlgorithm {
  dilithium3,
  falcon512,
  falcon1024,
  sphincs128s,
  sphincs192s,
  sphincs256s;

  String toJson() => name;
  static PqAlgorithm fromJson(String json) => PqAlgorithm.values.byName(json);
}

/// Algorithm family classification
enum AlgorithmFamily {
  classical,
  lattice,
  hashBased,
  hybrid;

  String toJson() => name;
  static AlgorithmFamily fromJson(String json) => AlgorithmFamily.values.byName(json);
}

// ============================================================================
// Configuration Types
// ============================================================================

/// Crypto SDK configuration
class CryptoConfig {
  final String apiKey;
  final String endpoint;
  final int timeout;
  final int retries;
  final bool debug;
  final Network defaultNetwork;

  const CryptoConfig({
    required this.apiKey,
    this.endpoint = 'https://crypto.synor.io/v1',
    this.timeout = 30000,
    this.retries = 3,
    this.debug = false,
    this.defaultNetwork = Network.mainnet,
  });
}

/// Key derivation configuration
class DerivationConfig {
  final Uint8List? salt;
  final Uint8List? info;
  final int outputLength;

  const DerivationConfig({
    this.salt,
    this.info,
    this.outputLength = 32,
  });
}

/// Password derivation configuration
class PasswordDerivationConfig {
  final Uint8List salt;
  final int iterations;
  final int outputLength;

  const PasswordDerivationConfig({
    required this.salt,
    this.iterations = 100000,
    this.outputLength = 32,
  });
}

/// BIP-44 derivation path
class DerivationPath {
  static const int coinType = 0x5359; // Synor coin type

  final int account;
  final int change;
  final int index;

  const DerivationPath({
    this.account = 0,
    this.change = 0,
    this.index = 0,
  });

  factory DerivationPath.external(int account, int index) =>
      DerivationPath(account: account, change: 0, index: index);

  factory DerivationPath.internal(int account, int index) =>
      DerivationPath(account: account, change: 1, index: index);

  @override
  String toString() => "m/44'/$coinType'/$account'/$change/$index";

  Map<String, dynamic> toJson() => {
        'account': account,
        'change': change,
        'index': index,
      };
}

// ============================================================================
// Key Types
// ============================================================================

/// Hybrid public key (Ed25519 + Dilithium3)
class HybridPublicKey {
  /// Ed25519 component (32 bytes)
  final Uint8List ed25519;

  /// Dilithium3 component (~1952 bytes)
  final Uint8List dilithium;

  const HybridPublicKey({
    required this.ed25519,
    required this.dilithium,
  });

  /// Returns the total size in bytes
  int get size => ed25519.length + dilithium.length;

  factory HybridPublicKey.fromJson(Map<String, dynamic> json) => HybridPublicKey(
        ed25519: base64Decode(json['ed25519'] as String),
        dilithium: base64Decode(json['dilithium'] as String),
      );

  Map<String, dynamic> toJson() => {
        'ed25519': base64Encode(ed25519),
        'dilithium': base64Encode(dilithium),
      };
}

/// Secret key (master seed)
class SecretKey {
  /// Ed25519 seed (32 bytes)
  final Uint8List ed25519Seed;

  /// Master seed (64 bytes)
  final Uint8List masterSeed;

  const SecretKey({
    required this.ed25519Seed,
    required this.masterSeed,
  });

  factory SecretKey.fromJson(Map<String, dynamic> json) => SecretKey(
        ed25519Seed: base64Decode(json['ed25519_seed'] as String),
        masterSeed: base64Decode(json['master_seed'] as String),
      );
}

/// Falcon public key
class FalconPublicKey {
  final FalconVariant variant;
  final Uint8List bytes;

  const FalconPublicKey({required this.variant, required this.bytes});

  factory FalconPublicKey.fromJson(Map<String, dynamic> json) => FalconPublicKey(
        variant: FalconVariant.fromJson(json['variant'] as String),
        bytes: base64Decode(json['bytes'] as String),
      );
}

/// Falcon secret key
class FalconSecretKey {
  final FalconVariant variant;
  final Uint8List bytes;

  const FalconSecretKey({required this.variant, required this.bytes});

  factory FalconSecretKey.fromJson(Map<String, dynamic> json) => FalconSecretKey(
        variant: FalconVariant.fromJson(json['variant'] as String),
        bytes: base64Decode(json['bytes'] as String),
      );
}

/// SPHINCS+ public key
class SphincsPublicKey {
  final SphincsVariant variant;
  final Uint8List bytes;

  const SphincsPublicKey({required this.variant, required this.bytes});

  factory SphincsPublicKey.fromJson(Map<String, dynamic> json) => SphincsPublicKey(
        variant: SphincsVariant.fromJson(json['variant'] as String),
        bytes: base64Decode(json['bytes'] as String),
      );
}

/// SPHINCS+ secret key
class SphincsSecretKey {
  final SphincsVariant variant;
  final Uint8List bytes;

  const SphincsSecretKey({required this.variant, required this.bytes});

  factory SphincsSecretKey.fromJson(Map<String, dynamic> json) => SphincsSecretKey(
        variant: SphincsVariant.fromJson(json['variant'] as String),
        bytes: base64Decode(json['bytes'] as String),
      );
}

// ============================================================================
// Signature Types
// ============================================================================

/// Hybrid signature (Ed25519 + Dilithium3)
class HybridSignature {
  /// Ed25519 component (64 bytes)
  final Uint8List ed25519;

  /// Dilithium3 component (~3293 bytes)
  final Uint8List dilithium;

  const HybridSignature({
    required this.ed25519,
    required this.dilithium,
  });

  /// Returns the total size in bytes
  int get size => ed25519.length + dilithium.length;

  /// Serializes to bytes
  Uint8List toBytes() {
    final result = Uint8List(size);
    result.setAll(0, ed25519);
    result.setAll(ed25519.length, dilithium);
    return result;
  }

  factory HybridSignature.fromBytes(Uint8List data) {
    if (data.length < 64) {
      throw ArgumentError('Invalid signature length');
    }
    return HybridSignature(
      ed25519: Uint8List.fromList(data.sublist(0, 64)),
      dilithium: Uint8List.fromList(data.sublist(64)),
    );
  }

  factory HybridSignature.fromJson(Map<String, dynamic> json) => HybridSignature(
        ed25519: base64Decode(json['ed25519'] as String),
        dilithium: base64Decode(json['dilithium'] as String),
      );

  Map<String, dynamic> toJson() => {
        'ed25519': base64Encode(ed25519),
        'dilithium': base64Encode(dilithium),
      };
}

/// Falcon signature
class FalconSignature {
  final FalconVariant variant;
  final Uint8List bytes;

  const FalconSignature({required this.variant, required this.bytes});

  int get size => bytes.length;

  factory FalconSignature.fromJson(Map<String, dynamic> json) => FalconSignature(
        variant: FalconVariant.fromJson(json['variant'] as String),
        bytes: base64Decode(json['signature'] as String),
      );
}

/// SPHINCS+ signature
class SphincsSignature {
  final SphincsVariant variant;
  final Uint8List bytes;

  const SphincsSignature({required this.variant, required this.bytes});

  int get size => bytes.length;

  factory SphincsSignature.fromJson(Map<String, dynamic> json) => SphincsSignature(
        variant: SphincsVariant.fromJson(json['variant'] as String),
        bytes: base64Decode(json['signature'] as String),
      );
}

// ============================================================================
// Keypair Types
// ============================================================================

/// Hybrid keypair (Ed25519 + Dilithium3)
class HybridKeypair {
  final HybridPublicKey publicKey;
  final SecretKey secretKey;
  final Map<Network, String> _addresses;

  const HybridKeypair({
    required this.publicKey,
    required this.secretKey,
    Map<Network, String> addresses = const {},
  }) : _addresses = addresses;

  /// Returns the address for a network
  String address(Network network) => _addresses[network] ?? '';

  factory HybridKeypair.fromJson(Map<String, dynamic> json) {
    final addressesJson = json['addresses'] as Map<String, dynamic>? ?? {};
    final addresses = <Network, String>{};
    addressesJson.forEach((k, v) {
      try {
        addresses[Network.fromJson(k)] = v as String;
      } catch (_) {}
    });

    return HybridKeypair(
      publicKey: HybridPublicKey.fromJson(json['public_key'] as Map<String, dynamic>),
      secretKey: SecretKey.fromJson(json['secret_key'] as Map<String, dynamic>),
      addresses: addresses,
    );
  }
}

/// Falcon keypair
class FalconKeypair {
  final FalconVariant variant;
  final FalconPublicKey publicKey;
  final FalconSecretKey secretKey;

  const FalconKeypair({
    required this.variant,
    required this.publicKey,
    required this.secretKey,
  });

  factory FalconKeypair.fromJson(Map<String, dynamic> json) => FalconKeypair(
        variant: FalconVariant.fromJson(json['variant'] as String),
        publicKey: FalconPublicKey.fromJson(json['public_key'] as Map<String, dynamic>),
        secretKey: FalconSecretKey.fromJson(json['secret_key'] as Map<String, dynamic>),
      );
}

/// SPHINCS+ keypair
class SphincsKeypair {
  final SphincsVariant variant;
  final SphincsPublicKey publicKey;
  final SphincsSecretKey secretKey;

  const SphincsKeypair({
    required this.variant,
    required this.publicKey,
    required this.secretKey,
  });

  factory SphincsKeypair.fromJson(Map<String, dynamic> json) => SphincsKeypair(
        variant: SphincsVariant.fromJson(json['variant'] as String),
        publicKey: SphincsPublicKey.fromJson(json['public_key'] as Map<String, dynamic>),
        secretKey: SphincsSecretKey.fromJson(json['secret_key'] as Map<String, dynamic>),
      );
}

// ============================================================================
// Mnemonic Types
// ============================================================================

/// BIP-39 mnemonic phrase
class Mnemonic {
  final String phrase;
  final List<String> words;
  final int wordCount;
  final Uint8List entropy;

  const Mnemonic({
    required this.phrase,
    required this.words,
    required this.wordCount,
    required this.entropy,
  });

  factory Mnemonic.fromJson(Map<String, dynamic> json) => Mnemonic(
        phrase: json['phrase'] as String,
        words: List<String>.from(json['words'] ?? (json['phrase'] as String).split(' ')),
        wordCount: json['word_count'] as int? ?? (json['phrase'] as String).split(' ').length,
        entropy: json['entropy'] != null ? base64Decode(json['entropy'] as String) : Uint8List(0),
      );
}

/// Mnemonic validation result
class MnemonicValidation {
  final bool valid;
  final String? error;

  const MnemonicValidation({required this.valid, this.error});

  factory MnemonicValidation.fromJson(Map<String, dynamic> json) => MnemonicValidation(
        valid: json['valid'] as bool,
        error: json['error'] as String?,
      );
}

// ============================================================================
// Address Types
// ============================================================================

/// Blockchain address
class Address {
  final String address;
  final Network network;
  final Uint8List pubkeyHash;

  const Address({
    required this.address,
    required this.network,
    required this.pubkeyHash,
  });

  factory Address.fromJson(Map<String, dynamic> json) => Address(
        address: json['address'] as String,
        network: Network.fromJson(json['network'] as String),
        pubkeyHash: json['pubkey_hash'] != null
            ? base64Decode(json['pubkey_hash'] as String)
            : Uint8List(0),
      );
}

// ============================================================================
// Hash Types
// ============================================================================

/// 256-bit hash
class Hash256 {
  final Uint8List bytes;
  final String hex;

  const Hash256({required this.bytes, required this.hex});

  factory Hash256.fromJson(Map<String, dynamic> json) {
    final hexStr = json['hash'] as String;
    return Hash256(
      bytes: _hexDecode(hexStr),
      hex: hexStr,
    );
  }
}

Uint8List _hexDecode(String hex) {
  final result = Uint8List(hex.length ~/ 2);
  for (var i = 0; i < result.length; i++) {
    result[i] = int.parse(hex.substring(i * 2, i * 2 + 2), radix: 16);
  }
  return result;
}

// ============================================================================
// Negotiation Types
// ============================================================================

/// Algorithm capabilities for negotiation
class AlgorithmCapabilities {
  final List<PqAlgorithm> pqAlgorithms;
  final bool classical;
  final bool hybrid;
  final PqAlgorithm? preferred;

  const AlgorithmCapabilities({
    required this.pqAlgorithms,
    this.classical = true,
    this.hybrid = true,
    this.preferred,
  });

  factory AlgorithmCapabilities.fromJson(Map<String, dynamic> json) => AlgorithmCapabilities(
        pqAlgorithms: (json['pq_algorithms'] as List)
            .map((e) => PqAlgorithm.fromJson(e as String))
            .toList(),
        classical: json['classical'] as bool? ?? true,
        hybrid: json['hybrid'] as bool? ?? true,
        preferred: json['preferred'] != null
            ? PqAlgorithm.fromJson(json['preferred'] as String)
            : null,
      );
}

/// Negotiation policy
class NegotiationPolicy {
  final int minSecurityLevel;
  final bool preferCompact;
  final bool allowClassical;
  final List<AlgorithmFamily> requiredFamilies;

  const NegotiationPolicy({
    this.minSecurityLevel = 128,
    this.preferCompact = false,
    this.allowClassical = false,
    this.requiredFamilies = const [],
  });

  Map<String, dynamic> toJson() => {
        'min_security_level': minSecurityLevel,
        'prefer_compact': preferCompact,
        'allow_classical': allowClassical,
        'required_families': requiredFamilies.map((e) => e.toJson()).toList(),
      };
}

/// Negotiation result
class NegotiationResult {
  final PqAlgorithm algorithm;
  final int securityLevel;
  final AlgorithmFamily family;
  final bool hybrid;

  const NegotiationResult({
    required this.algorithm,
    required this.securityLevel,
    required this.family,
    required this.hybrid,
  });

  factory NegotiationResult.fromJson(Map<String, dynamic> json) => NegotiationResult(
        algorithm: PqAlgorithm.fromJson(json['algorithm'] as String),
        securityLevel: json['security_level'] as int,
        family: AlgorithmFamily.fromJson(json['family'] as String),
        hybrid: json['hybrid'] as bool,
      );
}

/// Session parameters after negotiation
class SessionParams {
  final PqAlgorithm algorithm;
  final Uint8List? sessionKey;
  final int? expiresAt;

  const SessionParams({
    required this.algorithm,
    this.sessionKey,
    this.expiresAt,
  });

  factory SessionParams.fromJson(Map<String, dynamic> json) => SessionParams(
        algorithm: PqAlgorithm.fromJson(json['algorithm'] as String),
        sessionKey: json['session_key'] != null
            ? base64Decode(json['session_key'] as String)
            : null,
        expiresAt: json['expires_at'] as int?,
      );
}

// ============================================================================
// Error Types
// ============================================================================

/// Crypto operation error
class CryptoException implements Exception {
  final String message;
  final String? code;

  const CryptoException(this.message, [this.code]);

  @override
  String toString() =>
      code != null ? 'CryptoException: $message (code: $code)' : 'CryptoException: $message';
}

// ============================================================================
// Constants
// ============================================================================

abstract class CryptoConstants {
  static const int ed25519PublicKeySize = 32;
  static const int ed25519SecretKeySize = 32;
  static const int ed25519SignatureSize = 64;
  static const int dilithium3PublicKeySize = 1952;
  static const int dilithium3SignatureSize = 3293;
  static const int hybridSignatureSize = 64 + 3293;
  static const int falcon512SignatureSize = 690;
  static const int falcon512PublicKeySize = 897;
  static const int falcon1024SignatureSize = 1330;
  static const int falcon1024PublicKeySize = 1793;
  static const int sphincs128sSignatureSize = 7856;
  static const int sphincs192sSignatureSize = 16224;
  static const int sphincs256sSignatureSize = 29792;
  static const int coinType = 0x5359;
  static const int minPbkdf2Iterations = 10000;
  static const int minSaltLength = 8;
  static const String defaultEndpoint = 'https://crypto.synor.io/v1';
}
