"""
abstract_abis.py - ABIBridge Module

This module defines the `ABIBridge` class, which provides functionality for interacting with Ethereum smart contracts' ABIs (Application Binary Interfaces).
It allows you to retrieve and use contract ABIs, call contract functions, and manage rate limiting for API requests.

Classes:
    ABIBridge: A class to interact with Ethereum smart contract ABIs and functions.

Functions:
    default_rpc: Returns a default RPC configuration dictionary.

Example Usage:
    # Create an instance of ABIBridge
    abi_manager = ABIBridge(contract_address='0x3dCCeAE634f371E779c894A1cEa43a09C23af8d5', rpc=default_rpc())
    
    # Retrieve read-only functions from the contract
    read_only_functions = abi_manager.get_read_only_functions()
    
    # Iterate through each read-only function
    for function_name in read_only_functions:
        inputs = abi_manager.get_required_inputs(function_name)
        if len(inputs) == 0:
            result = abi_manager.call_function(function_name)
            print(function_name, result)
        else:
            print(function_name, inputs)
"""
from .abstract_apis import APIBridge
from .abstract_rpcs import RPCBridge
from abstract_utilities import safe_json_loads
from typing import Optional
from abstract_blockchain_functions import *

class ABIBridge:
    """


    ABIBridge class provides functionality to interact with Ethereum smart contract ABIs and functions.
    """

    def __init__(self, address: Optional[str] = None, api_mgr=None, api_gui: bool = False, rpc_js: Optional[dict] = None,w3_mgr=None):
        """
        Initializes the ABIBridge instance.

        :param address: Ethereum contract address. Default is None.
        :param api_mgr: API manager instance. Default is None.
        :param api_gui: Boolean flag for API GUI. Default is False.
        :param rpc_js: RPC configuration dictionary. Default is an empty dictionary.
        """
        # Use os.path.join for path concatenation to ensure cross-platform compatibility
        self.abi_bridges = {}
        api_mgr=api_mgr or APIBridge(api_gui=api_gui)
        self.w3_mgr=w3_mgr or w3Manager()
    def fetch_abi(self,address=None,rpc_js=None):
        """
        Fetches the ABI for the contract.

        :return: ABI of the contract.
        """
        response = safe_json_loads(derive_network(address=address,initial_network=rpc_js))
    
        return response['contract_data'][0]['ABI']
    def get_abi_data(self,address=None):
        address=address or self.address
        if address:
            abi = self.fetch_abi(address)
            self.contract_functions = self.list_contract_functions(address)
            return abi
    def create_abi_bridge(self, address, abi=None, rpc_js=None):
        normalized_address = get_normalized_address(address)
        if normalized_address not in self.abi_bridges:
            self.update_abi_bridge(address=address,abi=abi,rpc_js=rpc_js)
        return self.abi_bridges[normalized_address]
    def update_abi_bridge(self,address,abi=None,rpc_js=None):
        normalized_address = get_normalized_address(address)
        rpc = rpc_js.get('user_settings',rpc_js)
        if rpc and rpc.get('rpc'):
            address = checksum(address=address, rpc=rpc.get('rpc'))
        else:
            address = checksum(address=address, rpc=rpc)
        abi = abi or self.get_abi_data(address=address)
        self.abi_bridges[normalized_address] = self.w3_mgr.create_w3(address=address, rpc=rpc).eth.contract(address=address, abi=abi)
    def list_contract_functions(self,address,abi:list=None):
        """
        List all contract functions and their details.

        :return: List of contract function details.
        """
        abi=abi or self.fetch_abi(address=address)
        if isinstance(abi,list):
            functions = []
            if abi != None:
                for item in abi:
                    if item.get('type') == 'function':
                        function_details = {
                            "name": item.get('name'),
                            "inputs": [(i.get('name'), i.get('type')) for i in item.get('inputs')],
                            "outputs": [(o.get('name'), o.get('type')) for o in item.get('outputs')]
                        }
                        functions.append(function_details)
            self.contract_functions = functions
            return self.contract_functions
    def get_read_only_functions(self, address,abi:list=None):
        """
        Get a list of read-only functions from the ABI.

        :param abi: ABI to analyze (default is None, uses instance ABI).
        :return: List of read-only function names.
        """
        abi=abi or self.abi
        if isinstance(abi,list):
            read_only_functions = []
            for item in abi:
                if item.get('type') == 'function' and item.get('stateMutability') in ['view','pure']:
                    read_only_functions.append(item.get('name'))
            return read_only_functions

    def get_required_inputs(self, function_name: str, abi: list = None):
        """
        Get the required inputs for a specific function from the ABI.

        :param function_name: Name of the function.
        :param abi: ABI to analyze (default is None, uses instance ABI).
        :return: List of required inputs for the function.
        """
        abi=abi or self.abi
        if isinstance(abi,list):
            for item in abi:
                if item.get('type') == 'function' and item.get("name") == function_name:
                    return item.get("inputs")
    def call_function(self, address, function_name, *args, **kwargs):
        contract_bridge = self.create_abi_bridge(address)
        contract_function = getattr(contract_bridge.functions, function_name)

        if len(args) == 1 and not kwargs:
            return contract_function(args[0]).call()
        elif args and not kwargs:
            return contract_function(*args).call()
        elif kwargs:
            return contract_function(**kwargs).call()
        else:
            return contract_function().call()
    def create_functions(self,*args,function_name:str,subsinstance:str="functions",contract_bridge=None, **kwargs):
        contract_bridge = contract_bridge or self.contract_bridge
        # Access the subsinstance (like "functions" in the contract)
        sub_instance = getattr(contract_bridge, subsinstance)  # use self.contract_bridge
            
        # Get the desired function from the subsinstance
        contract_function = getattr(sub_instance, function_name)

        # If there's only one positional argument and no keyword arguments, use it directly.
        # Otherwise, use kwargs as named arguments.
        if len(args) == 1 and not kwargs:
            return contract_function(args[0])
        elif args and not kwargs:
            return contract_function(*args)
        # If there are keyword arguments, use them.
        elif kwargs:
            return contract_function(**kwargs)
        # If no arguments, just call the function.
        else:
            return contract_function()

