import json
import asyncio
import requests

class Block:
    def __init__(self, data):
        if isinstance(data, dict):
            self.hash = data["hash"]
            self.height = data["height"]
            self.depth = data["depth"]
            self.version = data["version"]
            self.prevBlock = data["prevBlock"]
            self.merkleRoot = data["merkleRoot"]
            self.witnessRoot = data["witnessRoot"]
            self.treeRoot = data["treeRoot"]
            self.reservedRoot = data["reservedRoot"]
            self.time = data["time"]
            self.bits = data["bits"]
            self.nonce = data["nonce"]
            self.extraNonce = data["extraNonce"]
            self.mask = data["mask"]
            self.txs = data["txs"]
        elif isinstance(data, list) or isinstance(data, tuple):
            self.hash = data[0]
            self.height = data[1]
            self.depth = data[2]
            self.version = data[3]
            self.prevBlock = data[4]
            self.merkleRoot = data[5]
            self.witnessRoot = data[6]
            self.treeRoot = data[7]
            self.reservedRoot = data[8]
            self.time = data[9]
            self.bits = data[10]
            self.nonce = data[11]
            self.extraNonce = data[12]
            self.mask = data[13]
            self.txs = json.loads(data[14])
        else:
            raise ValueError("Invalid data type")

    def __str__(self):
        return f"Block {self.height}"
    
    def toJSON(self) -> dict:
        return {
            "hash": self.hash,
            "height": self.height,
            "depth": self.depth,
            "version": self.version,
            "prevBlock": self.prevBlock,
            "merkleRoot": self.merkleRoot,
            "witnessRoot": self.witnessRoot,
            "treeRoot": self.treeRoot,
            "reservedRoot": self.reservedRoot,
            "time": self.time,
            "bits": self.bits,
            "nonce": self.nonce,
            "extraNonce": self.extraNonce,
            "mask": self.mask,
            "txs": self.txs
        }
    
class Transaction:
    def __init__(self, data):
        if isinstance(data, dict):
            self.hash = data["hash"]
            self.witnessHash = data["witnessHash"]
            self.fee = data["fee"]
            self.rate = data["rate"]
            self.mtime = data["mtime"]
            self.block = data["block"]
            self.index = data["index"]
            self.version = data["version"]
            self.inputs = data["inputs"]
            self.outputs = data["outputs"]
            self.locktime = data["locktime"]
            self.hex = data["hex"]
        elif isinstance(data, list) or isinstance(data, tuple):
            self.hash = data[0]
            self.witnessHash = data[1]
            self.fee = data[2]
            self.rate = data[3]
            self.mtime = data[4]
            self.block = data[5]
            self.index = data[6]
            self.version = data[7]
            # Load inputs with Input class
            self.inputs = []
            for input in json.loads(data[8]):
                self.inputs.append(Input(input))
            self.outputs = []
            for output in json.loads(data[9]):
                self.outputs.append(Output(output))
            self.locktime = data[10]
            self.hex = data[11]
        else:
            raise ValueError("Invalid data type")

    def __str__(self):
        return f"Transaction {self.hash}"
    
    def toJSON(self) -> dict:
        return {
            "hash": self.hash,
            "witnessHash": self.witnessHash,
            "fee": self.fee,
            "rate": self.rate,
            "mtime": self.mtime,
            "block": self.block,
            "index": self.index,
            "version": self.version,
            "inputs": [input.toJSON() for input in self.inputs],
            "outputs": [output.toJSON() for output in self.outputs],
            "locktime": self.locktime,
            "hex": self.hex
        }
    
class Input:
    
    def __init__(self, data):
        if isinstance(data, dict):
            self.prevout = data["prevout"]
            self.witness = data["witness"]
            self.sequence = data["sequence"]
            self.address = None
            self.coin = None
            if "address" in data:
                self.address = data["address"]
            if "coin" in data:
                self.coin = Coin(data["coin"])
        else:
            raise ValueError("Invalid data type")

    def __str__(self):
        return f"Input {self.prevout['hash']} {self.coin}"

    def toJSON(self) -> dict:
        return {
            "prevout": self.prevout,
            "witness": self.witness,
            "sequence": self.sequence,
            "address": self.address,
            "coin": self.coin.toJSON() if self.coin else None
        }
    
class Output:
    
    def __init__(self, data):
        if isinstance(data, dict):
            self.value = data["value"]
            self.address = data["address"]
            self.covenant = Covenant(data["covenant"])
        else:
            raise ValueError("Invalid data type")

    def __str__(self):
        return f"Output {self.value} {self.address} {self.covenant}"
    
    def toJSON(self) -> dict:
        return {
            "value": self.value,
            "address": self.address,
            "covenant": self.covenant.toJSON()
        }
    
def hex_to_ascii(hex_string):
    # Convert the hex string to bytes
    bytes_obj = bytes.fromhex(hex_string)
    # Decode the bytes object to an ASCII string
    ascii_string = bytes_obj.decode('ascii')
    return ascii_string

class Covenant:
    def __init__(self, data):
        if isinstance(data, dict):
            self.type = data["type"]
            self.action = data["action"]
            self.items = data["items"]
            self.nameHash = None
            self.height = None
            self.name = None
            self.flags = None
            self.hash = None
            self.nonce = None
            self.recordData = None
            self.blockHash = None
            self.version = None
            self.Address = None
            self.claimHeight = None
            self.renewalCount = None


            if self.type > 0: # All but NONE
                self.nameHash = self.items[0]
                self.height = self.items[1]
            
            if self.type == 1: # CLAIM
                self.flags = self.items[3]
            
            if self.type in [1,2,3]: # CLAIM, OPEN, BID
                self.name = hex_to_ascii(self.items[2])

            if self.type == 3: # BID
                self.hash = self.items[3]
            
            if self.type == 4: # REVEAL
                self.nonce = self.items[2]

            if self.type in [6,7]: # REGISTER, UPDATE
                self.recordData = self.items[2]
            
            if self.type == 6: # REGISTER
                self.blockHash = self.items[3]

            if self.type == 8: # RENEW
                self.blockHash = self.items[2]
            
            if self.type == 9: # TRANSFER
                self.version = self.items[2]
                self.Address = self.items[3]

            if self.type == 10: # FINALIZE
                self.name = hex_to_ascii(self.items[2])
                self.flags = self.items[3]
                self.claimHeight= self.items[4]
                self.renewalCount = self.items[5]
                self.blockHash = self.items[6]

        else:
            raise ValueError("Invalid data type")

    def __str__(self):
        return self.toString()
    
    def toString(self):
        return self.action

    def toJSON(self) -> dict:
        return {
            "type": self.type,
            "action": self.action,
            "items": self.items
        }
    
class Coin:
    def __init__(self, data):
        if isinstance(data, dict):
            self.version = data["version"]
            self.height = data["height"]
            self.value = data["value"]
            self.address = data["address"]
            self.covenant = Covenant(data["covenant"])
            self.coinbase = data["coinbase"]
        else:
            raise ValueError("Invalid data type")

    def __str__(self):
        return f"Coin {self.value} {self.address} {self.covenant}"
    

    def toJSON(self) -> dict:
        return {
            "version": self.version,
            "height": self.height,
            "value": self.value,
            "address": self.address,
            "covenant": self.covenant.toJSON(),
            "coinbase": self.coinbase
        }