/// Synor Bridge SDK Types for Flutter
///
/// Cross-chain asset transfers with lock-mint and burn-unlock patterns.
library synor_bridge_types;

// ==================== Chain Types ====================

/// Supported blockchain networks
enum ChainId { synor, ethereum, polygon, arbitrum, optimism, bsc, avalanche, solana, cosmos }

/// Native currency info
class NativeCurrency {
  final String name;
  final String symbol;
  final int decimals;

  const NativeCurrency({required this.name, required this.symbol, required this.decimals});

  factory NativeCurrency.fromJson(Map<String, dynamic> json) => NativeCurrency(
        name: json['name'] as String,
        symbol: json['symbol'] as String,
        decimals: json['decimals'] as int,
      );
}

/// Chain information
class Chain {
  final ChainId id;
  final String name;
  final int chainId;
  final String rpcUrl;
  final String explorerUrl;
  final NativeCurrency nativeCurrency;
  final int confirmations;
  final int estimatedBlockTime;
  final bool supported;

  const Chain({
    required this.id,
    required this.name,
    required this.chainId,
    required this.rpcUrl,
    required this.explorerUrl,
    required this.nativeCurrency,
    required this.confirmations,
    required this.estimatedBlockTime,
    required this.supported,
  });

  factory Chain.fromJson(Map<String, dynamic> json) => Chain(
        id: ChainId.values.firstWhere((e) => e.name == json['id']),
        name: json['name'] as String,
        chainId: json['chainId'] as int,
        rpcUrl: json['rpcUrl'] as String,
        explorerUrl: json['explorerUrl'] as String,
        nativeCurrency: NativeCurrency.fromJson(json['nativeCurrency']),
        confirmations: json['confirmations'] as int,
        estimatedBlockTime: json['estimatedBlockTime'] as int,
        supported: json['supported'] as bool,
      );
}

// ==================== Asset Types ====================

/// Asset type
enum AssetType { native, erc20, erc721, erc1155 }

/// Asset information
class Asset {
  final String id;
  final String symbol;
  final String name;
  final AssetType type;
  final ChainId chain;
  final String? contractAddress;
  final int decimals;
  final String? logoUrl;
  final bool verified;

  const Asset({
    required this.id,
    required this.symbol,
    required this.name,
    required this.type,
    required this.chain,
    this.contractAddress,
    required this.decimals,
    this.logoUrl,
    this.verified = false,
  });

  factory Asset.fromJson(Map<String, dynamic> json) => Asset(
        id: json['id'] as String,
        symbol: json['symbol'] as String,
        name: json['name'] as String,
        type: AssetType.values.firstWhere((e) => e.name == json['type']),
        chain: ChainId.values.firstWhere((e) => e.name == json['chain']),
        contractAddress: json['contractAddress'] as String?,
        decimals: json['decimals'] as int,
        logoUrl: json['logoUrl'] as String?,
        verified: json['verified'] as bool? ?? false,
      );

  Map<String, dynamic> toJson() => {
        'id': id,
        'symbol': symbol,
        'name': name,
        'type': type.name,
        'chain': chain.name,
        if (contractAddress != null) 'contractAddress': contractAddress,
        'decimals': decimals,
        if (logoUrl != null) 'logoUrl': logoUrl,
        'verified': verified,
      };
}

/// Wrapped asset mapping
class WrappedAsset {
  final Asset originalAsset;
  final Asset wrappedAsset;
  final ChainId chain;
  final String bridgeContract;

  const WrappedAsset({
    required this.originalAsset,
    required this.wrappedAsset,
    required this.chain,
    required this.bridgeContract,
  });

  factory WrappedAsset.fromJson(Map<String, dynamic> json) => WrappedAsset(
        originalAsset: Asset.fromJson(json['originalAsset']),
        wrappedAsset: Asset.fromJson(json['wrappedAsset']),
        chain: ChainId.values.firstWhere((e) => e.name == json['chain']),
        bridgeContract: json['bridgeContract'] as String,
      );
}

// ==================== Transfer Types ====================

/// Transfer status
enum TransferStatus { pending, locked, confirming, minting, completed, failed, refunded }

/// Transfer direction
enum TransferDirection { lockMint, burnUnlock }

/// Validator signature
class ValidatorSignature {
  final String validator;
  final String signature;
  final int timestamp;

  const ValidatorSignature({
    required this.validator,
    required this.signature,
    required this.timestamp,
  });

  factory ValidatorSignature.fromJson(Map<String, dynamic> json) => ValidatorSignature(
        validator: json['validator'] as String,
        signature: json['signature'] as String,
        timestamp: json['timestamp'] as int,
      );

  Map<String, dynamic> toJson() => {
        'validator': validator,
        'signature': signature,
        'timestamp': timestamp,
      };
}

/// Lock receipt
class LockReceipt {
  final String id;
  final String txHash;
  final ChainId sourceChain;
  final ChainId targetChain;
  final Asset asset;
  final String amount;
  final String sender;
  final String recipient;
  final int lockTimestamp;
  final int confirmations;
  final int requiredConfirmations;

  const LockReceipt({
    required this.id,
    required this.txHash,
    required this.sourceChain,
    required this.targetChain,
    required this.asset,
    required this.amount,
    required this.sender,
    required this.recipient,
    required this.lockTimestamp,
    required this.confirmations,
    required this.requiredConfirmations,
  });

  factory LockReceipt.fromJson(Map<String, dynamic> json) => LockReceipt(
        id: json['id'] as String,
        txHash: json['txHash'] as String,
        sourceChain: ChainId.values.firstWhere((e) => e.name == json['sourceChain']),
        targetChain: ChainId.values.firstWhere((e) => e.name == json['targetChain']),
        asset: Asset.fromJson(json['asset']),
        amount: json['amount'] as String,
        sender: json['sender'] as String,
        recipient: json['recipient'] as String,
        lockTimestamp: json['lockTimestamp'] as int,
        confirmations: json['confirmations'] as int,
        requiredConfirmations: json['requiredConfirmations'] as int,
      );

  Map<String, dynamic> toJson() => {
        'id': id,
        'txHash': txHash,
        'sourceChain': sourceChain.name,
        'targetChain': targetChain.name,
        'asset': asset.toJson(),
        'amount': amount,
        'sender': sender,
        'recipient': recipient,
        'lockTimestamp': lockTimestamp,
        'confirmations': confirmations,
        'requiredConfirmations': requiredConfirmations,
      };
}

/// Lock proof
class LockProof {
  final LockReceipt lockReceipt;
  final List<String> merkleProof;
  final String blockHeader;
  final List<ValidatorSignature> signatures;

  const LockProof({
    required this.lockReceipt,
    required this.merkleProof,
    required this.blockHeader,
    required this.signatures,
  });

  factory LockProof.fromJson(Map<String, dynamic> json) => LockProof(
        lockReceipt: LockReceipt.fromJson(json['lockReceipt']),
        merkleProof: List<String>.from(json['merkleProof']),
        blockHeader: json['blockHeader'] as String,
        signatures: (json['signatures'] as List)
            .map((e) => ValidatorSignature.fromJson(e))
            .toList(),
      );

  Map<String, dynamic> toJson() => {
        'lockReceipt': lockReceipt.toJson(),
        'merkleProof': merkleProof,
        'blockHeader': blockHeader,
        'signatures': signatures.map((s) => s.toJson()).toList(),
      };
}

/// Burn receipt
class BurnReceipt {
  final String id;
  final String txHash;
  final ChainId sourceChain;
  final ChainId targetChain;
  final Asset wrappedAsset;
  final Asset originalAsset;
  final String amount;
  final String sender;
  final String recipient;
  final int burnTimestamp;
  final int confirmations;
  final int requiredConfirmations;

  const BurnReceipt({
    required this.id,
    required this.txHash,
    required this.sourceChain,
    required this.targetChain,
    required this.wrappedAsset,
    required this.originalAsset,
    required this.amount,
    required this.sender,
    required this.recipient,
    required this.burnTimestamp,
    required this.confirmations,
    required this.requiredConfirmations,
  });

  factory BurnReceipt.fromJson(Map<String, dynamic> json) => BurnReceipt(
        id: json['id'] as String,
        txHash: json['txHash'] as String,
        sourceChain: ChainId.values.firstWhere((e) => e.name == json['sourceChain']),
        targetChain: ChainId.values.firstWhere((e) => e.name == json['targetChain']),
        wrappedAsset: Asset.fromJson(json['wrappedAsset']),
        originalAsset: Asset.fromJson(json['originalAsset']),
        amount: json['amount'] as String,
        sender: json['sender'] as String,
        recipient: json['recipient'] as String,
        burnTimestamp: json['burnTimestamp'] as int,
        confirmations: json['confirmations'] as int,
        requiredConfirmations: json['requiredConfirmations'] as int,
      );

  Map<String, dynamic> toJson() => {
        'id': id,
        'txHash': txHash,
        'sourceChain': sourceChain.name,
        'targetChain': targetChain.name,
        'wrappedAsset': wrappedAsset.toJson(),
        'originalAsset': originalAsset.toJson(),
        'amount': amount,
        'sender': sender,
        'recipient': recipient,
        'burnTimestamp': burnTimestamp,
        'confirmations': confirmations,
        'requiredConfirmations': requiredConfirmations,
      };
}

/// Burn proof
class BurnProof {
  final BurnReceipt burnReceipt;
  final List<String> merkleProof;
  final String blockHeader;
  final List<ValidatorSignature> signatures;

  const BurnProof({
    required this.burnReceipt,
    required this.merkleProof,
    required this.blockHeader,
    required this.signatures,
  });

  factory BurnProof.fromJson(Map<String, dynamic> json) => BurnProof(
        burnReceipt: BurnReceipt.fromJson(json['burnReceipt']),
        merkleProof: List<String>.from(json['merkleProof']),
        blockHeader: json['blockHeader'] as String,
        signatures: (json['signatures'] as List)
            .map((e) => ValidatorSignature.fromJson(e))
            .toList(),
      );

  Map<String, dynamic> toJson() => {
        'burnReceipt': burnReceipt.toJson(),
        'merkleProof': merkleProof,
        'blockHeader': blockHeader,
        'signatures': signatures.map((s) => s.toJson()).toList(),
      };
}

/// Transfer record
class Transfer {
  final String id;
  final TransferDirection direction;
  final TransferStatus status;
  final ChainId sourceChain;
  final ChainId targetChain;
  final Asset asset;
  final String amount;
  final String sender;
  final String recipient;
  final String? sourceTxHash;
  final String? targetTxHash;
  final String fee;
  final Asset feeAsset;
  final int createdAt;
  final int updatedAt;
  final int? completedAt;
  final String? errorMessage;

  const Transfer({
    required this.id,
    required this.direction,
    required this.status,
    required this.sourceChain,
    required this.targetChain,
    required this.asset,
    required this.amount,
    required this.sender,
    required this.recipient,
    this.sourceTxHash,
    this.targetTxHash,
    required this.fee,
    required this.feeAsset,
    required this.createdAt,
    required this.updatedAt,
    this.completedAt,
    this.errorMessage,
  });

  factory Transfer.fromJson(Map<String, dynamic> json) => Transfer(
        id: json['id'] as String,
        direction: json['direction'] == 'lock_mint'
            ? TransferDirection.lockMint
            : TransferDirection.burnUnlock,
        status: TransferStatus.values.firstWhere((e) => e.name == json['status']),
        sourceChain: ChainId.values.firstWhere((e) => e.name == json['sourceChain']),
        targetChain: ChainId.values.firstWhere((e) => e.name == json['targetChain']),
        asset: Asset.fromJson(json['asset']),
        amount: json['amount'] as String,
        sender: json['sender'] as String,
        recipient: json['recipient'] as String,
        sourceTxHash: json['sourceTxHash'] as String?,
        targetTxHash: json['targetTxHash'] as String?,
        fee: json['fee'] as String,
        feeAsset: Asset.fromJson(json['feeAsset']),
        createdAt: json['createdAt'] as int,
        updatedAt: json['updatedAt'] as int,
        completedAt: json['completedAt'] as int?,
        errorMessage: json['errorMessage'] as String?,
      );
}

// ==================== Fee Types ====================

/// Fee estimate
class FeeEstimate {
  final String bridgeFee;
  final String gasFeeSource;
  final String gasFeeTarget;
  final String totalFee;
  final Asset feeAsset;
  final int estimatedTime;
  final String? exchangeRate;

  const FeeEstimate({
    required this.bridgeFee,
    required this.gasFeeSource,
    required this.gasFeeTarget,
    required this.totalFee,
    required this.feeAsset,
    required this.estimatedTime,
    this.exchangeRate,
  });

  factory FeeEstimate.fromJson(Map<String, dynamic> json) => FeeEstimate(
        bridgeFee: json['bridgeFee'] as String,
        gasFeeSource: json['gasFeeSource'] as String,
        gasFeeTarget: json['gasFeeTarget'] as String,
        totalFee: json['totalFee'] as String,
        feeAsset: Asset.fromJson(json['feeAsset']),
        estimatedTime: json['estimatedTime'] as int,
        exchangeRate: json['exchangeRate'] as String?,
      );
}

/// Exchange rate
class ExchangeRate {
  final Asset fromAsset;
  final Asset toAsset;
  final String rate;
  final String inverseRate;
  final int lastUpdated;
  final String source;

  const ExchangeRate({
    required this.fromAsset,
    required this.toAsset,
    required this.rate,
    required this.inverseRate,
    required this.lastUpdated,
    required this.source,
  });

  factory ExchangeRate.fromJson(Map<String, dynamic> json) => ExchangeRate(
        fromAsset: Asset.fromJson(json['fromAsset']),
        toAsset: Asset.fromJson(json['toAsset']),
        rate: json['rate'] as String,
        inverseRate: json['inverseRate'] as String,
        lastUpdated: json['lastUpdated'] as int,
        source: json['source'] as String,
      );
}

/// Signed transaction
class SignedTransaction {
  final String txHash;
  final ChainId chain;
  final String from;
  final String to;
  final String value;
  final String data;
  final String gasLimit;
  final String? gasPrice;
  final String? maxFeePerGas;
  final String? maxPriorityFeePerGas;
  final int nonce;
  final String signature;

  const SignedTransaction({
    required this.txHash,
    required this.chain,
    required this.from,
    required this.to,
    required this.value,
    required this.data,
    required this.gasLimit,
    this.gasPrice,
    this.maxFeePerGas,
    this.maxPriorityFeePerGas,
    required this.nonce,
    required this.signature,
  });

  factory SignedTransaction.fromJson(Map<String, dynamic> json) => SignedTransaction(
        txHash: json['txHash'] as String,
        chain: ChainId.values.firstWhere((e) => e.name == json['chain']),
        from: json['from'] as String,
        to: json['to'] as String,
        value: json['value'] as String,
        data: json['data'] as String,
        gasLimit: json['gasLimit'] as String,
        gasPrice: json['gasPrice'] as String?,
        maxFeePerGas: json['maxFeePerGas'] as String?,
        maxPriorityFeePerGas: json['maxPriorityFeePerGas'] as String?,
        nonce: json['nonce'] as int,
        signature: json['signature'] as String,
      );
}

// ==================== Options & Filter ====================

/// Lock options
class LockOptions {
  final String? recipient;
  final int? deadline;
  final double? slippage;

  const LockOptions({this.recipient, this.deadline, this.slippage});

  Map<String, dynamic> toJson() => {
        if (recipient != null) 'recipient': recipient,
        if (deadline != null) 'deadline': deadline,
        if (slippage != null) 'slippage': slippage,
      };
}

/// Mint options
class MintOptions {
  final String? gasLimit;
  final String? maxFeePerGas;
  final String? maxPriorityFeePerGas;

  const MintOptions({this.gasLimit, this.maxFeePerGas, this.maxPriorityFeePerGas});

  Map<String, dynamic> toJson() => {
        if (gasLimit != null) 'gasLimit': gasLimit,
        if (maxFeePerGas != null) 'maxFeePerGas': maxFeePerGas,
        if (maxPriorityFeePerGas != null) 'maxPriorityFeePerGas': maxPriorityFeePerGas,
      };
}

/// Burn options
class BurnOptions {
  final String? recipient;
  final int? deadline;

  const BurnOptions({this.recipient, this.deadline});

  Map<String, dynamic> toJson() => {
        if (recipient != null) 'recipient': recipient,
        if (deadline != null) 'deadline': deadline,
      };
}

/// Unlock options
class UnlockOptions {
  final String? gasLimit;
  final String? gasPrice;

  const UnlockOptions({this.gasLimit, this.gasPrice});

  Map<String, dynamic> toJson() => {
        if (gasLimit != null) 'gasLimit': gasLimit,
        if (gasPrice != null) 'gasPrice': gasPrice,
      };
}

/// Transfer filter
class TransferFilter {
  final TransferStatus? status;
  final ChainId? sourceChain;
  final ChainId? targetChain;
  final String? asset;
  final String? sender;
  final String? recipient;
  final int? limit;
  final int? offset;

  const TransferFilter({
    this.status,
    this.sourceChain,
    this.targetChain,
    this.asset,
    this.sender,
    this.recipient,
    this.limit,
    this.offset,
  });
}

// ==================== Config ====================

/// Bridge configuration
class BridgeConfig {
  final String apiKey;
  final String endpoint;
  final Duration timeout;
  final int retries;
  final bool debug;

  const BridgeConfig({
    required this.apiKey,
    this.endpoint = 'https://bridge.synor.io/v1',
    this.timeout = const Duration(seconds: 60),
    this.retries = 3,
    this.debug = false,
  });
}

// ==================== Error ====================

/// Bridge error
class BridgeException implements Exception {
  final String message;
  final String? code;
  final int? statusCode;

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

  bool get isConfirmationsPending => code == 'CONFIRMATIONS_PENDING';

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