# Helper file to geocode address using Google Geocoder
import logging
import os
from random import random
from sendgrid import SendGridAPIClient, Personalization, Email
from sendgrid.helpers.mail import Mail
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from tixsell.settings import SENDGRID_API_KEY
from backoffice.models import Invoice, User, Notification
from tixsell import settings
from tixsell.settings import wallet_pool
from django.utils import timezone
from datetime import timedelta
import pytz
import json
import uuid
import requests
from web3 import Web3
import sendgrid
from web3.middleware import geth_poa_middleware

from mnemonic import Mnemonic
from django.core import signing
# Get an instance of a logger
logger = logging.getLogger('django')
logger_transaction = logging.getLogger('transaction')

def get_titles(title):
    out = chunk_text(title_case_word(title), 19)
    logger.info(out)
    if out:
        if len(out) == 1:
            phrase1 = title_case_word(out[0])
            phrase2 = '___________________'
        elif len(out) == 2:
            phrase1 = title_case_word(out[0])
            if out[1] is None:
                phrase2 = '___________________'
            else:
                phrase2 = title_case_word(out[1])
        else:
            return ['___________________', '___________________']
        return [phrase1, phrase2]
    else:
        return None


def title_case_word(text):
    return text.title()


def chunk_text(text, chunk_size):
    return [text[i:i+chunk_size] for i in range(0, len(text), chunk_size)]


def notify_partner_webhook(user, event, type, message):
    webhook_url = user.webhookUrl
    theId = str(uuid.uuid4())
    if event:
        data = {
            "id": theId,
            "event": event.id,
            "type": type,
            "message": message
        }
    else:
        data = {
            "id": theId,
            "event": None,
            "type": type,
            "message": message
        }

    response = requests.post(webhook_url, data=json.dumps(data))
    notifs = Notification()
    notifs.refOrganizer = user
    notifs.refEvent = event
    notifs.notifId = theId
    notifs.type = type
    notifs.message = message
    notifs.success = response.status_code == 200
    if response.status_code != 200:
        logger.error(
            f"Error sending webhook notification {response.status_code}")
        from backoffice.helpers import sendEmail
        message = "Error sending notification pour organisateur : <b>%s</b> transfert échoué !" % (
            user.email)
        sendEmail("csurbier@idevotion.fr",
                  "[SellTix] Sending notifs échoué", message)
    notifs.save()
    return response.status_code == 200


def getWalletToUseOrWait(walletToUse):
    # Need to wait for wallet being unlocked and then locked it while processing
    import time
    needToWait = True
    while needToWait:
        wallet_from_pool = wallet_pool.get_wallet()
        if wallet_from_pool is None:
            time.sleep(0.5)
        else:
            if wallet_from_pool == walletToUse:
                needToWait = False
            else:
                time.sleep(0.5)


def getFreeWallet():
    # Need to wait for wallet being unlocked and then locked it while processing
    needToWait = True
    import time
    while needToWait:
        wallet_from_pool = wallet_pool.get_wallet()
        if wallet_from_pool is None:
            time.sleep(0.5)
        else:
            needToWait = False

    if wallet_from_pool == "wallet1":
        ADRESS_TO_USE = settings.SELLTIX_WALLET
        PRIVATE_KEY_TO_USE = settings.SELLTIX_WALLET_PRIVATE_KEY
    elif wallet_from_pool == "wallet2":
        ADRESS_TO_USE = settings.SELLTIX_WALLET2
        PRIVATE_KEY_TO_USE = settings.SELLTIX_WALLET_PRIVATE_KEY2
    elif wallet_from_pool == "wallet3":
        ADRESS_TO_USE = settings.SELLTIX_WALLET3
        PRIVATE_KEY_TO_USE = settings.SELLTIX_WALLET_PRIVATE_KEY3
    elif wallet_from_pool == "wallet4":
        ADRESS_TO_USE = settings.SELLTIX_WALLET4
        PRIVATE_KEY_TO_USE = settings.SELLTIX_WALLET_PRIVATE_KEY4
    elif wallet_from_pool == "wallet5":
        ADRESS_TO_USE = settings.SELLTIX_WALLET5
        PRIVATE_KEY_TO_USE = settings.SELLTIX_WALLET_PRIVATE_KEY5

    return ADRESS_TO_USE, PRIVATE_KEY_TO_USE

def releaseWallet(addressToRelease):
    if addressToRelease == settings.SELLTIX_WALLET:
        wallet_pool.release_wallet("wallet1")
    elif addressToRelease == settings.SELLTIX_WALLET2:
        wallet_pool.release_wallet("wallet2")
    elif addressToRelease == settings.SELLTIX_WALLET3:
        wallet_pool.release_wallet("wallet3")
    elif addressToRelease == settings.SELLTIX_WALLET4:
        wallet_pool.release_wallet("wallet4")
    elif addressToRelease == settings.SELLTIX_WALLET5:
        wallet_pool.release_wallet("wallet5")
    return

def getEventDateToParisTimeZone(event):
    event_timezone = pytz.timezone('Europe/Paris')
    event_time = event.eventDate.astimezone(event_timezone)
    return event_time


def hasEventStarted(event):
    # get organizer timezone
    refOrga = event.refOrganiser
    event_timezone = pytz.timezone(refOrga.timezone)
    event_time = event.eventDate.astimezone(event_timezone)
    current_time = timezone.now().astimezone(event_timezone)

    if event_time < current_time:
        return True
    else:
        return False


def hasEventEnded(event):
    # get organizer timezone
    refOrga = event.refOrganiser
    event_timezone = pytz.timezone(refOrga.timezone)
    event_time = event.eventDate.astimezone(event_timezone)
    # add the event.duration to the event_time
    # Convert duration to timedelta
    duration = timedelta(minutes=event.duration)
    event_time = event_time + duration
    current_time = timezone.now().astimezone(event_timezone)
    if event_time < current_time:
        return True
    else:
        return False


def sendEmail(email, subject, html_content):
    logger.error("===On envoi erreur email")
    logger.error(email)
    logger.error(subject)
    logger.error(html_content)
    logger.error("=======================")
    message = Mail(
        from_email='contact@selltix.fr',
        #to_emails=email,
        to_emails=[email,"rfarache@mac.com"],
        subject=subject,
        html_content=html_content)
    try:
        sg = SendGridAPIClient(SENDGRID_API_KEY)
        response = sg.send(message)
        # print(response.status_code)
        # print(response.body)
        # print(response.headers)
    except Exception as e:
        logger.error(e.message)


def sendInvoice(payment):
    import json
    try:
        invoice = Invoice()
        invoice.refUser = payment.refUser
        # get month form payment.createdAt
        invoice.month = payment.createdAt.month
        invoice.year = payment.createdAt.year
        invoice.totalVATExcluded = payment.amount
        invoice.vatAmount = 0
        invoice.totalVATIncluded = payment.amount
        invoice.paid = True
        invoice.stripeInvoiceId = json.loads(payment.jsonStripe)["id"]
        invoice.save()  # Save the invoice first to ensure it has an ID
        invoice.payments.add(payment)
        urlFacture = "https://api.selltix.live/invoice/"+str(invoice.id)+"/"
        sg = sendgrid.SendGridAPIClient(settings.SENDGRID_API_KEY)
        data = {
            "personalizations": [
                {
                    "to": [
                        {
                            "email": payment.refUser.email
                        }

                    ],
                    "subject": "SellTix - Votre facture",
                    "dynamic_template_data": {
                        "urlFacture": urlFacture
                    }
                }
            ],
            "from": {
                "email": "SellTix <contact@selltix.live>"
            },
            "template_id": settings.TEMPLATE_ID_FACTURE
        }
        response = sg.client.mail.send.post(request_body=data)
        if response.status_code == 202:
            logger.info("Email sent successfully!")
        else:
            logger.error("Failed to send email.")
            logger.error(response.status_code)
            logger.error(response.body)
            logger.error(response.headers)
    except Exception as e:
        from backoffice.helpers import sendEmail
        message = "sendInvoice <b>%s</b>   a échoué !" % (invoice.id)
        message += str(e)
        sendEmail("csurbier@idevotion.fr",
                  "[SellTix] Facturation failed", message)


class EmailBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        UserModel = get_user_model()
        try:
            user = UserModel.objects.get(email=username)
        except UserModel.DoesNotExist:
            return None

        if user.check_password(password):
            return user

    def get_user(self, user_id):
        UserModel = get_user_model()
        try:
            return UserModel.objects.get(pk=user_id)
        except UserModel.DoesNotExist:
            return None


def sendMatic(walletAddress, amount):
    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
        needToWait = True
        while needToWait:
            wallet_from_pool = wallet_pool.get_wallet()
            if wallet_from_pool is None:
                time.sleep(0.5)
            else:
                if wallet_from_pool == walletToUse:
                    needToWait = False
                else:
                    time.sleep(0.5)
        checkedTransferTOWalletAddress = w3.to_checksum_address(walletAddress)
        checkedFromWalletAddress = w3.to_checksum_address(ADRESS_TO_USE)
        nonce = w3.eth.get_transaction_count(
            checkedFromWalletAddress, "latest")
        amountWei = w3.to_wei(amount, 'ether')
        # Estimating gas price
        gasEstimate = w3.eth.estimate_gas({
            "from": checkedFromWalletAddress,
            "to": checkedTransferTOWalletAddress, "value": amountWei
        })

        # creating a raw transaction
        raw_transaction = {
            "from": checkedFromWalletAddress,
            "to": checkedTransferTOWalletAddress,
            "value": amountWei,
            "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)')
        status = resultTx["status"]
        
        wallet_pool.release_wallet(wallet_from_pool)
        if status == 1:
            logger.info("API publish event send MATIC OK")
            # : send selltix an email
            # send code to user using sendgrid
            from backoffice.helpers import sendEmail
            sendEmail("csurbier@idevotion.fr","SellTix - Envoi matic sur wallet","Envoi de %s Matic sur wallet %s" % (amount, walletAddress))
            return True
        else:
            logger.info("Envoi de matic statut transaction failed")
            return False
    except Exception as e:
        logger.error(e)
        from backoffice.helpers import sendEmail
        message = "Envoi matic sur wallet : <b>%s</b> Exception %s" % (
            walletAddress, str(e))
        sendEmail("csurbier@idevotion.fr",
                  "SellTix - Envoi matic sur wallet : error", message)
        return False


def askTva():
    # TODO : ask tva to user
    pass


def generateInvoice(invoice):
    # créer le fichier PDF
    try:
        from weasyprint import HTML
        url = settings.BACKEND_URL + "/invoice/" + str(invoice.id) + "/"
        path = os.path.join(os.path.abspath(
            os.path.dirname("__file__")) + "/static/storage/factures")
        if not os.path.exists(path):
            os.makedirs(path)
        file_name = "factures_{0}_{1}.pdf".format(
            invoice.id, invoice.createdAt.strftime('%d-%m-%Y'))
        file_path = path + "/" + file_name
        HTML(url).write_pdf(file_path)
        invoice.pdfFile = "/factures/" + file_name
        return True
    except Exception as e:
        logger.error(e)
        from backoffice.helpers import sendEmail
        message = "Erreur génération facture %s" % (str(e))
        sendEmail("csurbier@idevotion.fr",
                  "SellTix - Generation facture : error", message)
        return False

def transactionLogger(nonceValue,resultTx,data,status,wallet):
    try:
        from backoffice.models import TransactionManager
        transaction = TransactionManager()
        transaction.nonceValue = nonceValue
        transaction.transactionHashId = resultTx.transactionHash.hex()
        transaction.wallet = wallet
        transaction.jsonData = data
        if status==1:
            transaction.status = 0 #DOne
        else:
            transaction.status = 1 #Error
         
        transaction.save()
        if status==0:
            #warn selltix that transaction failed
            message = "Transaction failed for wallet %s" % (wallet)
            message+="\n\n Transaction hash : %s" % (resultTx.transactionHash.hex())
            message+="\n\n Transaction nonceValue : %s" % (nonceValue)
            logger_transaction.info(message)
            sendEmail("csurbier@idevotion.fr","Transaction Failure : ",message)
        else:
            #warn selltix that transaction was successful
            message = "Transaction successful for wallet %s" % (wallet)
            message+="\n\n Transaction hash : %s" % (resultTx.transactionHash.hex())
            message+="\n\n Transaction nonceValue : %s" % (nonceValue)
            logger_transaction.info(message)
    except Exception as e:
        logger.error(e)
        message = "Erreur  %s" % (str(e))
        sendEmail("csurbier@idevotion.fr","Transaction logger : error ",message)

def createuserWallet(user):
    walletAddressCreated = None
    mnemo = Mnemonic("english")
    words = mnemo.generate(strength=256)
    # generate codePin of 4 number digits
    codePin = str(random.randint(1000, 9999))
    #seed = mnemo.to_seed(words, passphrase=codePin)
    public_key=None
    private_key=None
    try:
        w3 = Web3(Web3.HTTPProvider(settings.CONTRACT_NODE_URL))
        account = w3.eth.account.create()
        private_key = w3.to_hex(account.key)
        public_key = account.address
        #update user with encoding keys
        # Create a signer wallet
        user.generatedWallet=True
        user.walletAddress = public_key
        user.publicKey = public_key
        from api.content import encrypt
        #encrypt private key 
        encryptedPrivateKey = encrypt(private_key,codePin)
        user.privateKey = encryptedPrivateKey
        user.words = encrypt(words,codePin)
        # calcule chiffre 
        theCode = 9999 - int(codePin)
        user.pinCode = encrypt(str(theCode),os.getenv("SECRET_PIN_KEY"))
        user.save()
        walletAddressCreated = public_key
    except Exception as e:
        status="ERROR"
        logger.error(e)
    return walletAddressCreated