# *coding: utf-8*
import datetime
from urllib import response
from django.core.paginator import Paginator
from django.http import HttpResponseForbidden, JsonResponse

from django.urls import reverse
from eth_account.messages import defunct_hash_message
from tixsell import settings
from web3 import Web3
from web3.middleware import construct_sign_and_send_raw_middleware
from eth_account import Account
from eth_account.messages import encode_defunct
from eth_account.signers.local import LocalAccount
from api.serializers import *
from django.shortcuts import render, HttpResponse
from rest_framework import generics, permissions, serializers
from PIL import Image
from django_filters.rest_framework import DjangoFilterBackend
from django_filters import rest_framework as filters
from mnemonic import Mnemonic
from eth_account import Account
from django.core import signing
import logging
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import api_view, permission_classes
import json
import stripe
import io
from django.shortcuts import redirect
from django.db import connection
from web3.middleware import geth_poa_middleware
from tixsell.settings import TICKET_ABI
import sendgrid
import random
from tixsell.settings import wallet_pool
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
import requests
import boto3
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from django.utils import timezone
import pytz
from django.http import StreamingHttpResponse
from backoffice.helpers import askTva, getFreeWallet, getWalletToUseOrWait, sendEmail, sendInvoice,transactionLogger,releaseWallet
from rest_framework.response import Response
from rest_framework import status
from rest_framework.filters import OrderingFilter
import json
from mjml.mjml2html import mjml_to_html
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
import base64
logger = logging.getLogger('django')


def encrypt_private_key_AES256(raw_key: str, code_pin: str) -> str:
    salt = code_pin.encode()  # Convert string PIN to bytes
   
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(),
        length=32,
        salt=salt,
        iterations=100000,
    )
    key = base64.urlsafe_b64encode(kdf.derive(salt))
    f = Fernet(key)
    return f.encrypt(raw_key.encode()).decode()

def decrypt_private_key_AES256(encrypted_key: str, code_pin: str) -> str:
    salt = code_pin.encode()  # Convert string PIN to bytes
   
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(),
        length=32,
        salt=salt,
        iterations=100000,
    )
    key = base64.urlsafe_b64encode(kdf.derive(salt))
    f = Fernet(key)
    return f.decrypt(encrypted_key.encode()).decode()


def encrypt(raw_str: str, encryption_key: str) -> str:
    # takes in any encryption key (i.e. salt), and uses it to encrypt some object
    signer = signing.Signer(salt=encryption_key)
    return signer.sign_object(raw_str)


def decrypt(encoded_str: str, encryption_key: str) -> str:
    # takes in any encryption key (i.e. salt), and uses it to decrypt a token
    signer = signing.Signer(salt=encryption_key)
    try:
        return signer.unsign_object(encoded_str)
    except signing.BadSignature:
        raise ValueError(f"Unable to decode hash {encoded_str}")

class findOrCreateUser(generics.ListAPIView):
    permission_classes = [permissions.IsAuthenticated]
    def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            email = data['email']
             
            created = False 
            try:
                user = User.objects.get(email=email)
                
            except Exception as e:
                user = User.objects.create_user(email=email,isOrganizer=False,stripeCustomerId="")
                user.save()
                created=True
            
             #create JWT TOKEN
            from rest_framework_simplejwt.tokens import RefreshToken
            refresh = RefreshToken.for_user(user)
            jsonToReturn = {
                "status": "OK",
                "refUser":UserSerializer(user,many=False).data,
                "created":created,
                'refresh': str(refresh),
                'access': str(refresh.access_token),
            }
            return JsonResponse(jsonToReturn, safe=False)
        else:
            return HttpResponse(status=403)
    

class ticketCreated(generics.ListAPIView):
    def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            transactionHash = data['transactionHash']
            refUser = data["refUser"]
            wallet = data["wallet"]
            ticketContract = data["ticketContract"]
            refTicketType = data["refTicketType"]
            nbTickets = data["amount"]
            totalAmount = data["totalAmount"]
            try:
                user = User.objects.get(id=refUser,walletAddress__icontains=wallet)
                #cree un payment pour gérer le stock
                ticketType = TicketType.objects.get(id=refTicketType)
                payment = Payment()
                payment.refUser = user 
                payment.toUser = ticketType.refEvent.refOrganiser
                payment.refTicketType_id = refTicketType
                payment.nbTickets = nbTickets
                payment.amount = totalAmount
                # calculer les fees selltix ... 
                # 10% du montant + fixAmount 
               
                fixAmount = ticketType.fixAmount
                feesDixPourCent = (totalAmount*10)/100
                fees = feesDixPourCent + fixAmount
                payment.feesSelltix = fees
                polPrice = POLPrice.objects.get(id=1)
                payment.polPrice = polPrice.price
                payment.status=1
                payment.jsonStripe = "Transaction hash "+transactionHash
                payment.save()

                # on retrouve tous les nft de l'utilisateur sur la blockchain pour le contrat donné
                # et on ajoute dans backend si il n'existe pas...
                w3 = Web3(Web3.HTTPProvider(settings.CONTRACT_NODE_URL))
                # Initialize the address calling the functions/signing transactions
                from tixsell.settings import TICKET_ABI
                # Initialize contract ABI and address
                checkedWalletAddress = w3.to_checksum_address(wallet) 
                # Create smart contract instance
                checkedTicketAddress = w3.to_checksum_address(ticketContract)
                contract = w3.eth.contract(address=checkedTicketAddress, abi=TICKET_ABI)
                tickets = contract.functions.fetchTicketsForOwner(checkedWalletAddress).call()
                userTickets = Ticket.objects.filter(refUser=user)
                for ticketBlockchain in tickets:
                    """
                    uint256 ticketId;
                    uint256 ticketTypeId;
                    address owner;
                    bytes32 hashedTicket; // EventId:TicketType:TicketId encrypté en SHA256
                    uint256 pricePaid;
                    uint256 purchasedDate;
                    bool used;
                    bool exists;
                    """
                    ticketId=ticketBlockchain[0]
                   
                    hashedTicket = ticketBlockchain[3] 
                    result = bytearray(hashedTicket)  # for testing
                    hxstr = "".join(["{:02x}".format(v) for v in result])
                    finalTicketCode="0x"+hxstr
                    pricePaid = ticketBlockchain[4]
                    #convert to human format 
                    finalPrice = w3.from_wei(pricePaid,'ether')
                    purchasedDate = ticketBlockchain[5]
                    import datetime
                    createdAt = datetime.datetime.fromtimestamp(purchasedDate)
                   
                    needToCreate = True
                    for ticket in userTickets:
                        logger.info("====Ticket utilisateur %s %s Contrat %s Compare %d avec %d"%(user,ticket.id,ticket.refTicketType.refEvent.ticketContract,ticket.ticketId,ticketId))
                        if ticket.ticketId==ticketId and ticket.refTicketType.refEvent.ticketContract==ticketContract:
                            needToCreate=False
                            logger.info("=== Trouve pas besoin de créer")
                            break
                    logger.info("NeedToCreate ?",needToCreate)
                    if needToCreate==True:
                        newTicket = Ticket()
                        newTicket.refUser = user
                        newTicket.ticketId = ticketId
                        newTicket.refTicketType_id = str(refTicketType)
                        newTicket.lastOwner = wallet
                        newTicket.hashedTicket = finalTicketCode
                        newTicket.pricePaid = finalPrice 
                        newTicket.createdAt = createdAt
                        newTicket.transactionHash = transactionHash
                        newTicket.status = 1 #minted 
                        newTicket.save()
                        
                jsonToReturn = {
                    "status": "OK","created":needToCreate
                }
                return JsonResponse(jsonToReturn, safe=False)
            except Exception as e:
                logger.error(e)
                return HttpResponse(status=500)
        else:
            return HttpResponse(status=403)

class getUserNonce(generics.ListAPIView):
    permission_classes = [permissions.IsAuthenticated]
    def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            walletAddress = data['walletAddress']
            user = self.request.user
            import random
            nonce = random.randint(1000, 9999)
            try:
                user = User.objects.get(id=user.id)
                user.nonce = nonce
                user.walletAddress = walletAddress
                user.save()
                jsonToReturn = {
                "status": "OK",
                "refUser":user.id,
                "nonce":nonce
                }
                return JsonResponse(jsonToReturn, safe=False)
            except Exception as e:
                logger.error(e)
                return HttpResponse(status=500)               
            
        else:
            return HttpResponse(status=403)

class stripeReturnAccount(generics.ListAPIView):
    def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            refUser = data['refUser']
            try:
                merchantAccount = Seller.objects.get(user__id=refUser)
                account = stripe.Account.retrieve(merchantAccount.stripe_user_id)
                logger.info("===On recoit de stripe ")
                logger.info(account)
                #Update stripe json received 
                merchantAccount.stripe_account_json = json.dumps(account)
                
                if account["details_submitted"] and account["charges_enabled"]:
                    merchantAccount.stripe_charges_enabled=True
                merchantAccount.save()
            except Exception as e:
                logger.info(e)
            jsonToReturn = {
                "status": "OK"
            }
            return JsonResponse(jsonToReturn, safe=False)
        else:
            return HttpResponse(status=403)


class createStripeAccountLink(generics.ListAPIView):
    permission_classes = [permissions.IsAuthenticated]
    def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            refUser = data['refUser'] 
            link=None
            try:
                seller_profile = Seller.objects.get(user__id=refUser)
                refresh_url = "https://www.selltix.live/striperefresh/"+refUser+"/"
                return_url = "https://www.selltix.live/stripereturn/"+refUser+"/"
                logger.info("Return url %s"%return_url)
                logger.info("refresh_url url %s"%refresh_url)
                
                # on crée le lien de redirection
                accountLink = stripe.AccountLink.create(
                    account=seller_profile.stripe_user_id,
                    refresh_url=refresh_url,
                    return_url=return_url,
                    type="account_onboarding",
                )
                logger.info(accountLink)
                link=accountLink["url"]
                status="OK"
            except User.DoesNotExist:
                status="NOTFOUND"

            except Exception as e:
                status="ERROR"
                logger.error(e)
                HttpResponse(status=500)
            
            jsonToReturn = {
                    "status": status,
                    "link":link
            }
            return JsonResponse(jsonToReturn, safe=False)
        else:
            return HttpResponse(status=403)
class createStripeAccount(generics.ListAPIView):
    permission_classes = [permissions.IsAuthenticated]
    def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            refUser = data['refUser'] 
            link=None
            try:
                user = User.objects.get(id=refUser)
                seller_profile = Seller()
                seller_profile.user = user
                #Create stripe user account
                businessType="company"
                if user.organiserType==4:
                    businessType="individual"
                elif user.organiserType==3:
                    businessType="non_profit"
                elif user.organiserType==2 or user.organiserType==1:
                    businessType="company"
                # if user.organizerCompany and user.organiserType==2:
                #     #Add orga account info 
                #     account = stripe.Account.create(
                #         country=user.country,
                #         type="express",
                #         business_type="company",
                #         email=user.email,
                #         default_currency=user.organizerCurrency,
                #         company={
                #             "name":user.organizerCompany,
                #             "address":{
                #                 "line1":user.organizerAddress,
                #                 "city":user.organizerCity,
                #                 "postal_code":user.organizerZipCode,
                #                 "state":'',
                #                 "country":user.country
                #             },
                #             "vat_id":user.vatNumber,
                #         },
                #         business_profile={
                #             "name":user.organizerCompany,
                #             "url":user.organizerWebsite
                #         },
                #         capabilities={"card_payments": {"requested": True}, "transfers": {"requested": True}} 
                #     )
                # else:               
                account = stripe.Account.create(
                        country=user.country,
                        email=user.email,
                        default_currency=user.organizerCurrency,
                        type="express",
                        business_type=businessType,
                        capabilities={"card_payments": {"requested": True}, "transfers": {"requested": True}} 
                )
                logger.info("===On recoit de stripe ")
                logger.info(account)
                seller_profile.stripe_user_id = account["id"]
                seller_profile.stripe_account_json = json.dumps(account)
                seller_profile.save()
                refresh_url = "https://www.selltix.live/striperefresh/"+str(user.id)+"/"
                return_url = "https://www.selltix.live/stripereturn/"+str(user.id)+"/"
                logger.info("Return url %s"%return_url)
                logger.info("refresh_url url %s"%refresh_url)
                
                # on crée le lien de redirection
                accountLink = stripe.AccountLink.create(
                    account=account["id"],
                    refresh_url=refresh_url,
                    return_url=return_url,
                    type="account_onboarding",
                )
                logger.info(accountLink)
                link=accountLink["url"]
                status="OK"
            except User.DoesNotExist:
                status="NOTFOUND"

            except Exception as e:
                status="ERROR"
                link=str(e)
                logger.error(e)
                 
            
            jsonToReturn = {
                    "status": status,
                    "link":link
            }
            return JsonResponse(jsonToReturn, safe=False)
        else:
            return HttpResponse(status=403)


class checkPinWallet(generics.ListAPIView):
    permission_classes = [permissions.IsAuthenticated]
    def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            codePin = data['codePin']
             
            try:
                user = User.objects.get(id=request.user.id)
               
                #Try to decrypt wallet
                words = decrypt_private_key_AES256(user.words,codePin)
                private_key = decrypt_private_key_AES256(user.privateKey,codePin)
                 
                jsonToReturn = {
                        "status": "OK",
                        "words":words,
                        "privateKey":private_key   
                }
                return JsonResponse(jsonToReturn, safe=False) 
            except Exception as e:
                logger.error(e)
                jsonToReturn = {
                        "status": "KO"
                }
                return JsonResponse(jsonToReturn, safe=False)

class sendLoginCode(generics.ListAPIView):
    def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            email = data['email']
            if "fromGoogle" in data:
                fromGoogle = data['fromGoogle']
            else:
                fromGoogle = False
            
            user=None
            created=False
            try:
                # generate a random 6 digit code 
                user = User.objects.get(email=email)
            except User.DoesNotExist:
                #to do need to create user 
                user = User.objects.create_user(email=email,isOrganizer=False,stripeCustomerId="")
                user.save()
                created=True   
            if fromGoogle==False:
                number_part1 = str(random.randint(100, 999))
                number_part2 = str(random.randint(100, 999))
                code = number_part1 + '-' + number_part2
                    
                # send code to user using sendgrid 
                sg = sendgrid.SendGridAPIClient(settings.SENDGRID_API_KEY)
                data = {
                        "personalizations": [
                            {
                                "to": [
                                    {
                                        "email": user.email
                                    }
                                
                                ],
                                "subject": "SellTix - Votre code de connexion:"+code,
                                "dynamic_template_data": {
                                    "code_selltix":code
                                }
                            }
                        ],
                        "from": {
                            "email": "SellTix <contact@selltix.live>"
                        },
                    
                        "template_id": settings.TEMPLATE_ID_CODESELLTIX
                    }
                response = sg.client.mail.send.post(request_body=data)
                if response.status_code == 202:
                    user.lastLoginCode = code
                
                    user.lastLoginCodeDate = datetime.datetime.now()
                    user.save()
                    status=""
                    if created:
                        status="NEWUSER"
                    else:
                        status="OK"
                    
                    jsonToReturn = {
                            "status": status,
                            "created":created
                    }                
                else:
                    jsonToReturn = {
                                "status": "KO",
                                "reason": response.body
                    }
            else:
                status=""
                if created:
                    status="NEWUSER"
                else:
                    status="OK"
                #create JWT TOKEN
                from rest_framework_simplejwt.tokens import RefreshToken
                refresh = RefreshToken.for_user(user)
                jsonToReturn = {
                            "status": status,
                            "created":created,
                            "refUser":UserSerializer(user,many=False).data,
                            'refresh': str(refresh),
                            'access': str(refresh.access_token),
                        }
                    
            return JsonResponse(jsonToReturn, safe=False)
            

class checkLoginCode(generics.ListAPIView):
    def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            code = data['code']
            email = data['email']
            try:
                user = User.objects.get(email=email)
                logger.info("=== On compare code recu %s avec code en base %s ==="%(code,user.lastLoginCode))
                if user.lastLoginCode == code:
                    # check time between last code and now
                    time_difference = timezone.now() - user.lastLoginCodeDate
                    if time_difference.total_seconds() < 180:
                        #create JWT TOKEN
                        from rest_framework_simplejwt.tokens import RefreshToken
                        refresh = RefreshToken.for_user(user)
                        jsonToReturn = {
                            "status": "OK",
                            "refUser":UserSerializer(user,many=False).data,
                            'refresh': str(refresh),
                            'access': str(refresh.access_token),
                        }
                        return JsonResponse(jsonToReturn, safe=False)
                    else:
                        jsonToReturn = {
                            "status": "KO",
                            "reason": "code expired"
                        }
                        return JsonResponse(jsonToReturn, safe=False)
                else:
                    jsonToReturn = {
                        "status": "KO"
                    }
                    return JsonResponse(jsonToReturn, safe=False)
            except Exception as e:
                logger.error(e)
                jsonToReturn = {
                        "status": "KO"
                }
                return JsonResponse(jsonToReturn, safe=False)

class checkPinCode(generics.ListAPIView):
    permission_classes = [permissions.IsAuthenticated]
    def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            codePinRecu = data['code']
            
            try:
                user = User.objects.get(id=request.user.id)
                logger.info("=== On compare code pin recu %s avec code en base  ==="%(codePinRecu))
                thePinCodeToDecrypt = decrypt_private_key_AES256(user.pinCode,os.getenv("SECRET_PIN_KEY"))
                realCodePin = 9999 - int(thePinCodeToDecrypt) 
                if (int(codePinRecu) == realCodePin):
                    jsonToReturn = {
                                "status": "OK"
                    }
                else:
                    jsonToReturn = {
                        "status": "KO"
                    }
                return JsonResponse(jsonToReturn, safe=False)
            except Exception as e:
                logger.error(e)
                jsonToReturn = {
                        "status": "KO"
                }
                return JsonResponse(jsonToReturn, safe=False)
            
class createOrganizerContract(generics.ListAPIView):
    permission_classes = [permissions.IsAuthenticated]
    def post(self, request, format=None):
        if request.method == "POST":
            try:
                data = request.data
                walletAddress = data['walletAddress']
                user = User.objects.get(id=request.user.id)
                user.walletAddress = walletAddress
                user.save()
                try:
                    from tixsell.settings import wallet_pool
                    import time
                    
                    web3 = Web3(Web3.HTTPProvider(settings.CONTRACT_NODE_URL))
                    DEBUG = os.getenv("DEBUG")
                     
                    if DEBUG:
                        web3.middleware_onion.inject(geth_poa_middleware, layer=0)
                        
                    logger.info("===On essaye recupere wallet selltix libre pour créer smart contract organisateur") 
                    # GEt wallet to mint
                    ADRESS_TO_USE, PRIVATE_KEY_TO_USE = getFreeWallet()

                    logger.info("===Wallet Address : "+ADRESS_TO_USE)
                    # Create organiser smart contract and transfer 
                    checkedContractAddress = web3.to_checksum_address(settings.ORGAFACTORY_ADDRESS) 
                    checkedWalletAddress = web3.to_checksum_address(ADRESS_TO_USE) 
                    # Create smart contract instance
                    contract = web3.eth.contract(address=checkedContractAddress, abi=settings.ORGANISER_FACTORY_ABI)
                    # : call deploy sart contract function
                    nonce = web3.eth.get_transaction_count(checkedWalletAddress,'latest')
                    transaction = contract.functions.deployOrganizerContract(web3.to_checksum_address(walletAddress)).build_transaction({
                            'from': checkedWalletAddress,
                            'nonce': nonce,
                            'gasPrice': web3.to_wei('30', 'gwei'),
                            'gas': '0'
                    })
                    gas = web3.eth.estimate_gas(transaction)
                    transaction.update({'gas': gas}) 
                    signed_txn = web3.eth.account.sign_transaction(transaction, private_key=PRIVATE_KEY_TO_USE)
                    receipt = web3.eth.send_raw_transaction(signed_txn.rawTransaction)
                    logger.info("waiting deploy organizer contract")
                    resultTx = web3.eth.wait_for_transaction_receipt(receipt, timeout=300)
                    logger.info('receipt with confirmed trx hash (after trx executed)')
                    logger.info(resultTx)
                    status = resultTx["status"]
                    transactionLogger(nonce,resultTx,'deployOrganizerContract',status,walletAddress)
                    if status==1:
                        # Wait for additional confirmations
                        current_block = web3.eth.block_number
                        logger.info("===Current block ",current_block)
                        target_block = resultTx['blockNumber'] + 3  # Wait for 3 more blocks
                        logger.info("===Target block ",target_block)
                        needToWait=True
                        while needToWait:
                            latestblock = web3.eth.block_number
                            logger.info("===latestblock ",latestblock)
                            if latestblock >= target_block:
                                needToWait = False
                            else:
                                time.sleep(1)
                             
                        releaseWallet(ADRESS_TO_USE)
                        smartContractDeployed = contract.functions.contractForOrganizer(web3.to_checksum_address(walletAddress)).call()
                        #associate created address to user organizerSmartContract
                        logger.info("=== On a deployed")
                        logger.info(smartContractDeployed)
                        user.organizerContract = smartContractDeployed
                        balance_wei = web3.eth.get_balance(web3.to_checksum_address(walletAddress))
                        balance_matic = web3.from_wei(balance_wei, 'ether')
                        logger.info(f"Organiser wallet balance: {balance_matic} POL")

                        # You can then compare this balance to a minimum threshold
                        min_balance_threshold = 1.1  # for example, 0.52 POL
                        if balance_matic < min_balance_threshold:
                            logger.info("Organiser wallet balance is below the threshold. Sending POL.")
                            maticToSendAmount = "1"
                            from backoffice.helpers import sendMatic
                            envoiDone = sendMatic(walletAddress,maticToSendAmount)
                            if envoiDone:
                                user.maticSent=True
                        user.save()
                        jsonToReturn = {
                            "status": "OK","user":UserSerializer(user,many=False).data
                        }
                    else:
                        releaseWallet(ADRESS_TO_USE)
                        jsonToReturn = {
                            "status": "KO",
                            "reason": "error deploying organizer contract"
                        }
                        
                    
                except Exception as e:
                    logger.error(e)
                
               
                return JsonResponse(jsonToReturn, safe=False)
            except Exception as e:
                logger.error(e)
                jsonToReturn = {
                    "status": "KO"
                }
                return JsonResponse(jsonToReturn, safe=False)
            

class createWallet(generics.ListAPIView):
    permission_classes = [permissions.IsAuthenticated]
    def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            codePin = data['codePin'] 
            refUser = data['refUser'] 
             
            mnemo = Mnemonic("english")
            words = mnemo.generate(strength=256)
            seed = mnemo.to_seed(words, passphrase=codePin)
            public_key=None
            private_key=None
            try:
                #w3 = Web3(Web3.HTTPProvider(settings.CONTRACT_NODE_URL))
                Account.enable_unaudited_hdwallet_features()
                account = Account.from_mnemonic(words)
                # Get keys
                private_key = account.key
                public_key = account.address
                #update user with encoding keys
                # Create a signer wallet
                user = User.objects.get(id=refUser)
                user.generatedWallet=True
                user.walletAddress = public_key
                user.publicKey = public_key
                #encrypt private key 
                encryptedPrivateKey = encrypt_private_key_AES256(private_key.hex(),codePin)
                user.privateKey = encryptedPrivateKey
                user.words = encrypt_private_key_AES256(words,codePin)
                # calcule chiffre 
                theCode = 9999 - int(codePin)
                user.pinCode = encrypt_private_key_AES256(str(theCode),os.getenv("SECRET_PIN_KEY"))
                user.save()
                status="OK"
                jsonToReturn = {
                    "status": status,
                    "user":UserSerializer(user,many=False).data
                }
                return JsonResponse(jsonToReturn, safe=False)
            except Exception as e:
                status="ERROR"
                logger.error(e)
                return HttpResponse(status=500)
        else:
            return HttpResponse(status=403)


class checkQRCode(generics.ListAPIView):
     permission_classes = [permissions.IsAuthenticated]
     def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            qrCode = data['qrcode']
            ticketContract = data["ticketContract"] 
            valeur=None
            logger.info("On va decrypter %s"%qrCode)
            try:
                import base64
                from web3 import Web3
                convertbytes = qrCode.encode("ascii")
                # converting into bytes from base64 system
                convertedbytes = base64.b64decode(convertbytes)
                # decoding the ASCII characters into alphabets
                decodedsample = convertedbytes.decode("ascii")
                logger.info("Valeur %s",decodedsample)
                x = decodedsample.split("-")
                walletAddres = x[0]
                hashedTicket = x[1]
                contract = x[2]
                timeqrcode = x[3]
                #get current timestamp and if more than 30 minutes reject
                import time 
                ts = time.time()
                logger.info("Compare timestmap %f avec %d"%(float(ts),float(timeqrcode)))
                difference = (float(ts)-float(timeqrcode)) / 60 
                logger.info("Difference %f"%difference)
                if difference>1:
                    jsonToReturn = {
                    "status": "KO",
                    "cause":"QRCode trop ancien (>1 minute)"
                    }
                    return JsonResponse(jsonToReturn, safe=False)
               
                w3 = Web3(Web3.HTTPProvider(settings.CONTRACT_NODE_URL))
                
                # Initialize the address calling the functions/signing transactions
                checkedWalletAddress = w3.to_checksum_address(walletAddres) 
                # Create smart contract instance
                contract = w3.eth.contract(address=ticketContract, abi=TICKET_ABI)
                tickets = contract.functions.fetchTicketsForOwner(checkedWalletAddress).call()
                for ticket in tickets:
                    ticketCode = ticket[3] 
                    result = bytearray(ticketCode)  # for testing
                    hxstr = "".join(["{:02x}".format(v) for v in result])
                    finalTicketCode="0x"+hxstr
                    if finalTicketCode==hashedTicket:
                        valeur="FOUND"
                        # get tickets and check not already scanned
                        try:
                            tickets = Ticket.objects.filter(lastOwner__iexact=walletAddres.lower(),hashedTicket__iexact=hashedTicket.lower())
                            for ticket in tickets:
                                if ticket.scanned:
                                    jsonToReturn = {
                                        "status": "KO",
                                        "cause":"Billet déjà scanné !"
                                        }
                                    return JsonResponse(jsonToReturn, safe=False)
                                else:
                                    ticket.scanned=True
                                    ticket.save() 
                        except Exception as e:
                            logger.info(e)
                        break
            except Exception as e:
                logger.error(e)
               
                 
            jsonToReturn = {
                "status": "OK",
                "valeur":valeur
                }
            return JsonResponse(jsonToReturn, safe=False)
        else:
            return HttpResponse(status=403)


def get_bytes_value(image):
    img_byte_arr = io.BytesIO()
    image.save(img_byte_arr, format='PNG')
    return img_byte_arr.getvalue()
class createQRCode(generics.ListAPIView):
     def get(self, request, format=None):
        if request.method == "GET":
            ticketContract = request.GET.get("ticketAddress")
            tokenId = request.GET.get("tokenId")
            try:
                import base64
                from web3 import Web3
                w3 = Web3(Web3.HTTPProvider(settings.CONTRACT_NODE_URL))
                # Initialize the address calling the functions/signing transactions
                caller = "0xf4f50f5d16a91980e8534872a0faf7ce4773c52a"
                private_key = "0x3f37c414f762ddb08a5a604cc136bc9d6af37537f1429993c310fa83002ce901"  # To sign the transaction
                # Initialize address nonce
                # Initialize contract ABI and address
                checkedTicketAddress = w3.to_checksum_address(ticketContract) 
                # Create smart contract instance
                contract = w3.eth.contract(address=checkedTicketAddress, abi=TICKET_ABI)
                ownerAddress = contract.functions.ownerOf(int(tokenId)).call()
                checkedOwnerAddress = w3.to_checksum_address(ownerAddress) 
                ticket = contract.functions.tickets(int(tokenId)).call()
                ticketCode = ticket[3] #prend le hash
                result = bytearray(ticketCode)  # for testing
                hxstr = "".join(["{:02x}".format(v) for v in result])
                finalTicketCode="0x"+hxstr
                import time
                qrCodeString = checkedOwnerAddress+"-"+finalTicketCode+"-"+str(time.time())
                # base64 encoding
                message_bytes = qrCodeString.encode('ascii')
                base64QrCodeString = base64.b64encode(message_bytes)
                # or encrypt using a private key from environment

                #fabrique le QRCode
                import qrcode
                from qrcode.image.styledpil import StyledPilImage
                qr = qrcode.QRCode(
                    version=1,
                    error_correction=qrcode.constants.ERROR_CORRECT_L,
                    box_size=8,
                    border=4,
                )
                qr.add_data(base64QrCodeString)
                qr.make(fit=True)
                #img = qr.make_image(fill_color="black", back_color="white")
                img = qr.make_image(image_factory=StyledPilImage, embeded_image_path="static/assets/images/logoTixsell.png")
                # return an image
                return HttpResponse(get_bytes_value(img), content_type="image/png")

            except Exception as e:
                logger.error(e)
                import numpy as np
                img = np.zeros([100,100,3],dtype=np.uint8)
                img.fill(255)
                return HttpResponse(img, content_type="image/png")
        else:
            import numpy as np
            img = np.zeros([100,100,3],dtype=np.uint8)
            img.fill(255)
            return HttpResponse(img, content_type="image/png")

class sendMatic(generics.ListAPIView):
    permission_classes = [permissions.IsAuthenticated]
    def post(self, request, format=None):
        if request.method == "POST":
            user = User.objects.get(id=self.request.user.id)
            walletAddress = user.walletAddress
            try:
                from tixsell.settings import wallet_pool
                import time
                 
                w3 = Web3(Web3.HTTPProvider(settings.CONTRACT_NODE_URL))
                DEBUG = os.getenv("DEBUG")
                CHAIN_ID = settings.CHAIN_ID
                if DEBUG:
                    w3.middleware_onion.inject(geth_poa_middleware, layer=0)
                   
                ADRESS_TO_USE = settings.SELLTIX_WALLET
                PRIVATE_KEY_TO_USE = settings.SELLTIX_WALLET_PRIVATE_KEY
                walletToUse="wallet1"
                #Need to wait for wallet being unlocked and then locked it while processing 
                getWalletToUseOrWait(walletToUse)
                
                checkedTransferTOWalletAddress = w3.to_checksum_address(walletAddress) 
                checkedFromWalletAddress = w3.to_checksum_address(ADRESS_TO_USE) 
                nonce = w3.eth.get_transaction_count(checkedFromWalletAddress,"latest")
                #check if amount in request or not
                if "amount" in request.data:
                    amount = w3.to_wei(request.data["amount"], 'ether')
                else:
                    amount = w3.to_wei('0.10', 'ether')
                # Estimating gas price
                gasEstimate = w3.eth.estimate_gas({
                    "from": checkedFromWalletAddress,
                    "to": checkedTransferTOWalletAddress, "value": amount
                })

                # creating a raw transaction
                raw_transaction = {
                    "from": checkedFromWalletAddress,
                    "to": checkedTransferTOWalletAddress,
                    "value": amount,
                    "gas": gasEstimate,
                    "gasPrice": w3.eth.gas_price,
                    "nonce": nonce,
                    "chainId": CHAIN_ID  # Matic Mainnet Chain ID
                }
                
                signed_tx = w3.eth.account.sign_transaction(raw_transaction, private_key=PRIVATE_KEY_TO_USE) 
                receipt = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
                resultTx = w3.eth.wait_for_transaction_receipt(receipt, timeout=300)
                logger.info('receipt with confirmed trx hash (after trx executed)')
                logger.info(resultTx)
                status = resultTx["status"]
                transactionLogger(nonce,resultTx,'sendmatic',status,checkedTransferTOWalletAddress)
                wallet_pool.release_wallet(walletToUse)
                if status==1:
                    # : send selltix an email 
                     # send code to user using sendgrid 
                    sg = sendgrid.SendGridAPIClient(settings.SENDGRID_API_KEY)
                    data = {
                            "personalizations": [
                                {
                                    "to": [
                                        {
                                            "email": "csurbier@idevotion.fr"
                                        },
                                        {
                                            "email": "rofar@mac.com"
                                        }
                                    
                                    ],
                                    "subject": "SellTix - Envoi de 0.10 Matic sur wallet "+walletAddress
                                }
                            ],
                            "from": {
                                "email": "SellTix <contact@selltix.live>"
                            } 
                        }
                    response = sg.client.mail.send.post(request_body=data)
                    user.maticSent = True
                    user.save()
                    jsonToReturn = {
                        "status": "OK"
                    }
                else:
                    jsonToReturn = {
                        "status": "KO"
                    }
                return JsonResponse(jsonToReturn, safe=False)
            except Exception as e: 
                logger.info(e)
                jsonToReturn = {
                    "status": "KO"
                }
                return JsonResponse(jsonToReturn, safe=False)

        else:
            return HttpResponse(status=403)

class verifySignature(generics.ListAPIView):
    permission_classes = [permissions.IsAuthenticated]
    def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            walletAddress = data['walletAddress']
            signature = data['signature']
            logger.info("===On verifie signature %s pour %s"%(walletAddress,signature))
            walletUsed = None
            refresh = None
            try:
                
                w3 = Web3(Web3.HTTPProvider(settings.CONTRACT_NODE_URL))
                user = User.objects.get(walletAddress=walletAddress,id=self.request.user.id)
                nonce = user.nonce
                messageToSign = "Merci de signer ce message. Le code d'authentication est:"+ nonce
                message_hash = encode_defunct(text=messageToSign)
                logger.info(message_hash)
                walletUsed = w3.eth.account.recover_message(message_hash, signature=signature)
                logger.info("===Compare %s avec %s"%(walletUsed.lower(),walletAddress.lower()))
                if walletUsed.lower()==walletAddress.lower():
                    logger.info("==Singature ok")
                    status="OK"
                    #create JWT TOKEN
                    from rest_framework_simplejwt.tokens import RefreshToken
                    refresh = RefreshToken.for_user(user)
                else:
                    logger.info("==Singature ko")
                    status="KO"

            except Exception as e:
                status="KO"
                logger.error(e)
                HttpResponse(status=500)
            if refresh:
                jsonToReturn = {
                    "status": status,
                    'refresh': str(refresh),
                    'access': str(refresh.access_token),
                }
            else:
                jsonToReturn = {
                    "status": status
                }
            return JsonResponse(jsonToReturn, safe=False)
        else:
            return HttpResponse(status=403)
        
""" 
class deployOrganizerContract(generics.ListAPIView):
    def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            walletAddress = data['walletAddress']
            try:
                w3 = Web3(Web3.HTTPProvider(settings.CONTRACT_NODE_URL))
                #https://ethereum.stackexchange.com/questions/134720/execute-a-contract-function-from-web3-py
                #account: LocalAccount = Account.from_key(settings.PRIVATE_KEY)
                #w3.middleware_onion.add(construct_sign_and_send_raw_middleware(account))
                jsonToReturn ={}
            except Exception as e:
                status="KO"
                logger.error(e)
                return HttpResponse(status=500)
            return JsonResponse(jsonToReturn, safe=False)
        else:
            return HttpResponse(status=403)

"""
class InscriptionNewsletterCreateView(generics.CreateAPIView):
    queryset = InscriptionNewsletter.objects.all()
    serializer_class = InscriptionNewsletterSerializer

     
class UserListView(generics.ListCreateAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = User.objects.all()
    serializer_class = UserSerializer
    filterset_fields = ('id', 'email')
    filter_backends = (DjangoFilterBackend,)
    swagger_schema = None

    def get_queryset(self):
        user = self.request.user
        queryset = User.objects.filter(pk=user.id)
        return queryset

class CreateTasksAPICreateView(generics.ListCreateAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = Tasks.objects.all()
    serializer_class = TasksSerializer
 
class UserLogin(generics.ListAPIView):
   def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            username = data['username']
            password = data["password"]
            try:
                
                user = User.objects.get(username=username,password=password)
                from rest_framework_simplejwt.tokens import RefreshToken
                refresh = RefreshToken.for_user(user)
                jsonToReturn = {
                    "status": "OK",
                    "refUser":UserSerializer(user,many=False).data,
                    'refresh': str(refresh),
                    'access': str(refresh.access_token),
                }
                return JsonResponse(jsonToReturn, safe=False)
            except Exception as e:
                status="KO"
                logger.info(e)
                return HttpResponse(status=401)
        else:
            return HttpResponse(status=403)
        
class UserDetailView(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = User.objects.all()
    serializer_class = FullUserSerializer
    filterset_fields = ('email', 'password',)
    filter_backends = (DjangoFilterBackend,)
    
    swagger_schema = None

    def get_queryset(self):
        user = self.request.user
        queryset = User.objects.filter(pk=user.id)
        return queryset


class ContentListView(generics.ListCreateAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = Content.objects.all().select_related("refOrganiser")
    serializer_class = ContentSerializer
    

    def get_queryset(self):
        user = self.request.user
        queryset = Content.objects.filter(refOrganiser=user.id)
        return queryset
   
class ContentDetailView(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = Content.objects.all().select_related("refOrganiser") 
    serializer_class = ContentSerializer
    
    def get_queryset(self):
        if self.request.method == 'GET':
            return super().get_queryset()
        #otherwie filter to avoid security issue
        user = self.request.user
        queryset = Content.objects.filter(refOrganiser=user.id)
        return queryset
    
class getContentToSell(generics.RetrieveAPIView):
    queryset = Content.objects.all().select_related("refOrganiser") 
    serializer_class = ContentToSellSerializer



class EventFilter(filters.FilterSet):
    ticketContract = filters.CharFilter(lookup_expr='icontains')
    eventContract = filters.CharFilter(lookup_expr='icontains')
    slug = filters.CharFilter(lookup_expr='icontains')
    paymentContract = filters.CharFilter(lookup_expr='icontains')
    class Meta:
        model = Event
        fields = {
            'refOrganiser': ['exact'],
            'eventDate':['exact','gte','lte'],
            'status':['exact','gte','lte'],
            'isPrivate':['exact']
        }
        

class EventListView(generics.ListAPIView):
    queryset = Event.objects.all().select_related("refOrganiser").select_related("refCategory").select_related("refEventPageTemplate").order_by('status', '-createdAt')
    filterset_class = EventFilter
    filter_backends = (DjangoFilterBackend, OrderingFilter)
    ordering_fields = ['status', 'eventDate','createdAt']
    ordering = ['status', '-createdAt']
    serializer_class = EventSerializer
    
class EventCreateView(generics.CreateAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = Event.objects.all().select_related("refOrganiser").select_related("refCategory").select_related("refEventPageTemplate")
    serializer_class = EventSerializer

    
   
class EventDetailView(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = Event.objects.all().select_related("refOrganiser").select_related("refCategory").select_related("refEventPageTemplate")
    serializer_class = EventSerializer

class EventDetailByIdView(generics.RetrieveAPIView):
    queryset = Event.objects.all().select_related("refOrganiser").select_related("refCategory").select_related("refEventPageTemplate")
    serializer_class = EventSerializer

class EventParticipantListView(generics.ListCreateAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = EventParticipant.objects.all().select_related("refEvent").select_related("refUser")
    filterset_fields = ('refEvent','refUser', )
    filter_backends = (DjangoFilterBackend,)
    serializer_class = EventParticipantSerializer

class EventParticipantDetailView(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = EventParticipant.objects.all().select_related("refEvent").select_related("refUser")
    serializer_class = EventParticipantSerializer

class EventPayeesListView(generics.ListCreateAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = EventPayees.objects.all().select_related("refEvent")
    filterset_fields = ('refEvent', )
    filter_backends = (DjangoFilterBackend,)
    serializer_class = EventPayeesSerializer


class EventPayeesDetailView(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = EventPayees.objects.all().select_related("refEvent")
    serializer_class = EventPayeesSerializer


class ScanUsersListView(generics.ListCreateAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = ScanUsers.objects.all().select_related("user")
    filterset_fields = ('user', )
    filter_backends = (DjangoFilterBackend,)
    serializer_class = ScanUsersSerializer

    def get_queryset(self):
        user = self.request.user
        queryset = ScanUsers.objects.filter(user=user.id)
        return queryset

class ScanUsersDetailView(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = ScanUsers.objects.all().select_related("user")
    serializer_class = ScanUsersSerializer
    def get_queryset(self):
        user = self.request.user
        queryset = ScanUsers.objects.filter(user=user.id)
        return queryset

class TicketTypeListView(generics.ListAPIView):
    queryset = TicketType.objects.all().select_related("refEvent").select_related("refTicketTypeTemplate") 
    serializer_class = TicketTypeSerializer
    
   

class TicketTypeCreateView(generics.CreateAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = TicketType.objects.all().select_related("refEvent").select_related("refTicketTypeTemplate") 
    serializer_class = TicketTypeCreateSerializer

class TicketTypeUpdateView(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = TicketType.objects.all().select_related("refEvent").select_related("refTicketTypeTemplate") 
    serializer_class = TicketTypeCreateSerializer

class TicketTypeDetailView(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = TicketType.objects.all().select_related("refEvent").select_related("refTicketTypeTemplate") 
    serializer_class = TicketTypeSerializer

class TicketTypeForEventView(generics.ListAPIView):
    queryset = TicketType.objects.all().select_related("refEvent").select_related("refTicketTypeTemplate") 
    serializer_class = TicketTypeSerializer

class TicketAPIListView(generics.ListCreateAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = Ticket.objects.all().select_related("refTicketType").select_related("refUser") 
    serializer_class = TicketSerializer
    
    def get_queryset(self):
        user = self.request.user
        queryset = Ticket.objects.filter(refUser=user,status=1).order_by("-createdAt").select_related(
            "refUser").select_related("refTicketType")
        return queryset

class TicketSoldForEventAPIListView(generics.ListCreateAPIView):
    permission_classes = [permissions.IsAuthenticated]
    serializer_class = TicketSoldSerializer
    filterset_fields = ('refTicketType__refEvent',)
    filter_backends = (DjangoFilterBackend,)
   
    #Ne peut voir que ses events...  
    def get_queryset(self):
        user = self.request.user
        queryset = Ticket.objects.filter(refTicketType__refEvent__refOrganiser=user).order_by("createdAt").select_related(
            "refUser").select_related("refTicketType")
        return queryset

class TicketDetailView(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = Ticket.objects.all().select_related("refTicketType", "refUser")
    serializer_class = TicketSerializer
    
class TicketTypeTemplateDetailView(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = TicketTypeTemplate.objects.all()
    serializer_class = TicketTypeTemplateSerializer


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def createpaymentintentOffrir(request):
    if request.method == "POST":
         totalAmount = request.data["totalAmount"]
         selltix_fees = request.data["fees"]
         ticketId = request.data["ticketId"]
         stripe.api_key = settings.STRIPE_API_KEY
         try:
            #refUser = request.user.id
            ticket = Ticket.objects.get(id=ticketId)
            
            theOrganisateur = User.objects.get(id=ticket.refTicketType.refEvent.refOrganiser.id)
            refUser = request.user.id
            theUser = User.objects.get(id=refUser)
            customer = None
            if not theOrganisateur.stripeCustomerId:
                    customer = stripe.Customer.create(email=theOrganisateur.email)
                    logger.info("on creer customer %s" % customer)
                    theOrganisateur.stripeCustomerId = customer["id"]
                    theOrganisateur.save()
             
            logger.info("on va creer payment intent pour un montant de %d" %int(totalAmount))
            logger.info("et stripe customer %s" %theOrganisateur.stripeCustomerId)
             
            ephemeralKey = stripe.EphemeralKey.create(
                        customer=theOrganisateur.stripeCustomerId,
                        stripe_version='2020-08-27',
            )
            intent = stripe.PaymentIntent.create(
                    amount=int(totalAmount),
                    currency=theOrganisateur.organizerCurrency.lower(),
                    automatic_payment_methods={"enabled": True},
                   # setup_future_usage='on_session',
                    description="Offrir un billet",
                    stripe_account=theOrganisateur.seller.stripe_user_id,
                    application_fee_amount=int(selltix_fees)
            )
            logger.info(f"selltix_fees: {selltix_fees}")
            logger.info(f"feesSeltix being set to: {float(selltix_fees/100)}")
            # On cree un payment 
            payment = Payment()
            payment.status=0
            payment.refUser_id = theUser.id
            payment.toUser = theOrganisateur
            payment.typePayment = 1
            payment.refTicketType = ticket.refTicketType
            payment.refContent_id = None
            payment.jsonStripe = intent 
            payment.paymentIntent = intent["id"]
            payment.amount = float(totalAmount/100)
            payment.feesSelltix = float(selltix_fees/100)
            polPrice = POLPrice.objects.get(id=1)
            payment.polPrice = polPrice.price
            payment.nbTickets = 0
            payment.isContent = False
            payment.withWallet = None
            payment.save()
            newdict = {'status': 'OK',"intent": intent,"refPayment":payment.id,"ephemeralKey":ephemeralKey,"customer":theUser.stripeCustomerId}
            
            logger.info(newdict)
            return JsonResponse(newdict, safe=False)
                   
         except Exception as e:
            logger.error(e) 
            #execution reverted: Amount too high: no reservation available at this moment
            if "execution reverted" in  str(e):
                causes = str(e) 
                newdict = {'status': 'KO',"cause":causes}
            else:
                newdict = {'status': 'KO',"cause":"exception"}
            return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def createpaymentintentWithFees(request):
    """
        Utiliser pour le paiement contenu
    """
    if request.method == "POST":
        amount =  request.data["amount"]
        refOrganizer = request.data["refOrganizer"]
        description = request.data["description"]
        selltix_fees = request.data["fees"]
        contentId = request.data["contentId"]
        isContent = request.data["isContent"]
        stripe.api_key = settings.STRIPE_API_KEY
        logger.info("===Stripe key %s" % settings.STRIPE_API_KEY)
        try:
            theOrganizer = User.objects.get(id=refOrganizer)
          
            amount = amount * 100
            refUser = request.user.id
            theUser = User.objects.get(id=refUser)
            #check si le theOrganizer a un customer stripe
            customer = None
            if not theOrganizer.stripeCustomerId:
                    customer = stripe.Customer.create(email=theOrganizer.email)
                    logger.info("on creer customer %s" % customer)
                    theOrganizer.stripeCustomerId = customer["id"]
                    theOrganizer.save()
             
            logger.info("on va creer payment intent pour un montant de %d" %int(amount))
            logger.info("et stripe customer %s" %theOrganizer.stripeCustomerId)
             
            ephemeralKey = stripe.EphemeralKey.create(
                        customer=theOrganizer.stripeCustomerId,
                        stripe_version='2020-08-27',
            )
            intent = stripe.PaymentIntent.create(
                    amount=int(amount),
                    currency=theOrganizer.organizerCurrency.lower(),
                    automatic_payment_methods={"enabled": True},
                   # setup_future_usage='on_session',
                    description=description,
                    stripe_account=theOrganizer.seller.stripe_user_id,
                    application_fee_amount=int(selltix_fees)
            )
            logger.info(f"selltix_fees: {selltix_fees}")
            logger.info(f"feesSeltix being set to: {float(selltix_fees/100)}")
            # On cree un payment 
            payment = Payment()
            payment.status=0
            payment.refUser_id = theUser.id
            payment.toUser = theOrganizer
            payment.refTicketType_id = None
            payment.refContent_id = contentId
            payment.jsonStripe = intent 
            payment.paymentIntent = intent["id"]
            payment.amount = float(amount/100)
            payment.feesSelltix = float(selltix_fees/100)
            payment.nbTickets = 0
            payment.isContent = True
            payment.withWallet = None
            payment.save()
            newdict = {'status': 'OK',"intent": intent,"refPayment":payment.id,"ephemeralKey":ephemeralKey,"customer":theUser.stripeCustomerId}
            
            logger.info(newdict)
            return JsonResponse(newdict, safe=False)
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO'}
            return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)
    

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def createReservation(request):
    if request.method == "POST":
         wallet_from_pool = None
         reservationId = request.data["reservationId"]
         ticketContract = request.data["ticketContract"]
         totalAmount = request.data["totalAmount"]
         ticketTypeId = request.data["ticketTypeId"]
         description =  request.data["description"] #Billet libelle
         stripe.api_key = settings.STRIPE_API_KEY
         selltix_fees = request.data["fees"]
         refOrganizer = request.data["refOrganizer"]
         refTicketType = request.data["refTicketType"]
         toUser = request.data["toUser"]
         nbTickets = request.data["nbTickets"]
         # il faut aussi créer un ticket de reservation sur la blockchain
         try:
            #Il faut demander au pool de Thread un wallet libre et attendre si pas de wallet libre 
             # GEt wallet to mint
            ADRESS_TO_USE, PRIVATE_KEY_TO_USE = getFreeWallet()
            
            web3 = Web3(Web3.HTTPProvider(settings.CONTRACT_NODE_URL))
            DEBUG = os.getenv("DEBUG")
            if DEBUG:
                web3.middleware_onion.inject(geth_poa_middleware, layer=0)
            
            logger.info("===Wallet Address : "+ADRESS_TO_USE)
            checkedWalletAddress = web3.to_checksum_address(ADRESS_TO_USE) 
            # Create smart contract instance
          
            checkedTicketAddress = web3.to_checksum_address(ticketContract)
            logger.info("===Contract Address : "+checkedTicketAddress)
            contract = web3.eth.contract(address=checkedTicketAddress, abi=TICKET_ABI)
            # : call 
            nonce = web3.eth.get_transaction_count(checkedWalletAddress,"latest")
            logger.info("===nonce : "+str(nonce))
            transaction = contract.functions.createReservation(reservationId,int(ticketTypeId),int(nbTickets)).build_transaction({
                'from': checkedWalletAddress,
                'nonce': nonce,
                'gasPrice': web3.to_wei('40', 'gwei'),
                'gas': '0'
            }) 
            gas = web3.eth.estimate_gas(transaction)
            transaction.update({'gas': gas})
             
            signed_txn = web3.eth.account.sign_transaction(transaction, private_key=PRIVATE_KEY_TO_USE)
            receipt = web3.eth.send_raw_transaction(signed_txn.rawTransaction)
            logger.info('preview trx hash (trx could be in pending status)', web3.to_hex(web3.keccak(signed_txn.rawTransaction)))
            # blocking: Wait for the transaction to be mined, and get the transaction receipt
            resultTx = web3.eth.wait_for_transaction_receipt(receipt)
            logger.info('receipt with confirmed trx hash (after trx executed)',resultTx )
            status = resultTx["status"]
            transactionLogger(nonce,resultTx,'createReservation',status,checkedWalletAddress)
            #release the wallet to the pool as soon as possible
            releaseWallet(ADRESS_TO_USE)
             
            if status==1:
                # créer directement le payment intent 
                try:
                    theOrganizer = User.objects.get(id=refOrganizer)
                    refUser = request.user.id
                    theUser = User.objects.get(id=refUser)
                    #check si le user a un customer stripe
                    if not theUser.stripeCustomerId:
                        customer = stripe.Customer.create(email=theUser.email)
                        logger.info("on creer customer %s" % customer)
                        theUser.stripeCustomerId = customer["id"]
                        theUser.save()
                    logger.info("on va creer payment intent pour un montant de %d" %int(totalAmount))
                    logger.info("et stripe customer %s" %theUser.stripeCustomerId)
                     
                    #Get the currency from the event
                    ticketType = TicketType.objects.get(id=refTicketType)
                    ephemeralKey = None
                    if totalAmount>0:
                        ephemeralKey = stripe.EphemeralKey.create(
                            customer=theUser.stripeCustomerId,
                            stripe_version='2020-08-27',
                        )
                        intent = stripe.PaymentIntent.create(
                            amount=int(totalAmount),
                            currency=ticketType.refEvent.currency.lower(),
                            automatic_payment_methods={"enabled": True},
                        # setup_future_usage='on_session',
                            description=description,
                            stripe_account=theOrganizer.seller.stripe_user_id,
                            application_fee_amount=int(selltix_fees)
                        )
                    else:
                        intent = None
                    
                    # On cree un payment 
                    # sauve le wallet utilisé pour la reservation 
                    wallet_pool_used = None
                    if ADRESS_TO_USE == settings.SELLTIX_WALLET:
                        wallet_pool_used = "wallet1"
                    elif  ADRESS_TO_USE == settings.SELLTIX_WALLET2:
                        wallet_pool_used = "wallet2"
                    elif ADRESS_TO_USE == settings.SELLTIX_WALLET3:
                        wallet_pool_used = "wallet3"
                    elif ADRESS_TO_USE == settings.SELLTIX_WALLET4:
                        wallet_pool_used = "wallet4"
                    elif ADRESS_TO_USE == settings.SELLTIX_WALLET5:
                        wallet_pool_used = "wallet5"
                        
                    payment = Payment()
                    payment.status=0
                    payment.refUser_id = toUser
                    payment.toUser = theOrganizer
                    payment.refTicketType_id = refTicketType
                    payment.refContent = None
                    payment.jsonStripe = intent 
                    payment.paymentIntent = intent["id"]
                    payment.amount = float(totalAmount)/100
                    payment.feesSelltix = float(selltix_fees)/100
                    #get current PolPrice
                    polPrice = POLPrice.objects.get(id=1)
                    payment.polPrice = polPrice.price
                    payment.nbTickets = nbTickets
                    payment.withWallet = wallet_pool_used
                    payment.save()
                   
                    newdict = {'status': 'OK',"intent": intent,"refPayment":payment.id,"ephemeralKey":ephemeralKey,"customer":theUser.stripeCustomerId}
                    logger.info(newdict)
                    
                    return JsonResponse(newdict, safe=False)
                except Exception as e:
                    logger.error(e)
                    newdict = {'status': 'KO',"cause":"Problème avec Stripe"}
                    return JsonResponse(newdict, safe=False)
            else:
                newdict = {'status': 'KO',"cause":"transactionRejected"}
                return JsonResponse(newdict, safe=False)
         except Exception as e:
            logger.error(e) 
            #execution reverted: Amount too high: no reservation available at this moment
            if "execution reverted" in  str(e):
                causes = str(e) 
                newdict = {'status': 'KO',"cause":causes}
            else:
                newdict = {'status': 'KO',"cause":"exception"}
            return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)     

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def refundpaymentintent(request):
    if request.method == "POST":
        intent =  request.data["intent"]
        organizerStripeAccountId = request.data["organizerStripeAccountId"]
        stripe.api_key = settings.STRIPE_API_KEY
        try:
            if organizerStripeAccountId:            
                refund = stripe.Refund.create(payment_intent=intent, stripe_account=organizerStripeAccountId,)
            else:
                refund = stripe.Refund.create(payment_intent=intent)
            payment = Payment.objects.get(paymentIntent=intent)
            payment.status = 2
            payment.save()
            newdict = {'status': 'OK',"refund": refund}
            logger.info("===Refund ")
            logger.info(refund)
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO'}
       
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)     

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def getStreamingAuthToken(request):
    if request.method == "POST":
        import jwt
        import json
        import datetime
        try:
            expiration_in_seconds = 7200
            expiration = datetime.datetime.now() + datetime.timedelta(seconds=expiration_in_seconds)
            token = jwt.encode(payload={
                'exp': expiration,
                'apikey': settings.VIDEO_RTC_API_KEY,
                'permissions': ["allow_join"], # 'ask_join' || 'allow_mod' 
                'version': 2
                }, key=settings.VIDEO_RTC_SECRET_KEY, algorithm= 'HS256')
            logger.info("===Token ")
            logger.info(token)
            newdict = {'status': 'OK',"token": json.dumps(token.decode("utf-8"))}
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO',"error": str(e)}
        
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)  

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def requestDailyMeetingToken(request):
    if request.method == "POST":
        import json
        import datetime
        import time
        import requests
        from requests.exceptions import RequestException

        try:
            refEvent = request.data["refEvent"] #aka the room name
            userId = request.data["userId"]
            userName = request.data["userName"]
            is_owner = request.data["is_owner"]
            enable_screenshare = request.data["enable_screenshare"]
            start_video_off = request.data["start_video_off"]
            start_audio_off = request.data["start_audio_off"]
            permissions = request.data["permissions"]
            #TODO : increase security by verifying user has NFT tickets using alchemy SDK 
            #https://docs.alchemy.com/reference/getnftmetadata
            #https://docs.alchemy.com/reference/getnftsforowner
            
            # A POST request has three optional body parameters: name, privacy, and a properties object.
            # create a new room request
            url = "https://api.daily.co/v1/meeting-tokens/"
            headers = {
                'Content-Type': 'application/json',
                'Authorization': 'Bearer '+ settings.DAILY_API_KEY
            }
            body = {
                "properties":{
                    "room_name": str(refEvent)[:41],
                    "user_id":userId,
                    "user_name":userName,
                    "is_owner":is_owner,
                    "enable_screenshare":enable_screenshare,
                    "start_video_off":start_video_off,
                    "start_audio_off":start_audio_off,
                    "permissions":permissions
                }
               
            }
            logger.info("===Ask Token with body url %s"%url)
            logger.info(body)
            MAX_RETRIES = 3
            RETRY_DELAY = 3  # seconds
            for attempt in range(MAX_RETRIES):
                try:
                    response = requests.post(url, headers=headers, json=body)
                    if response.status_code == 200:
                        theEvent = Event.objects.get(id=refEvent)
                        newdict = {'status': 'OK', "token": json.loads(response.text),"url":theEvent.streamingId}
                        break
                    elif response.status_code == 429:
                        if attempt < MAX_RETRIES - 1:
                            time.sleep(RETRY_DELAY)
                            continue
                        else:
                            newdict = {'status': 'ERROR', "message": "Rate limit exceeded after multiple attempts"}
                    else:
                        newdict = {'status': 'ERROR', "message": response.text}
                        break
                except RequestException as e:
                    newdict = {'status': 'ERROR', "message": str(e)}
                    break
            
            logger.info("===Daily meeting token ")
            logger.info(response.text)
            
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO',"error": str(e)}
        
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)  

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def createDailyRoom(request):
    if request.method == "POST":
        import json
        import datetime
        try:
            refEvent = request.data["refEvent"]
            theEvent = Event.objects.get(id=refEvent)
            if theEvent.refOrganiser == request.user:
                duration = theEvent.duration
                #Need to convert to timezone
                from backoffice.helpers import getEventDateToParisTimeZone
                theDate = getEventDateToParisTimeZone(theEvent)
                expirationDate = theDate+ datetime.timedelta(minutes=duration)+ datetime.timedelta(minutes=6) #6 minutes de rab

                # A POST request has three optional body parameters: name, privacy, and a properties object.
                # create a new room request
                url = "https://api.daily.co/v1/rooms/"
                headers = {
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer '+ settings.DAILY_API_KEY
                }
                body = {
                    # Max 41 caracters
                    "name": str(theEvent.id)[:41],
                    "privacy": "private", #need a meeting token
                    "properties": {
                        #expires after event end 
                        "exp": int(expirationDate.timestamp()),
                        "eject_at_room_exp":True,
                        "enable_people_ui":False,
                        "enable_pip_ui":False,
                        "enable_prejoin_ui":False,
                        "enable_noise_cancellation_ui":True,
                        "enable_screenshare":True,
                        "enable_video_processing_ui":True,
                        "enable_chat":True,
                        "owner_only_broadcast":True,
                        "enable_recording":False,
                        "enable_advanced_chat":True,
                        "enable_hidden_participants":True,
                        "enable_mesh_sfu":True,
                        "experimental_optimize_large_calls":True,
                        "enable_terse_logging":True,
                        "sfu_switchover":3,
                        "enable_adaptive_simulcast":True,
                        "enforce_unique_user_ids":True,
                        "lang":"fr",
                    }
                }
                logger.info("===Ask url with")
                logger.info(body)
                response = requests.post(url, headers=headers, json=body)
                logger.info("===Daily Room ")
                logger.info(response.text)
                if response.status_code==200:
                    #save daily url
                    theEvent.streamingId = json.loads(response.text)["url"]
                    theEvent.save()
                    newdict = {'status': 'OK',"room": json.loads(response.text)}
                else:
                    newdict = {'status': 'KO',"error": response.text}
            else:
                 return HttpResponse(status=401)
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO',"error": str(e)}
        
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)  
    
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def createpaymentintent(request):
    """
        Utiliser pour le paiement des billets offerts.
    """
    if request.method == "POST":
        amount =  request.data["amount"]
        refUser = request.data["refUser"]
        stripe.api_key = settings.STRIPE_API_KEY
        
        try:
            intent = stripe.PaymentIntent.create(
                amount=int(amount),
                currency='eur',
                automatic_payment_methods={"enabled": True},
            )
            
             # On cree un payment 
            payment = Payment()
            payment.status=0
            payment.refUser_id = refUser
            payment.jsonStripe = intent 
            payment.paymentIntent = intent["id"]
            payment.amount = int(amount)/100
            payment.nbTickets = 1
            payment.save()
            newdict = {'status': 'OK', "intent": intent,"refPayment":payment.id}
            logger.info(newdict)
            return JsonResponse(newdict, safe=False)
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO'}
            return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)
    

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def createpaymentintentGasFees(request):
    """
        Utiliser pour le paiement pack gas fees.
    """
    if request.method == "POST":
        amount =  request.data["amount"]
        refUser = request.data["refUser"]
        stripe.api_key = settings.STRIPE_API_KEY
        
        try:
            theOrganizer = User.objects.get(id=refUser)
            intent = stripe.PaymentIntent.create(
                amount=int(amount),
                currency='eur',
                automatic_payment_methods={"enabled": True},
            )
            logger.info(intent)
             # On cree un payment 
            payment = Payment()
            payment.status=0
            payment.refUser = theOrganizer
            payment.typePayment = 2
            payment.jsonStripe = intent 
            payment.paymentIntent = intent["id"]
            payment.amount = int(amount)/100
            payment.nbTickets = 0
            payment.isContent = False
            payment.isPackGasFees = True
            payment.withWallet = theOrganizer.walletAddress
            payment.save()
            newdict = {'status': 'OK', "intent": intent,"refPayment":payment.id}
            logger.info(newdict)
            return JsonResponse(newdict, safe=False)
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO'}
            return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501) 


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def createpaymentintentReventeTicket(request):
    """
        Utiliser pour le paiement pack gas fees.
    """
    if request.method == "POST":
        amount =  request.data["amount"]
        refTicket = request.data["refTicket"]
        stripe.api_key = settings.STRIPE_API_KEY
        
        try:
            from django.db import transaction
            with transaction.atomic():
                ticket = Ticket.objects.select_for_update().get(id=refTicket)
                if ticket.sellInProgress==True:
                    newdict = {'status': 'KO', "cause":"Too late"}
                    return JsonResponse(newdict, safe=False)
                else:
                    ticket.sellInProgress = True
                    ticket.dateSellInProgress = datetime.datetime.now()
                    # On cree un payment sans toUser (argent recu global)
                    refUser = request.user.id
                    theUser = User.objects.get(id=refUser)
                    payment = Payment()
                    payment.status=0
                    payment.refUser = theUser
                    payment.refTicketType = ticket.refTicketType
                    payment.typePayment = 3
                    payment.amount = int(amount)/100
                    payment.nbTickets = 1
                    payment.isContent = False
                    payment.isPackGasFees = False
                    payment.withWallet = None
                    payment.save()

                    intent = stripe.PaymentIntent.create(
                        amount=int(amount),
                        currency='eur',
                        transfer_group=str(payment.id),
                        automatic_payment_methods={"enabled": True},
                    )
                    logger.info(intent)
                    payment.jsonStripe = intent 
                    payment.paymentIntent = intent["id"]
                    payment.save()
                    newdict = {'status': 'OK', "intent": intent,"refPayment":payment.id}
                    logger.info(newdict)
                    ticket.save()
                    return JsonResponse(newdict, safe=False)
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO'}
            return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501) 
"""   
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def setupPaymentintent(request):
    if request.method == "POST":
        amount =  request.data["amount"]
        description =  request.data["description"] #Billet libelle
        stripe.api_key = settings.STRIPE_API_KEY
        selltix_fees = request.data["fees"]
        refOrganizer = request.data["refOrganizer"]
        refTicketType = request.data["refTicketType"]
        toUser = request.data["toUser"]
        nbTickets = request.data["nbTickets"]
        #check si stripe customer exists
        try:
           
            theOrganizer = User.objects.get(id=refOrganizer)
            refUser = request.user.id
            theUser = User.objects.get(id=refUser)
            if not theUser.stripeCustomerId:
                customer = stripe.Customer.create(email=theUser.email)
                logger.info("on creer customer %s" % customer)
                theUser.stripeCustomerId = customer["id"]
                theUser.save()
            logger.info("on va creer payment intent pour un montant de %d" %int(amount))
            logger.info("et stripe customer %s" %theUser.stripeCustomerId)
            
            ephemeralKey = stripe.EphemeralKey.create(
                customer=theUser.stripeCustomerId,
                stripe_version='2020-08-27',
            )
            #get currency from the event
            ticketType = TicketType.objects.get(id=refTicketType)

            intent = stripe.PaymentIntent.create(
                amount=int(amount),
                currency=ticketType.refEvent.currency,
                automatic_payment_methods={"enabled": True},
                setup_future_usage='on_session',
                description=description,
                stripe_account=theOrganizer.seller.stripe_user_id,
                application_fee_amount=int(selltix_fees)
            )
            
             # On cree un payment 
            payment = Payment()
            payment.status=0
            payment.refUser_id = toUser
            payment.refTicketType_id = refTicketType
            payment.jsonStripe = intent 
            payment.paymentIntent = intent["id"]
            payment.amount = int(amount)/100
            payment.nbTickets = nbTickets
            payment.save()
            newdict = {'status': 'OK', "intent": intent,"refPayment":payment.id,"ephemeralKey":ephemeralKey,"customer":theUser.stripeCustomerId}
            logger.info(newdict)
            return JsonResponse(newdict, safe=False)
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO'}
            return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)
"""

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def askMintTicket(request):
    if request.method == "POST":
        reservationId = request.data["reservationId"]
        toUser = request.data["toUser"]
        refTicketType = request.data["refTicketType"]
        refPayment = request.data["refPayment"]
        nbTickets = request.data["nbTickets"] 
        ticketType = TicketType.objects.get(id=refTicketType)
        # On update payment 
        if refPayment is None:
            # Dans quel cas cela peut arriver...  ?
            payment = Payment()
            payment.refUser_id = toUser
            payment.refTicketType = refTicketType
            payment.toUser = ticketType.refEvent.refOrganiser
            payment.status=1
            payment.amount=0
            payment.nbTickets = nbTickets
            payment.withWallet = "wallet1"
            payment.refTicketType_id = refTicketType
            payment.save()
        else:
            payment = Payment.objects.get(id=refPayment)
            payment.refUser_id = toUser
            # vérifie si a vraiment payer
            #le seller est l'organisateur 
            # Vérifier que le paiement date de moins de 10 minutes
            createdAt = payment.createdAt
            now = datetime.datetime.now(timezone.utc)
            delta = now - createdAt
            if delta.total_seconds() > 600:
                newdict = {'status': 'KO','message':'Payment too old'}
                return JsonResponse(newdict, safe=False) 
            
            accountId = ticketType.refEvent.refOrganiser.seller.stripe_user_id
            #verifie paiement que si total>0
            if payment.amount>0:
                payment_intent = stripe.PaymentIntent.retrieve(payment.paymentIntent,stripe_account=accountId)
                if payment_intent.status != 'succeeded':
                    newdict = {'status': 'KO','message':'Payment not done'}
                    return JsonResponse(newdict, safe=False) 
            
            payment.status=1
            payment.save()
      
        # On cree une ligne TicketAMiner
        ticketToMint = TicketToMint()
        ticketToMint.refPayment_id = refPayment
        ticketToMint.status=0
        ticketToMint.reservationId = reservationId
        ticketToMint.refUser_id = toUser
        ticketToMint.refTicketType_id = refTicketType
        ticketToMint.withWallet = payment.withWallet
        ticketToMint.save()
        newdict = {'status': 'OK'}
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)    



def doQuery(query):
    logger.info("==DOQUERY %s"%query)
    with connection.cursor() as cursor:
        cursor.execute(query)
        columns = [col[0] for col in cursor.description]
        return [
            dict(zip(columns, row))
            for row in cursor.fetchall()
        ]

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def getWebRtcApiKey(request):
    if request.method == "POST":
        newdict = {'status': 'OK', "apikey":settings.WEBRTC_API_KEY}
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)
    
    
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def getDashboard(request):
    if request.method == "POST":
        refOrga = request.data["refOrga"]
        refEvent = request.data["refEvent"]
        totalPayment = doQuery("SELECT sum(\"amount\") as amount,sum(\"feesSeltix\") as feesseltix,sum(\"nbTickets\") as nbtickets FROM \"backoffice_payment\" as bpy "
                               "left join \"backoffice_tickettype\" as btt on btt.id= bpy.\"refTicketType_id\" " 
                               "left join \"backoffice_event\" as be on be.id= btt.\"refEvent_id\" "
                               "where be.\"refOrganiser_id\"='%s' and be.\"id\"='%s' and bpy.\"toUser\"='%s' and bpy.\"status\"='1'"%(refOrga,refEvent,refOrga))
        totalRevenu=0
        totalVente=0
        totalfeesSeltix= 0 
        for payment in totalPayment:
             try:
                totalRevenu+=payment["amount"]
                totalVente+=payment["nbtickets"]
                totalfeesSeltix+=payment["feesseltix"]
             except Exception as e:
                logger.error(e)
                totalRevenu=0
                totalVente=0
                totalfeesSeltix=0
        #nb max tickets
        qmax = doQuery("select sum(\"maxTickets\") as max from \"backoffice_tickettype\" as btt left join \"backoffice_event\" as be on be.id= btt.\"refEvent_id\""
                        "where be.\"refOrganiser_id\"='%s'"%refOrga)
        nbMaxTickets=0
        for max in qmax:
            try:
                nbMaxTickets+=max["max"]
            except Exception as e:
                nbMaxTickets=0
        newdict = {'status': 'OK','totalRevenu':totalRevenu,"totalfeesSeltix":totalfeesSeltix,"totalVente":totalVente,"maxTickets":nbMaxTickets}
        # use Django-q to do asynchronous job
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)    

class ScanUserLogin(generics.ListAPIView):
   def post(self, request, format=None):
        if request.method == "POST":
            data = request.data
            username = data['login']
            password = data["password"]
            try:
                
                scan = ScanUsers.objects.get(login=username,password=password)
                
                from rest_framework_simplejwt.tokens import RefreshToken
                refresh = RefreshToken.for_user(scan.user)
                jsonToReturn = {
                    "status": "OK",
                    "refUser":UserSerializer(scan.user,many=False).data,
                    'refresh': str(refresh),
                    'access': str(refresh.access_token),
                }
                return JsonResponse(jsonToReturn, safe=False)
            except Exception as e:
                jsonToReturn = {
                    "status": "KO"
                }
                return JsonResponse(jsonToReturn, safe=False)
        else:
            return HttpResponse(status=403)

class InvitationAPICreateView(generics.CreateAPIView):
    permission_classes = [permissions.IsAuthenticated]
    queryset = Invitation.objects.all().select_related("refTicket").select_related("fromUser") 
    serializer_class = InvitationSerializer

class InvitationAPIDetailView(generics.RetrieveAPIView):
    #permission_classes = [permissions.IsAuthenticated]
    queryset = Invitation.objects.all().select_related("refTicket").select_related("fromUser") 
    serializer_class = InvitationSerializer



@api_view(['POST'])
@permission_classes([IsAuthenticated])
def getUploadVideoUrl(request):
    if request.method == "POST":
        logger.info("==UPLOADVIDEO")
        refEvent =  request.data["refEvent"]
        filename =  request.data["filename"]
        session = boto3.session.Session()
        client = session.client(
            "s3",
            region_name="eu-west-3",
            endpoint_url=settings.AWS_S3_ENDPOINT_URL,
            aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
            aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
        )
       #import string
       # key = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(10))
        
        url = client.generate_presigned_url(
            ClientMethod="put_object",
            Params={
                "Bucket": "selltix",
                "Key": f"media/videos/{filename}",
            },
            ExpiresIn=11000, #3 hours
        )
      
        logger.info("===Session url created ")
        logger.info(url)
        #video_file = request.FILES.get("video_file")
        #title = request.data["title"]
        try:
            event = Event.objects.get(id=refEvent)
            #file_path = default_storage.save(f"videos/{video_file.name}", ContentFile(video_file.read()))
            #event.recordedFile =  file_path
            file_path = f"videos/{filename}"
            logger.info("==Enreigstre ",file_path) 
            event.recordedFile = file_path
            event.save()
            newdict = {'status': 'OK', 'urlToUpload':url}
           
        except Exception as e:
            logger.error(e)
            print(e)
            newdict = {'status': 'KO','cause':str(e)}
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)   

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def getUploadContentUrl(request):
    if request.method == "POST":
        logger.info("==UPLOADContent")
        logger.info(request.data)
        refContent =  request.data["refContent"]
        filename =  request.data["filename"]
        logger.info(refContent)
        logger.info(filename)

        session = boto3.session.Session()
        client = session.client(
            "s3",
            region_name="eu-west-3",
            endpoint_url=settings.AWS_S3_ENDPOINT_URL,
            aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
            aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
        )
       #import string
       # key = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(10))
        
        url = client.generate_presigned_url(
            ClientMethod="put_object",
            Params={
                "Bucket": "selltix",
                "Key": f"media/content/{filename}",
            },
            ExpiresIn=11000, #2 hours
        )
      
        logger.info("===Session url created ")
        logger.info(url)
        #video_file = request.FILES.get("video_file")
        #title = request.data["title"]
        try:
            content = Content.objects.get(id=refContent)
            #file_path = default_storage.save(f"videos/{video_file.name}", ContentFile(video_file.read()))
            #event.recordedFile =  file_path
            file_path = f"content/{filename}"
            logger.info("==Enreigstre ",file_path) 
            content.contentFile = file_path
            content.save()
            newdict = {'status': 'OK', 'urlToUpload':url}
           
        except Exception as e:
            logger.error(e)
            print(e)
            newdict = {'status': 'KO','cause':str(e)}
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)   

@swagger_auto_schema(method='post', request_body=openapi.Schema(
    type=openapi.TYPE_OBJECT, 
    properties={
        'refEvent': openapi.Schema(type=openapi.TYPE_STRING, description='string'),
        'title': openapi.Schema(type=openapi.TYPE_STRING, description='string'),
    }
))
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def sendToBitmovin(request):
    if request.method == "POST":
        refEvent =  request.data["refEvent"]
        title = request.data["title"]
        try:
            event = Event.objects.get(id=refEvent)
            #Send to Bitmovin 
            url = "https://api.bitmovin.com/v1/streams/video"
            payload = { 
                "encodingProfile": "FIXED_RESOLUTIONS", #     PER_TITLE
                "assetUrl":  str(event.recordedFile.url),
                "title":title,
                 "domainRestrictionId": "cp0hopcd1q7m56d4cj1g" }
            headers = {
                "accept": "application/json",
                "content-type": "application/json",
                "X-Api-Key": settings.BITMOVING_AKI_KEY
            }
            logger.info("===== Envoi a Bitmovin")
            logger.info(payload)
            response = requests.post(url, json=payload, headers=headers)
            logger.info("===== reponse Bitmovin")
            logger.info(response.text)
            if response.text:
                responseJson = json.loads(response.text)
                if responseJson["status"]=="SUCCESS":
                    data = responseJson["data"]
                    resultId = data["result"]["id"]
                    event.recordedStreamingUrl = resultId
                    event.save()
                    newdict = {'status': 'OK','recordedStreamingUrl':resultId}
                else:
                    newdict = {'status': 'KO','cause':responseJson}
            
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO','cause':str(e)}
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)   

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def deleteBitmovinStream(request):
    if request.method == "POST":
        refStream =  request.data["refStream"]
        refEvent =  request.data["refEvent"]
        try:
            #delete event file
            theEvent = Event.objects.get(id=refEvent)
            theEvent.recordedFile.delete()
            theEvent.recordedStreamingUrl = ""
            theEvent.save()
            #Send to Bitmovin
            url = "https://api.bitmovin.com/v1/streams/video/"+refStream
            headers = {
                "accept": "application/json",
                "content-type": "application/json",
                "X-Api-Key": settings.BITMOVING_AKI_KEY
            }
            logger.info("===== Envoi a Bitmovin")
             
            response = requests.delete(url,  headers=headers)
            if response.text:
                responseJson = json.loads(response.text)
                if responseJson["status"]=="SUCCESS":
                    newdict = {'status': 'OK' }
                else:
                    newdict = {'status': 'KO','cause':responseJson}
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO','cause':str(e)}
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)
 

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def uniqueId(request):
    if request.method == "POST":
        logger.info("==uniqueId")
        uniqueId = request.data["uniqueId"]
        protection = ContentProtection()
        protection.uniqueId = uniqueId
        protection.save()
        newdict = {'status': 'OK' }
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def confirmPayment(request):
    if request.method == "POST":
        refPayment = request.data["refPayment"]
        try:
            payment = Payment.objects.get(id=refPayment)
            #vérifier le paiement statut stripe
            payment_intent = stripe.PaymentIntent.retrieve(payment.paymentIntent)
            if payment_intent.status != 'succeeded':
                newdict = {'status': 'KO','message':'Payment not done'}
                return JsonResponse(newdict, safe=False) 
            payment.status = 1
            payment.save()
            #Note : c'est l'organisateur qui vend le contenu et donc qui doit fournir une facture à l'utilisateur 
            newdict = {'status': 'OK' }
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO','message':str(e)}
        
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)

@api_view(['GET'])
def getPolPrice(request):
    try:
        polPrice = POLPrice.objects.get(id=1)
        newdict = {'status': 'OK','price':polPrice.price }
    except Exception as e:
        logger.error(e)
        newdict = {'status': 'KO','cause':str(e)}

    return JsonResponse(newdict, safe=False)
    
@api_view(['GET'])
def stream_video(request,video_id,accessToken):
    # Perform authentication and authorization checks here
    try:
        protection = ContentProtection.objects.get(uniqueId=accessToken)
        if protection.used:
            logger.info("===Protection used %d nb sgements %d"%(protection.nbUsed,protection.num_segments))
            if protection.nbUsed  > 20:
                logger.info("===Protection used %d nb sgements %d"%(protection.nbUsed,protection.num_segments))
                return HttpResponse(status=401)
            
            #check timestamp 
            createdAt = protection.createdAt
            from datetime import datetime
            now = datetime.datetime.now()
            diff = now - createdAt
            if diff.total_seconds() > 11000:
                logger.info("===Protection expired")
                return HttpResponse(status=401)
        
        content = Content.objects.get(id=video_id)
        # Retrieve the video from S3
        session = boto3.session.Session()
        client = session.client(
                "s3",
                region_name="eu-west-3",
                endpoint_url=settings.AWS_S3_ENDPOINT_URL,
                aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
                aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
        )
        filename = str(content.contentFile)
        myKey = "media/"+filename
        logger.info(myKey)
         # Generate a pre-signed URL for the S3 object
        url = client.generate_presigned_url('get_object',
                                            Params={'Bucket': 'selltix',
                                                    'Key': myKey},
                                            ExpiresIn=11000)
        
        r = requests.get(url, stream=True)
        file_size = int(r.headers.get('Content-Length', 0))
        chunk_size = 8192  # or whatever chunk size you prefer
        # a revoir
        num_segments = (file_size + chunk_size - 1)  
        response = StreamingHttpResponse(streaming_content=r.iter_content(chunk_size=8192))
        response['Content-Type'] = content.fileContentType
        response['Content-Disposition'] = f'inline; filename="{filename}"'
        
        protection.used = True
        protection.num_segments = num_segments
        protection.nbUsed+=1
        protection.save()
        return response
    except Exception as e:
        logger.error(e)
        newdict = {'status': 'KO','cause':str(e)}
        return JsonResponse(newdict, safe=False)
 
    
@api_view(['GET']) 
def contentFile(request,content_id,accessToken,type):
    # Perform authentication and authorization checks here
    try:
        protection = ContentProtection.objects.get(uniqueId=accessToken)
         
        if protection.used:
            logger.info("===Protection used %d  "%(protection.nbUsed))
            if protection.nbUsed  > 1:
                logger.info("===Protection used %d"%(protection.nbUsed ))
                return HttpResponse(status=401)
            
            #check timestamp 
            createdAt = protection.createdAt
            from datetime import datetime
            now = datetime.datetime.now()
            diff = now - createdAt
            if diff.total_seconds() > 120:
                logger.info("===Protection expired")
                return HttpResponse(status=401)
        
        content = Content.objects.get(id=content_id)
         # Retrieve the video from S3
        session = boto3.session.Session()
        client = session.client(
                "s3",
                region_name="eu-west-3",
                endpoint_url=settings.AWS_S3_ENDPOINT_URL,
                aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
                aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
        )
        filename = str(content.contentFile)
        myKey = "media/"+filename
        logger.info(myKey)
         # Generate a pre-signed URL for the S3 object
        url = client.generate_presigned_url('get_object',
                                            Params={'Bucket': 'selltix',
                                                    'Key': myKey},
                                            ExpiresIn=120)
        
        r = requests.get(url)
        #return pdf binary file
         
        
        logger.info("===Renvoi content type ")
        logger.info(content.fileContentType)
        response = HttpResponse(r.content, content_type=content.fileContentType)
        response['Content-Disposition'] = f'inline; filename="{filename}"'
        response['Content-Length'] = len(r.content)
        protection.used = True
        protection.num_segments = 1
        protection.nbUsed+=1
        protection.save()
        return response
    except Exception as e:
        logger.error(e)
        newdict = {'status': 'KO','cause':str(e)}
        return JsonResponse(newdict, safe=False)     
    
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def  sendInvitation(request):
    if request.method == "POST":
        email =  request.data["email"]
        refTicket = request.data["refTicket"]
        refPayment = request.data["refPayment"]
        try:
            logger.info("========send invitation %s"%(email))
            logger.info("====refTicket %s"%(refTicket))
            logger.info("====refPayment %s"%(refPayment))
            payment = Payment.objects.get(id=refPayment)
            # vérifier le statut du paiement
            createdAt = payment.createdAt
            now = datetime.datetime.now(timezone.utc)
            delta = now - createdAt
            if delta.total_seconds() > 600:
                newdict = {'status': 'KO','message':'Payment too old'}
                return JsonResponse(newdict, safe=False) 
            ticket = Ticket.objects.get(id=refTicket)   
            #si le billet n'est pas géré par SellTix cela signifie qu'il nous a été transféré
            if ticket.refUser.generatedWallet == False:
                ticket.transferedToSellTix = True
                ticket.save()
            accountId = ticket.refTicketType.refEvent.refOrganiser.seller.stripe_user_id
            payment_intent = stripe.PaymentIntent.retrieve(payment.paymentIntent,stripe_account=accountId)
            if payment_intent.status != 'succeeded':
                newdict = {'status': 'KO','message':'Payment not done'}
                return JsonResponse(newdict, safe=False) 
            payment.status=1
            payment.nbTickets = 1              
            payment.save()
            invitation = Invitation()
            invitation.fromUser = request.user
            invitation.toUser = email
            invitation.code = ''
            invitation.emailSent = False 
            invitation.refPayment = payment
            invitation.refTicket = ticket
            invitation.save()
           
           
            emailInvit = request.user.email
            urlInvitation = "https://www.selltix.live/billetoffert/"+str(invitation.id)+"/"
            if ticket.typeTicket==0:
                eventName=ticket.refTicketType.refEvent.name
                sg = sendgrid.SendGridAPIClient(settings.SENDGRID_API_KEY)
                data = {
                            "personalizations": [
                                {
                                    "to": [
                                        {
                                            "email": email
                                        }                                    
                                    ],
                                    "bcc": [
                                        {
                                            "email": "csurbier@selltix.fr"
                                        },
                                        {
                                            "email": "rfarache@mac.com"
                                        }
                                    ],
                                    "subject": "SellTix - Un billet vous a été offert",
                                    "dynamic_template_data": {
                                        "eventname":eventName,
                                        "email":emailInvit,
                                        "urlInvitation":urlInvitation
                                    }
                                }
                            ],
                            "from": {
                                "email": "SellTix <contact@selltix.live>"
                            },
                        
                            "template_id": settings.TEMPLATE_ID_OFFRIR
                        }
            else:
                contentName = ticket.refContent.name
                sg = sendgrid.SendGridAPIClient(settings.SENDGRID_API_KEY)
                data = {
                            "personalizations": [
                                {
                                    "to": [
                                        {
                                            "email": email
                                        }
                                    
                                    ],
                                     "bcc": [
                                        {
                                            "email": "csurbier@selltix.fr"
                                        },
                                        {
                                            "email": "rfarache@mac.com"
                                        }
                                    ],
                                    "subject": "SellTix - Un contenu vous a été offert",
                                    "dynamic_template_data": {
                                        "contentname":contentName,
                                        "email":emailInvit,
                                        "urlInvitation":urlInvitation
                                    }
                                }
                            ],
                            "from": {
                                "email": "SellTix <contact@selltix.live>"
                            },
                        
                            "template_id": settings.TEMPLATE_ID_OFFRIR_CONTENU
                        }
            try:
                response = sg.client.mail.send.post(request_body=data)
                if response.status_code == 202:
                    ticket.hasBeenOffered=True
                    ticket.save()
                    invitation.emailSent = True
                    invitation.save()
                    newdict = {'status': 'OK'}
                else:
                    newdict = {'status': 'KO','cause': "Sent email failed"}
            except Exception as e:
                logger.error(e)
                newdict = {'status': 'KO','cause': "Sent email failed"}
            return JsonResponse(newdict, safe=False)
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO','cause': "Sent email failed"}
            return JsonResponse(newdict, safe=False)
 
@api_view(['POST']) 
@permission_classes([IsAuthenticated])
def invitationTransfert(request):
    if request.method == "POST":
        refInvitation =  request.data["refInvitation"]
        #codePin = request.data["codePin"]
        walletAddress = request.data["walletAddress"]
        metadata = request.data["metadata"]
        try:
            
            invitation = Invitation.objects.get(id=refInvitation)
            if invitation.burned:
                newdict = {'status': 'KO','cause': "Invitation already burned"}
                return JsonResponse(newdict, safe=False)
            
            user = User.objects.get(email=invitation.toUser)
            if user.walletAddress:
                pass
            else:
                # On assigne le wallet connecté recu
                user.walletAddress = walletAddress
                user.save()
             
            # create Tasks
            task = Tasks()
            task.status = 0 #TO_DO
            task.action = "offrirTickets"
            task.metadata = metadata
            task.save()
            #create JWT TOKEN
            from rest_framework_simplejwt.tokens import RefreshToken
            refresh = RefreshToken.for_user(user)
            newdict = {
                    "status": "OK",
                    "refUser":UserSerializer(user,many=False).data,
                    'refresh': str(refresh),
                    'access': str(refresh.access_token),
            }
            return JsonResponse(newdict, safe=False)
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO','cause': str(e)}
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)     

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def uploadLogo(request):
    if request.method == "POST":
        try:
            user = request.user
            # refUser and an image file is sent in the request
            # save the image in the user object
            # and return the image url
            refUser = request.POST.get("refUser")
            image = request.FILES.get("image")
            if str(user.id) != str(refUser):
                logger.info("===On compare")
                logger.info(user.id)
                logger.info("avec ")
                logger.info(refUser)
                newdict = {'status': 'KO','cause': "You are not allowed to upload this image"}
                return JsonResponse(newdict, safe=False)
            user = User.objects.get(id=refUser)
            #remove existing logo
            if user.organizerImage:
                user.organizerImage.delete()
            user.organizerImage = image
            user.save()
            newdict = {'status': 'OK','image': user.organizerImage.url}
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO','cause': str(e)}
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)
    
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def getInfoSales(request):
    if request.method == "POST":
        try:
            user = request.user
            # refUser and an image file is sent in the request
            # save the image in the user object
            # and return the image url
            refUser = request.data["refUser"] 
            if str(user.id) != str(refUser):
                logger.info("===On compare")
                logger.info(user.id)
                logger.info("avec ")
                logger.info(refUser)
                newdict = {'status': 'KO','cause': "You are not allowed to request this sale info"}
                return JsonResponse(newdict, safe=False)
            nbEvents = Event.objects.filter(refOrganiser_id=refUser).count()
            nbTickets = Ticket.objects.filter(refTicketType__refEvent__refOrganiser_id=refUser).count()
            revenusDico = Payment.objects.filter(status=1,refTicketType__refEvent__refOrganiser_id=refUser).aggregate(Sum('amount'),Sum('feesSelltix'),Sum('feesStripeForSellTix'))
            # return json object
            params = {
                    "nbEvents":nbEvents,
                    "nbTickets":nbTickets,
                    "revenusDico":revenusDico
            } 
            newdict = {'status': 'OK','params': params}
        except Exception as e:
            logger.error(e)
            newdict = {'status': 'KO','cause': str(e)}
        return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)


@api_view(['POST']) 
def getTicketForSale(request):
    if request.method == "POST":
        try:
            refEvent = request.data["refEvent"]
            tickets = Ticket.objects.filter(refTicketType__refEvent_id=refEvent,sellable=True,sellInProgress=False).select_related('refTicketType').select_related('refUser')
            serialized_tickets = TicketSerializer(tickets,many=True).data
            return JsonResponse({'status': 'OK','list': serialized_tickets}, safe=False)
        except Exception as e:  
            logger.error(e)
            newdict = {'status': 'KO','cause': str(e)}
            return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def stripe_account_session(request):
    if request.method == "POST":
        try:
            CONNECTED_ACCOUNT_ID = request.data["account_id"] 
            stripe.api_key = settings.STRIPE_API_KEY
            account_session = stripe.AccountSession.create(
            account=CONNECTED_ACCOUNT_ID,
            components={
                "payments": {
                    "enabled": True,
                    "features": {
                        "refund_management": True,
                        "dispute_management": True,
                        "capture_payments": True
                    }
                },
                "balances": {
                    "enabled": True,
                    "features": {
                        "instant_payouts": True,
                        "standard_payouts": True,
                        "edit_payout_schedule": True,
                    },
                    },
            },
            )

            return JsonResponse({'status': 'OK','client_secret': account_session.client_secret}, safe=False)
        except Exception as e:  
            logger.error(e)
            newdict = {'status': 'KO','cause': str(e)}
            return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)
    

@csrf_exempt
def convert_mjml(request):
    data = json.loads(request.body)
    mjml = data.get('mjml')
    html = mjml_to_html(mjml)
    return JsonResponse({'html': html})



@api_view(['POST'])
@permission_classes([IsAuthenticated])
def confirmSale(request):
    if request.method == "POST":
        refTicket = request.data["refTicket"]
        refPayment = request.data["refPayment"]
        # On update payment 
        payment = Payment.objects.get(id=refPayment)
        # vérifie si a vraiment payer
        ticket = Ticket.objects.get(id=refTicket)
        fromUser = ticket.refUser
        toUser = User.objects.get(id=request.user.id)
        #payment_intent = stripe.PaymentIntent.retrieve(payment.paymentIntent)
        try:
            payment_intent = stripe.PaymentIntent.retrieve(
                payment.paymentIntent,expand=['latest_charge.balance_transaction'],
            )
            if payment_intent.status != 'succeeded':
                newdict = {'status': 'KO','message':'Payment not done'}
                return JsonResponse(newdict, safe=False) 
            if payment_intent:
                fee_details = payment_intent.latest_charge.balance_transaction.fee_details
                logger.info("PAyment intent : ")
                logger.info(payment_intent)
                logger.info("Fee details : ")
                logger.info(fee_details)
                stripeFees = (fee_details[0].amount)/100
            else:
                stripeFees = 0.35
        except Exception as e:
            logger.info(e)
            stripeFees = 0.35
        
        payment.feesStripeForSellTix = stripeFees    
        payment.status=1
        payment.save()
       
        # Il faut transférer le ticket au user qui a payé
        PRIVATE_KEY_TO_USE = None
        ADRESS_TO_USE = None
        from django.db import transaction
        with transaction.atomic():
            try:
                # On transfere l'argent avant le billet 
                #Send money to seller, organizer 
                priceWithoutStripeFees = ticket.priceForSell - stripeFees   
                pourcentageVoulu = ticket.refTicketType.royaltySellable/100
                amountForOrganisateur = (priceWithoutStripeFees * pourcentageVoulu)/100
                totalUser = priceWithoutStripeFees - amountForOrganisateur
                #Transfert to seller 
                stripe.api_key = settings.STRIPE_API_KEY
                stripe.Transfer.create(
                        amount=int(totalUser*100),
                        currency="eur",
                        destination=fromUser.seller.stripe_user_id,
                        transfer_group=str(refPayment),
                )
                # Enleve les frais Selltix
                feesSellTix = (amountForOrganisateur*10)/100
                amountForOrganisateur = amountForOrganisateur - feesSellTix
                # Il faut créer les X lignes de paiement 
                payment.feesSelltix = feesSellTix  
                payment.save()
                # créer 1 ligne payment pour le vendeur et 1 ligne pour l'organisateur
                paymentVendeur = Payment()
                paymentVendeur.refUser = payment.refUser
                paymentVendeur.toUser = ticket.refUser
                paymentVendeur.typePayment = payment.typePayment
                paymentVendeur.amount = totalUser
                paymentVendeur.refTicketType = payment.refTicketType
                paymentVendeur.nbTickets = 1
                paymentVendeur.status =  1 
                paymentVendeur.save()

                #Transfert to organisateur
                stripe.Transfer.create(
                        amount=int(amountForOrganisateur*100),
                        currency="eur",
                        destination=ticket.refTicketType.refEvent.refOrganiser.seller.stripe_user_id,
                        transfer_group=str(refPayment),
                )
                paymentOrga = Payment()
                paymentOrga.refUser = payment.refUser
                paymentOrga.toUser = ticket.refTicketType.refEvent.refOrganiser
                paymentOrga.typePayment = payment.typePayment
                paymentOrga.amount = amountForOrganisateur
                paymentOrga.refTicketType = payment.refTicketType
                paymentOrga.nbTickets = 1
                paymentOrga.status =  1 
                paymentOrga.save()
                if fromUser.generatedWallet:
                    ADRESS_TO_USE = fromUser.walletAddress
                    thePinCodeToDecrypt = decrypt_private_key_AES256(fromUser.pinCode,os.getenv("SECRET_PIN_KEY"))
                    realCodePin = 9999 - int(thePinCodeToDecrypt)
                    PRIVATE_KEY_TO_USE = decrypt_private_key_AES256(fromUser.privateKey,str(realCodePin))
                else:
                    #Ticket transféré à SellTix
                    PRIVATE_KEY_TO_USE = settings.SELLTIX_WALLET_PRIVATE_KEY4
                    ADRESS_TO_USE = settings.SELLTIX_WALLET4
                #Transfert 
                web3 = Web3(Web3.HTTPProvider(settings.CONTRACT_NODE_URL))
                DEBUG = os.getenv("DEBUG")
                CHAIN_ID = settings.CHAIN_ID
                if DEBUG:
                    web3.middleware_onion.inject(geth_poa_middleware, layer=0)
                checkedFromWalletAddress = web3.to_checksum_address(ADRESS_TO_USE) 
                checkedToWalletAddress = web3.to_checksum_address(toUser.walletAddress)
                #Vérifier qu'il reste du POL dans la balance du fromUser sinon le transfert va échouer
                if web3.eth.get_balance(checkedFromWalletAddress) < web3.to_wei(0.1, 'ether'):
                     # On envoi un peu de matic pour payer les gas fees
                    maticToSendAmount = "0.1"
                    from backoffice.helpers import sendMatic
                    envoiDone = sendMatic(fromUser.walletAddress,maticToSendAmount)
                    if envoiDone:
                        fromUser.maticSent = True
                        fromUser.save()
                    else:
                        message="Billet revente<b>%s</b> transfert échoué !"%(str(ticket.pk))
                        sendEmail("csurbier@idevotion.fr","[SellTix] Revente de ticket transfert échoué car pas assez de POL",message)
                        newdict = {'status': 'KO','message':'Not enough MATIC in the wallet'}
                        return JsonResponse(newdict, safe=False)
                # Create smart contract instance
                checkedTicketAddress = web3.to_checksum_address(ticket.refTicketType.refEvent.ticketContract)
                logger.info("===Contract Address : "+checkedTicketAddress)
                contract = web3.eth.contract(address=checkedTicketAddress, abi=TICKET_ABI)
                # : call 
                nonce = web3.eth.get_transaction_count(checkedFromWalletAddress,"latest")
                logger.info("===nonce : "+str(nonce))
                # call transfert METHOD 
                transaction = contract.functions.transferFrom(checkedFromWalletAddress,checkedToWalletAddress,int(ticket.ticketId)).build_transaction({
                        'from': checkedFromWalletAddress,
                        'nonce': nonce,
                        'gasPrice': web3.to_wei('40', 'gwei'),
                        'gas': '0'
                    }) 
                gas = web3.eth.estimate_gas(transaction)
                transaction.update({'gas': gas})
                    
                signed_txn = web3.eth.account.sign_transaction(transaction, private_key=PRIVATE_KEY_TO_USE)
                receipt = web3.eth.send_raw_transaction(signed_txn.rawTransaction)
                logger.info('preview trx hash (trx could be in pending status)', web3.to_hex(web3.keccak(signed_txn.rawTransaction)))
                # blocking: Wait for the transaction to be mined, and get the transaction receipt
                resultTx = web3.eth.wait_for_transaction_receipt(receipt)
                logger.info('receipt with confirmed trx hash (after trx executed)',resultTx )
                status = resultTx["status"]
                transactionLogger(nonce,resultTx,'transferFrom',status,checkedToWalletAddress)
                if status == 1:
                    ticket.sellable=False
                    ticket.sellInProgress=False
                    ticket.transferedToSellTix=False
                    ticket.refUser = toUser
                    ticket.save()
                
                    logger.info("===Ticket transféré")
                    # envoyer mail pour prévenir vendeur que ticket vendu
                    sg = sendgrid.SendGridAPIClient(settings.SENDGRID_API_KEY)
                    data = {
                            "personalizations": [
                                {
                                    "to": [
                                        {
                                            "email": fromUser.email
                                        }
                                    
                                    ],
                                     "bcc": [
                                        {
                                            "email": "csurbier@selltix.fr"
                                        },
                                        {
                                            "email": "rfarache@mac.com"
                                        }
                                    ],
                                    "subject": "SellTix - Vous avez vendu un billet",
                                    "dynamic_template_data": {
                                        "ticketname":ticket.refTicketType.name                                         
                                    }
                                }
                            ],
                            "from": {
                                "email": "SellTix <contact@selltix.live>"
                            },
                        
                            "template_id": settings.TEMPLATE_ID_TICKET_VENDU
                        }
                    try:
                        response = sg.client.mail.send.post(request_body=data)
                    except Exception as e:
                        logger.info(e)
                    newdict = {'status': 'OK'}
                    return JsonResponse(newdict, safe=False)
                else:
                    #Ticket selltix non transféré ! 
                    message="Billet revente<b>%s</b> transfert échoué !"%(str(ticket.pk))
                    sendEmail("csurbier@idevotion.fr","[SellTix] Revente de ticket transfert blockchain échoué",message)
                    logger.info("Error when transferting ticket")
                    newdict = {'status': 'KO','message':'Error when transferting ticket'}
                    return JsonResponse(newdict, safe=False)
            except Exception as e:
                logger.info(e)
                newdict = {'status': 'KO','message':str(e)}
                return JsonResponse(newdict, safe=False) 
         
        
    else:
        return HttpResponse(status=501)    
    
@api_view(['POST'])
@permission_classes([IsAuthenticated])
def getNbTicketPurchasedForUser(request):
    if request.method == 'POST':
        try:
            data = request.data
            refUser = data['refUser']
            refTicketType = data['refTicketType']
            nbTicketPurchased = Ticket.objects.filter(refUser=refUser,refTicketType=refTicketType).count()
            newdict = {'status': 'OK','nbTicketPurchased':nbTicketPurchased}
            return JsonResponse(newdict, safe=False)
        except Exception as e:
            logger.info(e)
            newdict = {'status': 'KO','message':str(e)}
            return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def getTicketTypeNbSold(request):
    if request.method == 'POST':
        try:
            data = request.data
            refTicketType = data['refTicketType']
            getTicketTypeNbSold = Ticket.objects.filter(refTicketType=refTicketType).count()
            newdict = {'status': 'OK','getTicketTypeNbSold':getTicketTypeNbSold}
            return JsonResponse(newdict, safe=False)
        except Exception as e:
            logger.info(e)
            newdict = {'status': 'KO','message':str(e)}
            return JsonResponse(newdict, safe=False)
    else:
        return HttpResponse(status=501)