using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Synor.Contract
{
    // ==================== ABI Types ====================

    public record AbiParameter
    {
        [JsonPropertyName("name")]
        public string? Name { get; init; }

        [JsonPropertyName("type")]
        public required string Type { get; init; }

        [JsonPropertyName("indexed")]
        public bool? Indexed { get; init; }

        [JsonPropertyName("components")]
        public AbiParameter[]? Components { get; init; }
    }

    public record AbiEntry
    {
        [JsonPropertyName("type")]
        public required string Type { get; init; }

        [JsonPropertyName("name")]
        public string? Name { get; init; }

        [JsonPropertyName("inputs")]
        public AbiParameter[]? Inputs { get; init; }

        [JsonPropertyName("outputs")]
        public AbiParameter[]? Outputs { get; init; }

        [JsonPropertyName("stateMutability")]
        public string? StateMutability { get; init; }

        [JsonPropertyName("anonymous")]
        public bool? Anonymous { get; init; }
    }

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

    public record DeployContractOptions
    {
        [JsonPropertyName("bytecode")]
        public required string Bytecode { get; init; }

        [JsonPropertyName("abi")]
        public AbiEntry[]? Abi { get; init; }

        [JsonPropertyName("constructor_args")]
        public object[]? ConstructorArgs { get; init; }

        [JsonPropertyName("value")]
        public string? Value { get; init; }

        [JsonPropertyName("gas_limit")]
        public long? GasLimit { get; init; }

        [JsonPropertyName("gas_price")]
        public string? GasPrice { get; init; }

        [JsonPropertyName("nonce")]
        public long? Nonce { get; init; }
    }

    public record DeploymentResult
    {
        [JsonPropertyName("contract_address")]
        public required string ContractAddress { get; init; }

        [JsonPropertyName("transaction_hash")]
        public required string TransactionHash { get; init; }

        [JsonPropertyName("deployer")]
        public string? Deployer { get; init; }

        [JsonPropertyName("gas_used")]
        public long? GasUsed { get; init; }

        [JsonPropertyName("block_number")]
        public long? BlockNumber { get; init; }

        [JsonPropertyName("block_hash")]
        public string? BlockHash { get; init; }
    }

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

    public record CallContractOptions
    {
        [JsonPropertyName("contract")]
        public required string Contract { get; init; }

        [JsonPropertyName("method")]
        public required string Method { get; init; }

        [JsonPropertyName("args")]
        public object[] Args { get; init; } = [];

        [JsonPropertyName("abi")]
        public required AbiEntry[] Abi { get; init; }
    }

    public record SendContractOptions
    {
        [JsonPropertyName("contract")]
        public required string Contract { get; init; }

        [JsonPropertyName("method")]
        public required string Method { get; init; }

        [JsonPropertyName("args")]
        public object[] Args { get; init; } = [];

        [JsonPropertyName("abi")]
        public required AbiEntry[] Abi { get; init; }

        [JsonPropertyName("value")]
        public string? Value { get; init; }

        [JsonPropertyName("gas_limit")]
        public long? GasLimit { get; init; }

        [JsonPropertyName("gas_price")]
        public string? GasPrice { get; init; }

        [JsonPropertyName("nonce")]
        public long? Nonce { get; init; }
    }

    public record EventLog
    {
        [JsonPropertyName("address")]
        public required string Address { get; init; }

        [JsonPropertyName("topics")]
        public required string[] Topics { get; init; }

        [JsonPropertyName("data")]
        public required string Data { get; init; }

        [JsonPropertyName("block_number")]
        public long? BlockNumber { get; init; }

        [JsonPropertyName("transaction_hash")]
        public string? TransactionHash { get; init; }

        [JsonPropertyName("log_index")]
        public int? LogIndex { get; init; }

        [JsonPropertyName("block_hash")]
        public string? BlockHash { get; init; }

        [JsonPropertyName("removed")]
        public bool? Removed { get; init; }
    }

    public record TransactionResult
    {
        [JsonPropertyName("transaction_hash")]
        public required string TransactionHash { get; init; }

        [JsonPropertyName("block_number")]
        public long? BlockNumber { get; init; }

        [JsonPropertyName("block_hash")]
        public string? BlockHash { get; init; }

        [JsonPropertyName("gas_used")]
        public long? GasUsed { get; init; }

        [JsonPropertyName("effective_gas_price")]
        public string? EffectiveGasPrice { get; init; }

        [JsonPropertyName("status")]
        public string? Status { get; init; }

        [JsonPropertyName("logs")]
        public EventLog[]? Logs { get; init; }
    }

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

    public record DecodedEvent
    {
        [JsonPropertyName("name")]
        public required string Name { get; init; }

        [JsonPropertyName("signature")]
        public string? Signature { get; init; }

        [JsonPropertyName("args")]
        public Dictionary<string, JsonElement>? Args { get; init; }

        [JsonPropertyName("log")]
        public EventLog? Log { get; init; }
    }

    public record EventFilter
    {
        [JsonPropertyName("contract")]
        public required string Contract { get; init; }

        [JsonPropertyName("event")]
        public string? Event { get; init; }

        [JsonPropertyName("from_block")]
        public long? FromBlock { get; init; }

        [JsonPropertyName("to_block")]
        public long? ToBlock { get; init; }

        [JsonPropertyName("topics")]
        public string?[]? Topics { get; init; }

        [JsonPropertyName("abi")]
        public AbiEntry[]? Abi { get; init; }
    }

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

    public record EncodeCallOptions
    {
        [JsonPropertyName("method")]
        public required string Method { get; init; }

        [JsonPropertyName("args")]
        public object[] Args { get; init; } = [];

        [JsonPropertyName("abi")]
        public required AbiEntry[] Abi { get; init; }
    }

    public record DecodeResultOptions
    {
        [JsonPropertyName("data")]
        public required string Data { get; init; }

        [JsonPropertyName("method")]
        public required string Method { get; init; }

        [JsonPropertyName("abi")]
        public required AbiEntry[] Abi { get; init; }
    }

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

    public record EstimateGasOptions
    {
        [JsonPropertyName("contract")]
        public required string Contract { get; init; }

        [JsonPropertyName("method")]
        public required string Method { get; init; }

        [JsonPropertyName("args")]
        public object[] Args { get; init; } = [];

        [JsonPropertyName("abi")]
        public required AbiEntry[] Abi { get; init; }

        [JsonPropertyName("value")]
        public string? Value { get; init; }
    }

    public record GasEstimation
    {
        [JsonPropertyName("gas_limit")]
        public long GasLimit { get; init; }

        [JsonPropertyName("gas_price")]
        public string? GasPrice { get; init; }

        [JsonPropertyName("max_fee_per_gas")]
        public string? MaxFeePerGas { get; init; }

        [JsonPropertyName("max_priority_fee_per_gas")]
        public string? MaxPriorityFeePerGas { get; init; }

        [JsonPropertyName("estimated_cost")]
        public string? EstimatedCost { get; init; }
    }

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

    public record BytecodeInfo
    {
        [JsonPropertyName("bytecode")]
        public required string Bytecode { get; init; }

        [JsonPropertyName("deployed_bytecode")]
        public string? DeployedBytecode { get; init; }

        [JsonPropertyName("size")]
        public int? Size { get; init; }

        [JsonPropertyName("is_contract")]
        public bool? IsContract { get; init; }
    }

    public record VerifyContractOptions
    {
        [JsonPropertyName("address")]
        public required string Address { get; init; }

        [JsonPropertyName("source_code")]
        public required string SourceCode { get; init; }

        [JsonPropertyName("compiler_version")]
        public required string CompilerVersion { get; init; }

        [JsonPropertyName("constructor_args")]
        public string? ConstructorArgs { get; init; }

        [JsonPropertyName("optimization")]
        public bool? Optimization { get; init; }

        [JsonPropertyName("optimization_runs")]
        public int? OptimizationRuns { get; init; }

        [JsonPropertyName("license")]
        public string? License { get; init; }
    }

    public record VerificationResult
    {
        [JsonPropertyName("verified")]
        public bool Verified { get; init; }

        [JsonPropertyName("address")]
        public string? Address { get; init; }

        [JsonPropertyName("compiler_version")]
        public string? CompilerVersion { get; init; }

        [JsonPropertyName("optimization")]
        public bool? Optimization { get; init; }

        [JsonPropertyName("optimization_runs")]
        public int? OptimizationRuns { get; init; }

        [JsonPropertyName("license")]
        public string? License { get; init; }

        [JsonPropertyName("abi")]
        public AbiEntry[]? Abi { get; init; }

        [JsonPropertyName("source_code")]
        public string? SourceCode { get; init; }
    }

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

    public record MulticallRequest
    {
        [JsonPropertyName("contract")]
        public required string Contract { get; init; }

        [JsonPropertyName("method")]
        public required string Method { get; init; }

        [JsonPropertyName("args")]
        public object[] Args { get; init; } = [];

        [JsonPropertyName("abi")]
        public required AbiEntry[] Abi { get; init; }
    }

    public record MulticallResult
    {
        [JsonPropertyName("success")]
        public bool Success { get; init; }

        [JsonPropertyName("result")]
        public JsonElement? Result { get; init; }

        [JsonPropertyName("error")]
        public string? Error { get; init; }
    }

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

    public record ReadStorageOptions
    {
        public required string Contract { get; init; }
        public required string Slot { get; init; }
        public long? BlockNumber { get; init; }
    }
}
