// ==================== ABI Types ====================

class AbiParameter {
  final String? name;
  final String type;
  final bool? indexed;
  final List<AbiParameter>? components;

  AbiParameter({
    this.name,
    required this.type,
    this.indexed,
    this.components,
  });

  factory AbiParameter.fromJson(Map<String, dynamic> json) {
    return AbiParameter(
      name: json['name'] as String?,
      type: json['type'] as String,
      indexed: json['indexed'] as bool?,
      components: json['components'] != null
          ? (json['components'] as List).map((e) => AbiParameter.fromJson(e)).toList()
          : null,
    );
  }

  Map<String, dynamic> toJson() => {
        if (name != null) 'name': name,
        'type': type,
        if (indexed != null) 'indexed': indexed,
        if (components != null) 'components': components!.map((e) => e.toJson()).toList(),
      };
}

class AbiEntry {
  final String type;
  final String? name;
  final List<AbiParameter>? inputs;
  final List<AbiParameter>? outputs;
  final String? stateMutability;
  final bool? anonymous;

  AbiEntry({
    required this.type,
    this.name,
    this.inputs,
    this.outputs,
    this.stateMutability,
    this.anonymous,
  });

  factory AbiEntry.fromJson(Map<String, dynamic> json) {
    return AbiEntry(
      type: json['type'] as String,
      name: json['name'] as String?,
      inputs: json['inputs'] != null
          ? (json['inputs'] as List).map((e) => AbiParameter.fromJson(e)).toList()
          : null,
      outputs: json['outputs'] != null
          ? (json['outputs'] as List).map((e) => AbiParameter.fromJson(e)).toList()
          : null,
      stateMutability: json['stateMutability'] as String?,
      anonymous: json['anonymous'] as bool?,
    );
  }

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

// ==================== Deployment ====================

class DeployContractOptions {
  final String bytecode;
  final List<AbiEntry>? abi;
  final List<dynamic>? constructorArgs;
  final String? value;
  final int? gasLimit;
  final String? gasPrice;
  final int? nonce;

  DeployContractOptions({
    required this.bytecode,
    this.abi,
    this.constructorArgs,
    this.value,
    this.gasLimit,
    this.gasPrice,
    this.nonce,
  });
}

class DeploymentResult {
  final String contractAddress;
  final String transactionHash;
  final String? deployer;
  final int? gasUsed;
  final int? blockNumber;
  final String? blockHash;

  DeploymentResult({
    required this.contractAddress,
    required this.transactionHash,
    this.deployer,
    this.gasUsed,
    this.blockNumber,
    this.blockHash,
  });

  factory DeploymentResult.fromJson(Map<String, dynamic> json) {
    return DeploymentResult(
      contractAddress: json['contract_address'] as String,
      transactionHash: json['transaction_hash'] as String,
      deployer: json['deployer'] as String?,
      gasUsed: json['gas_used'] as int?,
      blockNumber: json['block_number'] as int?,
      blockHash: json['block_hash'] as String?,
    );
  }
}

// ==================== Contract Interaction ====================

class CallContractOptions {
  final String contract;
  final String method;
  final List<dynamic> args;
  final List<AbiEntry> abi;

  CallContractOptions({
    required this.contract,
    required this.method,
    this.args = const [],
    required this.abi,
  });
}

class SendContractOptions {
  final String contract;
  final String method;
  final List<dynamic> args;
  final List<AbiEntry> abi;
  final String? value;
  final int? gasLimit;
  final String? gasPrice;
  final int? nonce;

  SendContractOptions({
    required this.contract,
    required this.method,
    this.args = const [],
    required this.abi,
    this.value,
    this.gasLimit,
    this.gasPrice,
    this.nonce,
  });
}

class TransactionResult {
  final String transactionHash;
  final int? blockNumber;
  final String? blockHash;
  final int? gasUsed;
  final String? effectiveGasPrice;
  final String? status;
  final List<EventLog>? logs;

  TransactionResult({
    required this.transactionHash,
    this.blockNumber,
    this.blockHash,
    this.gasUsed,
    this.effectiveGasPrice,
    this.status,
    this.logs,
  });

  factory TransactionResult.fromJson(Map<String, dynamic> json) {
    return TransactionResult(
      transactionHash: json['transaction_hash'] as String,
      blockNumber: json['block_number'] as int?,
      blockHash: json['block_hash'] as String?,
      gasUsed: json['gas_used'] as int?,
      effectiveGasPrice: json['effective_gas_price'] as String?,
      status: json['status'] as String?,
      logs: json['logs'] != null
          ? (json['logs'] as List).map((e) => EventLog.fromJson(e)).toList()
          : null,
    );
  }
}

// ==================== Events ====================

class EventLog {
  final String address;
  final List<String> topics;
  final String data;
  final int? blockNumber;
  final String? transactionHash;
  final int? logIndex;
  final String? blockHash;
  final bool? removed;

  EventLog({
    required this.address,
    required this.topics,
    required this.data,
    this.blockNumber,
    this.transactionHash,
    this.logIndex,
    this.blockHash,
    this.removed,
  });

  factory EventLog.fromJson(Map<String, dynamic> json) {
    return EventLog(
      address: json['address'] as String,
      topics: (json['topics'] as List).map((e) => e as String).toList(),
      data: json['data'] as String,
      blockNumber: json['block_number'] as int?,
      transactionHash: json['transaction_hash'] as String?,
      logIndex: json['log_index'] as int?,
      blockHash: json['block_hash'] as String?,
      removed: json['removed'] as bool?,
    );
  }

  Map<String, dynamic> toJson() => {
        'address': address,
        'topics': topics,
        'data': data,
        if (blockNumber != null) 'block_number': blockNumber,
        if (transactionHash != null) 'transaction_hash': transactionHash,
        if (logIndex != null) 'log_index': logIndex,
        if (blockHash != null) 'block_hash': blockHash,
        if (removed != null) 'removed': removed,
      };
}

class DecodedEvent {
  final String name;
  final String? signature;
  final Map<String, dynamic>? args;
  final EventLog? log;

  DecodedEvent({
    required this.name,
    this.signature,
    this.args,
    this.log,
  });

  factory DecodedEvent.fromJson(Map<String, dynamic> json) {
    return DecodedEvent(
      name: json['name'] as String,
      signature: json['signature'] as String?,
      args: json['args'] as Map<String, dynamic>?,
      log: json['log'] != null ? EventLog.fromJson(json['log']) : null,
    );
  }
}

class EventFilter {
  final String contract;
  final String? event;
  final int? fromBlock;
  final int? toBlock;
  final List<String?>? topics;
  final List<AbiEntry>? abi;

  EventFilter({
    required this.contract,
    this.event,
    this.fromBlock,
    this.toBlock,
    this.topics,
    this.abi,
  });
}

// ==================== ABI Utilities ====================

class EncodeCallOptions {
  final String method;
  final List<dynamic> args;
  final List<AbiEntry> abi;

  EncodeCallOptions({
    required this.method,
    this.args = const [],
    required this.abi,
  });
}

class DecodeResultOptions {
  final String data;
  final String method;
  final List<AbiEntry> abi;

  DecodeResultOptions({
    required this.data,
    required this.method,
    required this.abi,
  });
}

// ==================== Gas Estimation ====================

class EstimateGasOptions {
  final String contract;
  final String method;
  final List<dynamic> args;
  final List<AbiEntry> abi;
  final String? value;

  EstimateGasOptions({
    required this.contract,
    required this.method,
    this.args = const [],
    required this.abi,
    this.value,
  });
}

class GasEstimation {
  final int gasLimit;
  final String? gasPrice;
  final String? maxFeePerGas;
  final String? maxPriorityFeePerGas;
  final String? estimatedCost;

  GasEstimation({
    required this.gasLimit,
    this.gasPrice,
    this.maxFeePerGas,
    this.maxPriorityFeePerGas,
    this.estimatedCost,
  });

  factory GasEstimation.fromJson(Map<String, dynamic> json) {
    return GasEstimation(
      gasLimit: json['gas_limit'] as int,
      gasPrice: json['gas_price'] as String?,
      maxFeePerGas: json['max_fee_per_gas'] as String?,
      maxPriorityFeePerGas: json['max_priority_fee_per_gas'] as String?,
      estimatedCost: json['estimated_cost'] as String?,
    );
  }
}

// ==================== Contract Information ====================

class BytecodeInfo {
  final String bytecode;
  final String? deployedBytecode;
  final int? size;
  final bool? isContract;

  BytecodeInfo({
    required this.bytecode,
    this.deployedBytecode,
    this.size,
    this.isContract,
  });

  factory BytecodeInfo.fromJson(Map<String, dynamic> json) {
    return BytecodeInfo(
      bytecode: json['bytecode'] as String,
      deployedBytecode: json['deployed_bytecode'] as String?,
      size: json['size'] as int?,
      isContract: json['is_contract'] as bool?,
    );
  }
}

class VerifyContractOptions {
  final String address;
  final String sourceCode;
  final String compilerVersion;
  final String? constructorArgs;
  final bool? optimization;
  final int? optimizationRuns;
  final String? license;

  VerifyContractOptions({
    required this.address,
    required this.sourceCode,
    required this.compilerVersion,
    this.constructorArgs,
    this.optimization,
    this.optimizationRuns,
    this.license,
  });
}

class VerificationResult {
  final bool verified;
  final String? address;
  final String? compilerVersion;
  final bool? optimization;
  final int? optimizationRuns;
  final String? license;
  final List<AbiEntry>? abi;
  final String? sourceCode;

  VerificationResult({
    required this.verified,
    this.address,
    this.compilerVersion,
    this.optimization,
    this.optimizationRuns,
    this.license,
    this.abi,
    this.sourceCode,
  });

  factory VerificationResult.fromJson(Map<String, dynamic> json) {
    return VerificationResult(
      verified: json['verified'] as bool,
      address: json['address'] as String?,
      compilerVersion: json['compiler_version'] as String?,
      optimization: json['optimization'] as bool?,
      optimizationRuns: json['optimization_runs'] as int?,
      license: json['license'] as String?,
      abi: json['abi'] != null
          ? (json['abi'] as List).map((e) => AbiEntry.fromJson(e)).toList()
          : null,
      sourceCode: json['source_code'] as String?,
    );
  }
}

// ==================== Multicall ====================

class MulticallRequest {
  final String contract;
  final String method;
  final List<dynamic> args;
  final List<AbiEntry> abi;

  MulticallRequest({
    required this.contract,
    required this.method,
    this.args = const [],
    required this.abi,
  });

  Map<String, dynamic> toJson() => {
        'contract': contract,
        'method': method,
        'args': args,
        'abi': abi.map((e) => e.toJson()).toList(),
      };
}

class MulticallResult {
  final bool success;
  final dynamic result;
  final String? error;

  MulticallResult({
    required this.success,
    this.result,
    this.error,
  });

  factory MulticallResult.fromJson(Map<String, dynamic> json) {
    return MulticallResult(
      success: json['success'] as bool,
      result: json['result'],
      error: json['error'] as String?,
    );
  }
}

// ==================== Storage ====================

class ReadStorageOptions {
  final String contract;
  final String slot;
  final int? blockNumber;

  ReadStorageOptions({
    required this.contract,
    required this.slot,
    this.blockNumber,
  });
}
