/// Types for Synor Wallet SDK.
library synor_wallet_types;

/// Network environment for wallet operations.
enum Network {
  mainnet,
  testnet,
  devnet;

  String toJson() => name;

  static Network fromJson(String json) {
    return Network.values.firstWhere(
      (e) => e.name == json,
      orElse: () => Network.mainnet,
    );
  }
}

/// Type of wallet to create.
enum WalletType {
  standard,
  multisig,
  hardware,
  stealth;

  String toJson() => name;

  static WalletType fromJson(String json) {
    return WalletType.values.firstWhere(
      (e) => e.name == json,
      orElse: () => WalletType.standard,
    );
  }
}

/// Transaction priority for fee estimation.
enum Priority {
  low,
  medium,
  high;

  String toJson() => name;

  static Priority fromJson(String json) {
    return Priority.values.firstWhere(
      (e) => e.name == json,
      orElse: () => Priority.medium,
    );
  }
}

/// Configuration for the wallet client.
class WalletConfig {
  final String apiKey;
  final String endpoint;
  final Network network;
  final Duration timeout;
  final int retries;
  final bool debug;

  const WalletConfig({
    required this.apiKey,
    this.endpoint = 'https://wallet.synor.io/v1',
    this.network = Network.mainnet,
    this.timeout = const Duration(seconds: 30),
    this.retries = 3,
    this.debug = false,
  });
}

/// A wallet address.
class Address {
  final String address;
  final int index;
  final bool isChange;

  const Address({
    required this.address,
    required this.index,
    this.isChange = false,
  });

  factory Address.fromJson(Map<String, dynamic> json) {
    return Address(
      address: json['address'] as String,
      index: json['index'] as int,
      isChange: json['is_change'] as bool? ?? false,
    );
  }

  Map<String, dynamic> toJson() => {
        'address': address,
        'index': index,
        'is_change': isChange,
      };
}

/// A Synor wallet with addresses and keys.
class Wallet {
  final String id;
  final WalletType type;
  final Network network;
  final List<Address> addresses;
  final String createdAt;

  const Wallet({
    required this.id,
    required this.type,
    required this.network,
    required this.addresses,
    required this.createdAt,
  });

  factory Wallet.fromJson(Map<String, dynamic> json) {
    return Wallet(
      id: json['id'] as String,
      type: WalletType.fromJson(json['type'] as String),
      network: Network.fromJson(json['network'] as String),
      addresses: (json['addresses'] as List)
          .map((e) => Address.fromJson(e as Map<String, dynamic>))
          .toList(),
      createdAt: json['created_at'] as String,
    );
  }
}

/// Stealth address for privacy transactions.
class StealthAddress {
  final String spendPublicKey;
  final String viewPublicKey;
  final String oneTimeAddress;

  const StealthAddress({
    required this.spendPublicKey,
    required this.viewPublicKey,
    required this.oneTimeAddress,
  });

  factory StealthAddress.fromJson(Map<String, dynamic> json) {
    return StealthAddress(
      spendPublicKey: json['spend_public_key'] as String,
      viewPublicKey: json['view_public_key'] as String,
      oneTimeAddress: json['one_time_address'] as String,
    );
  }
}

/// Result of wallet creation.
class CreateWalletResult {
  final Wallet wallet;
  final String? mnemonic;

  const CreateWalletResult({
    required this.wallet,
    this.mnemonic,
  });

  factory CreateWalletResult.fromJson(Map<String, dynamic> json) {
    return CreateWalletResult(
      wallet: Wallet.fromJson(json['wallet'] as Map<String, dynamic>),
      mnemonic: json['mnemonic'] as String?,
    );
  }
}

/// Transaction input (UTXO reference).
class TransactionInput {
  final String txid;
  final int vout;
  final int amount;

  const TransactionInput({
    required this.txid,
    required this.vout,
    required this.amount,
  });

  Map<String, dynamic> toJson() => {
        'txid': txid,
        'vout': vout,
        'amount': amount,
      };
}

/// Transaction output.
class TransactionOutput {
  final String address;
  final int amount;

  const TransactionOutput({
    required this.address,
    required this.amount,
  });

  Map<String, dynamic> toJson() => {
        'address': address,
        'amount': amount,
      };
}

/// A blockchain transaction for signing.
class Transaction {
  final List<TransactionInput> inputs;
  final List<TransactionOutput> outputs;
  final int? fee;
  final Priority priority;

  const Transaction({
    required this.inputs,
    required this.outputs,
    this.fee,
    this.priority = Priority.medium,
  });

  Map<String, dynamic> toJson() => {
        'inputs': inputs.map((e) => e.toJson()).toList(),
        'outputs': outputs.map((e) => e.toJson()).toList(),
        if (fee != null) 'fee': fee,
        'priority': priority.toJson(),
      };
}

/// A signed transaction ready for broadcast.
class SignedTransaction {
  final String txid;
  final String hex;
  final int size;
  final int fee;

  const SignedTransaction({
    required this.txid,
    required this.hex,
    required this.size,
    required this.fee,
  });

  factory SignedTransaction.fromJson(Map<String, dynamic> json) {
    return SignedTransaction(
      txid: json['txid'] as String,
      hex: json['hex'] as String,
      size: json['size'] as int,
      fee: json['fee'] as int,
    );
  }
}

/// A cryptographic signature.
class Signature {
  final String signature;
  final String address;
  final int? recoveryId;

  const Signature({
    required this.signature,
    required this.address,
    this.recoveryId,
  });

  factory Signature.fromJson(Map<String, dynamic> json) {
    return Signature(
      signature: json['signature'] as String,
      address: json['address'] as String,
      recoveryId: json['recovery_id'] as int?,
    );
  }
}

/// Unspent transaction output.
class UTXO {
  final String txid;
  final int vout;
  final int amount;
  final String address;
  final int confirmations;
  final String? scriptPubKey;

  const UTXO({
    required this.txid,
    required this.vout,
    required this.amount,
    required this.address,
    required this.confirmations,
    this.scriptPubKey,
  });

  factory UTXO.fromJson(Map<String, dynamic> json) {
    return UTXO(
      txid: json['txid'] as String,
      vout: json['vout'] as int,
      amount: json['amount'] as int,
      address: json['address'] as String,
      confirmations: json['confirmations'] as int,
      scriptPubKey: json['script_pubkey'] as String?,
    );
  }
}

/// Wallet balance information.
class Balance {
  final int confirmed;
  final int unconfirmed;
  final int total;

  const Balance({
    required this.confirmed,
    required this.unconfirmed,
    required this.total,
  });

  factory Balance.fromJson(Map<String, dynamic> json) {
    return Balance(
      confirmed: json['confirmed'] as int,
      unconfirmed: json['unconfirmed'] as int,
      total: json['total'] as int,
    );
  }
}

/// Exception thrown by wallet operations.
class WalletException implements Exception {
  final String message;
  final String? code;
  final int? statusCode;

  const WalletException(this.message, {this.code, this.statusCode});

  @override
  String toString() => 'WalletException: $message';
}
