from web3 import Web3
from fastapi import FastAPI
from pydantic import BaseModel
from GetFromBlockChain import fetchFromBlockchainForOwner,fetchFromBlockchainForOwner_Mnf,fetchFromBlockchainForPublic,getcertainUsrsandApprovedUsers
# from MNF.settings import PRIVATE_KEY, ACCOUNT
from decryption import decryptionOfPrivate ,convertHexToBinary
import time


app = FastAPI()

# @app.get("/")
key1= "MyNextFilm"
# web3 = Web3(Web3.HTTPProvider("https://polygon-mumbai.g.alchemy.com/v2/R7E9J-rgTrtt60KzPDVCJKnURkunbiuV"))
web3 = Web3(Web3.HTTPProvider("https://rpc-mumbai.maticvigil.com/"))
CAddress = '0xCE5F0873c6CDf37003E9B69dA0110B023005DA34'
# privatekey = "0x6f06e1108b833b1918067042e13e60eda262705b80385a02d0330ce0db31d3ad"
abi = '[{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"_userId","type":"uint256"},{"internalType":"string","name":"_module","type":"string"},{"internalType":"string","name":"_services","type":"string"},{"internalType":"uint256","name":"project","type":"uint256"}],"name":"addApprovedUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_userId","type":"uint256"},{"internalType":"string","name":"_module","type":"string"},{"internalType":"string","name":"_services","type":"string"},{"internalType":"uint256","name":"project","type":"uint256"},{"internalType":"string","name":"payloadData","type":"string"},{"internalType":"string[]","name":"_files","type":"string[]"},{"internalType":"address[]","name":"publickey","type":"address[]"}],"name":"setCertainUsersData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"user_id","type":"uint256"},{"internalType":"string","name":"module","type":"string"},{"internalType":"string","name":"services","type":"string"},{"internalType":"uint256","name":"project","type":"uint256"},{"internalType":"string","name":"_data","type":"string"},{"internalType":"string[]","name":"Files","type":"string[]"}],"name":"setMNFOrOwnerData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"user_id","type":"uint256"},{"internalType":"string","name":"module","type":"string"},{"internalType":"string","name":"services","type":"string"},{"internalType":"uint256","name":"project","type":"uint256"},{"internalType":"string","name":"_data","type":"string"},{"internalType":"string[]","name":"Files","type":"string[]"}],"name":"setPrivateData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"user_id","type":"uint256"},{"internalType":"string","name":"module","type":"string"},{"internalType":"string","name":"services","type":"string"},{"internalType":"uint256","name":"project","type":"uint256"},{"internalType":"string","name":"_data","type":"string"},{"internalType":"string[]","name":"Files","type":"string[]"}],"name":"setpublicData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"_userId","type":"uint256"},{"internalType":"string","name":"_module","type":"string"},{"internalType":"string","name":"_services","type":"string"},{"internalType":"uint256","name":"_project","type":"uint256"}],"name":"getCertainUsersData","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"user_id","type":"uint256"},{"internalType":"string","name":"module","type":"string"},{"internalType":"string","name":"services","type":"string"},{"internalType":"uint256","name":"project","type":"uint256"}],"name":"getMNFOrOwnerData","outputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"string","name":"mydata","type":"string"},{"internalType":"string[]","name":"files","type":"string[]"}],"internalType":"struct MNFAccessableDataBase.MNFOwnerData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"user_id","type":"uint256"},{"internalType":"string","name":"module","type":"string"},{"internalType":"string","name":"services","type":"string"},{"internalType":"uint256","name":"project","type":"uint256"}],"name":"getPrivateData","outputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"string","name":"mydata","type":"string"},{"internalType":"string[]","name":"files","type":"string[]"}],"internalType":"struct MNFAccessableDataBase.privateData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"userId","type":"uint256"},{"internalType":"string","name":"module","type":"string"},{"internalType":"string","name":"services","type":"string"}],"name":"getProjectId","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"user_id","type":"uint256"},{"internalType":"string","name":"module","type":"string"},{"internalType":"string","name":"services","type":"string"},{"internalType":"uint256","name":"project","type":"uint256"}],"name":"getPublicData","outputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"string","name":"mydata","type":"string"},{"internalType":"string[]","name":"files","type":"string[]"}],"internalType":"struct MNFAccessableDataBase.publicData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"}]'
contractInst = web3.eth.contract(address=CAddress, abi=abi)

async def uploadDataToBlockchainForOwner(privatekey,user_id,module,services,project,data,file): 
    try:
        acc1 = web3.eth.account.from_key(privatekey).address
        nonce = web3.eth.getTransactionCount(acc1)
        uploadDataForOwner = contractInst.functions.setPrivateData(user_id,module,services,project,data,file).buildTransaction({
        'gasPrice': web3.eth.gas_price, 'chainId': 80001, 'from': acc1, 'nonce': nonce})
        signed_transaction = web3.eth.account.sign_transaction(uploadDataForOwner, private_key=privatekey)
        transaction_hash = web3.eth.send_raw_transaction(signed_transaction.rawTransaction)
        transaction_receipt = web3.eth.wait_for_transaction_receipt(transaction_hash)
        tx_id = transaction_hash.hex()
        return tx_id
    except Exception as e:
        print({"error":str(e)})

def uploadDataToBlockchainForOwner_Mnf(privatekey,user_id,module,services,project,data,file): 
    try: 
        acc1 = web3.eth.account.from_key(privatekey).address
        nonce = web3.eth.getTransactionCount(acc1)
        uploadDataForOwner_Mnf = contractInst.functions.setMNFOrOwnerData(user_id,module,services,project,data,file).buildTransaction({
        'gasPrice': web3.eth.gas_price, 'chainId': 80001, 'from': acc1, 'nonce': nonce})
        signed_transaction = web3.eth.account.sign_transaction(uploadDataForOwner_Mnf, private_key=privatekey)
        transaction_hash = web3.eth.send_raw_transaction(signed_transaction.rawTransaction)
        transaction_receipt = web3.eth.wait_for_transaction_receipt(transaction_hash)
        tx_id = transaction_hash.hex()
        return tx_id
    except Exception as e:
        print({"error":str(e)})

def uploadDataToBlockchainForPublic(privatekey,user_id,module,services,project,data,file): 
    try:
        acc1 = web3.eth.account.from_key(privatekey).address
        nonce = web3.eth.getTransactionCount(acc1)
        uploadDataForPublic = contractInst.functions.setpublicData(
        user_id,
        module,services,project,
        data,
        file).buildTransaction({
        'gasPrice': web3.eth.gas_price, 'chainId': 80001, 'from': acc1, 'nonce': nonce})
        signed_transaction = web3.eth.account.sign_transaction(uploadDataForPublic , private_key=privatekey)
        transaction_hash = web3.eth.send_raw_transaction(signed_transaction.rawTransaction)
        transaction_receipt = web3.eth.wait_for_transaction_receipt(transaction_hash)
        tx_id = transaction_hash.hex()
        return tx_id
    except Exception as e:
        print({"error":str(e)})

def uploadDataToBlockchainForCertainUsers(privatekey,user_id,module,services,project,data,file,publickey):
    try:
        acc1 = web3.eth.account.from_key(privatekey).address
        nonce = web3.eth.getTransactionCount(acc1)
        uploadDataForcertainusers = contractInst.functions.setCertainUsersData(
        user_id,
        module,services,project,
        data,
        file,
        publickey).buildTransaction({
        'gasPrice': web3.eth.gas_price, 'chainId': 80001, 'from': acc1, 'nonce': nonce})
        signed_transaction = web3.eth.account.sign_transaction(uploadDataForcertainusers , private_key=privatekey)
        transaction_hash = web3.eth.send_raw_transaction(signed_transaction.rawTransaction)
        transaction_receipt = web3.eth.wait_for_transaction_receipt(transaction_hash)
        tx_id = transaction_hash.hex()
        return tx_id
    except Exception as e:
        print({"error":str(e)})

def setApproval(privatekey,userAddress,user_id,module,services,project,): 
    try:
        acc1 = web3.eth.account.from_key(privatekey).address
        nonce = web3.eth.getTransactionCount(acc1)
        addAprovedUsers = contractInst.functions.addApprovedUser(userAddress,
        user_id,
        module,services,project).buildTransaction({
        'gasPrice': web3.eth.gas_price, 'chainId': 80001, 'from': acc1, 'nonce': nonce})
        signed_transaction = web3.eth.account.sign_transaction(addAprovedUsers , private_key=privatekey)
        transaction_hash = web3.eth.send_raw_transaction(signed_transaction.rawTransaction)
        transaction_receipt = web3.eth.wait_for_transaction_receipt(transaction_hash)
        tx_id = transaction_hash.hex()
        return tx_id
    except Exception as e:
        print({"error":str(e)})



class Item(BaseModel):
    user_id:int
    Module: str
    Services: str
    project:int
    Data: str
    File:list
    userPrivateKey:str

class writeForAprrove(BaseModel):
    user_id:int
    Module: str
    Services: str
    Project: int
    Data: str
    File:list 
    publicKey:list
    userPrivateKey:str


@app.post("/Write/onlyOwner")
async def owner(item: Item):
    try:
        key = convertHexToBinary(item.userPrivateKey)
        print(key)
        userkey =decryptionOfPrivate(key)
        # userkey = str(userkey)
        # hextobainary = bytes.fromhex(userkey)
        # print(hextobainary.decode('utf-8'))
        # user = "0x0ebd74d51611110f8220f23255626d5455c34ff415d01ffcbd7d0380498937d8"

        print(userkey)
        hash= await uploadDataToBlockchainForOwner(userkey.decode('utf-8'),item.user_id,item.Module,item.Services,item.project,item.Data,item.File)
        return {"tx_hash":hash}
    except Exception as e:
        print({"error":str(e)})


@app.post("/Write/onlyOwner&MNF")
async def ownerMNF(item: Item):
    try:
        key = convertHexToBinary(item.userPrivateKey)
        userkey =decryptionOfPrivate(key)
        hash= uploadDataToBlockchainForOwner_Mnf(userkey.decode('utf-8'),item.user_id,item.Module,item.Services,item.project,item.Data,item.File)
        return {"hash":hash}
    except Exception as e:
        print({"error":str(e)})


@app.post("/Write/Public")
async def publicfun(item: Item):
    try:
        key = convertHexToBinary(item.userPrivateKey)
        userkey =decryptionOfPrivate(key,key1)
        hash= uploadDataToBlockchainForPublic(userkey.decode('utf-8'),item.user_id,item.Module,item.Services,item.project,item.Data,item.File)
        return {"hash":hash}
    except Exception as e:
        print({"error":str(e)})

@app.post("/Write/CertainUsers")
async def existingUsers(value: writeForAprrove):
    try:
        key = convertHexToBinary(value.userPrivateKey)
        userkey =decryptionOfPrivate(key)
        hash= await uploadDataToBlockchainForCertainUsers(userkey.decode('utf-8'),value.user_id,value.Module,value.Services,value.project,value.Data,value.File,value.publicKey)
        return {"hash":hash}
    except Exception as e:
        print({"error":str(e)})

@app.post("/Write/ApprovalUsers") 
async def approveUsers(value: writeForAprrove):
    try:
        key = convertHexToBinary(value.userPrivateKey)
        userkey =decryptionOfPrivate(key)
        hash= await setApproval(userkey.decode('utf-8'),value.publicKey,value.user_id,value.Module,value.Services,value.Project)
        return {"hash":hash}
    except Exception as e:
        print({"error":str(e)})        


@app.get("/Fetch/Public")
async def GetPublic(private_key: str,user_id: int,Module: str, Services: str, Project:int):
    key = convertHexToBinary(private_key)
    userkey =decryptionOfPrivate(key)
    getData= fetchFromBlockchainForPublic(userkey.decode('utf-8'), user_id,Module,Services,Project)
    return {"getData":getData}

@app.get("/Fetch/onlyOwner&MNF")
async def GetOwnerMNF(private_key: str,user_id: int,Module: str, Services: str, Project:int):
    key = convertHexToBinary(private_key)
    userkey =decryptionOfPrivate(key)
    getData=fetchFromBlockchainForOwner_Mnf(userkey.decode('utf-8'), user_id,Module,Services,Project)
    return {"getData":getData}
    
@app.get("/Fetch/CertainUsers")
async def GetCertainUsers(private_key: str,user_id: int,Module: str, Services: str, Project:int):
    key = convertHexToBinary(private_key)
    userkey =decryptionOfPrivate(key)
    getData= getcertainUsrsandApprovedUsers(userkey.decode('utf-8'), user_id,Module,Services,Project)
    return {"getData":getData}

@app.get("/Fetch/onlyOwner")
async def GetonlyOwner(private_key: str,user_id: int,Module: str, Services: str, Project:int):
    key = convertHexToBinary(private_key)
    userkey =decryptionOfPrivate(key)   
    startTime = time.time()
    result, getData= fetchFromBlockchainForOwner (userkey.decode('utf-8'), user_id,Module,Services,Project)
    endTime = time.time()
    finalTime = endTime-startTime
    print(finalTime)
    return {"getData":getData, "get_time":finalTime ,"data_found": result}

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8005)