import warnings

from decimal import Decimal

from django.conf import settings

from python_mbills.api import MBillsAPI
from python_mbills.exceptions import SignatureValidationException

from mbills.models import MBillsTransaction, TX_PAID

MBILLS_RSA_PUBLIC_KEY = getattr(settings, 'MBILLS_RSA_PUBLIC_KEY', None)
MBILLS_API_KEY = getattr(settings, 'MBILLS_API_KEY', None)
MBILLS_SECRET_KEY = getattr(settings, 'MBILSS_SECRET_KEY', None)

MBILLS_API_ENDPOINT = getattr(settings, 'MBILSS_API_ENDPOINT', 'https://demo3.halcom.com/MBillsWS')

if MBILLS_API_KEY is None or MBILLS_SECRET_KEY is None:
    warnings.warn("Missing MBILLS_API_KEY and/or MBILLS_SECRET_KEY from settings. API calls will fail!")

if MBILLS_RSA_PUBLIC_KEY is None:
    warnings.warn("Failed to load MBILLS_RSA_PUBLIC_KEY from settings. None of the API calls will be verified!")

# Configure the class only once...better than loading everytime a request is made...
mbills_api = MBillsAPI(api_key=MBILLS_API_KEY, shared_secret=MBILLS_SECRET_KEY,
                       mbills_rsa_pub_key=MBILLS_RSA_PUBLIC_KEY,
                       nonce_length=10, api_endpoint=MBILLS_API_ENDPOINT)


def generate_new_transaction(amount, purpose, payment_reference=None, order_id=None, channel_id=None, capture=True):
    """
    Create a new transaction object. 

    :param amount: 
    :param purpose: 
    :param payment_reference: 
    :param order_id: 
    :param channel_id: 
    :param capture: 
    :return: 
    """
    tx_id, token_number, status = mbills_api.create_new_sale(amount, purpose, payment_reference, order_id, channel_id,
                                                             capture)

    mbills_transaction = MBillsTransaction(transaction_id=tx_id,
                                           amount=amount,
                                           currency=mbills_api.currency,
                                           purpose=purpose,
                                           payment_reference=payment_reference,
                                           capture=capture,
                                           order_id=order_id,
                                           channel_id=channel_id,
                                           payment_token_number=token_number,
                                           status=status,
                                           )
    mbills_transaction.save()

    return mbills_transaction


def capture_transaction(transaction, capture_amount, message=''):
    """
    Capture existing transaction. 
    :param transaction: 
    :param capture_amount: 
    :param message: 
    :return: 
    """
    status_code = mbills_api.capture_sale(transaction.transaction_id, capture_amount=capture_amount, message=message)

    transaction.status = status_code
    transaction.save()

    return transaction


def void_transaction(transaction, message=''):
    """
    Void transaction.
    :param transaction: 
    :param message: 
    :return: 
    """
    status_code = mbills_api.void_preauthorization(transaction.transaction_id, message=message)

    transaction.status = status_code
    transaction.save()

    return transaction


def refund_transaction(transaction, refund_amount):
    """
    Refund transaction.

    :param transaction:
    :param refund_amount:
    :return:
    """
    status_code = mbills_api.refund_transaction(transaction.transaction_id, refund_amount=refund_amount)

    transaction.refunded = True
    transaction.refunded_amount = refund_amount
    transaction.save()

    return transaction


def update_transaction_status(transaction):
    """
    Update the transaction status and return the refreshed transcation object
    :param transaction: 
    :return: 
    """
    tx_json = mbills_api.fetch_transaction_status(transaction_id=transaction.transaction_id)

    return _update_transaction_from_data(transaction, tx_json)


def handle_webhook(json_data):
    """
    Fetch the transaction from the database and update it's status.

    If the transaction does not exist in the database this function will 
    raise a DoesNotExist exception on the MBillsTransaction object.

    If the received webhook data server signature is not valid it will raise SignatureValidationException exception.

    :param json_data: data received from the webhook
    :return: 
    """
    if not mbills_api.base.verify_response(json_data):
        raise SignatureValidationException('Server signature verification has failed')

    transaction = MBillsTransaction.objects.get(transaction_id=json_data.get('transactionid'))

    return _update_transaction_from_data(transaction, json_data)


def _update_transaction_from_data(transaction, json_data):
    if transaction.status != TX_PAID:  # Do not update if it's already paid - final state
        # Update the fields...
        transaction.status = json_data.get('status', transaction.status)
        transaction.settled_amount = Decimal(json_data.get('settledamount', 0) / 100.0)
        transaction.fees = Decimal(json_data.get('fees', 0) / 100.0)
        transaction.transaction_type = json_data.get('transactiontype', transaction.transaction_type)

        transaction.save()

    return transaction

import warnings

from decimal import Decimal

from django.conf import settings

from python_mbills.api import MBillsAPI
from python_mbills.exceptions import SignatureValidationException

from mbills.models import MBillsTransaction, TX_PAID

MBILLS_RSA_PUBLIC_KEY = getattr(settings, 'MBILLS_RSA_PUBLIC_KEY', None)
MBILLS_API_KEY = getattr(settings, 'MBILLS_API_KEY', None)
MBILLS_SECRET_KEY = getattr(settings, 'MBILSS_SECRET_KEY', None)

MBILLS_API_ENDPOINT = getattr(settings, 'MBILSS_API_ENDPOINT', 'https://demo3.halcom.com/MBillsWS')

if MBILLS_API_KEY is None or MBILLS_SECRET_KEY is None:
    warnings.warn("Missing MBILLS_API_KEY and/or MBILLS_SECRET_KEY from settings. API calls will fail!")

if MBILLS_RSA_PUBLIC_KEY is None:
    warnings.warn("Failed to load MBILLS_RSA_PUBLIC_KEY from settings. None of the API calls will be verified!")

# Configure the class only once...better than loading everytime a request is made...
mbills_api = MBillsAPI(api_key=MBILLS_API_KEY, shared_secret=MBILLS_SECRET_KEY,
                       mbills_rsa_pub_key=MBILLS_RSA_PUBLIC_KEY,
                       nonce_length=10, api_endpoint=MBILLS_API_ENDPOINT)


def generate_new_transaction(amount, purpose, payment_reference=None, order_id=None, channel_id=None, capture=True):
    """
    Create a new transaction object. 

    :param amount: 
    :param purpose: 
    :param payment_reference: 
    :param order_id: 
    :param channel_id: 
    :param capture: 
    :return: 
    """
    tx_id, token_number, status = mbills_api.create_new_sale(amount, purpose, payment_reference, order_id, channel_id,
                                                             capture)

    mbills_transaction = MBillsTransaction(transaction_id=tx_id,
                                           amount=amount,
                                           currency=mbills_api.currency,
                                           purpose=purpose,
                                           payment_reference=payment_reference,
                                           capture=capture,
                                           order_id=order_id,
                                           channel_id=channel_id,
                                           payment_token_number=token_number,
                                           status=status,
                                           )
    mbills_transaction.save()

    return mbills_transaction


def capture_transaction(transaction, capture_amount, message=''):
    """
    Capture existing transaction. 
    :param transaction: 
    :param capture_amount: 
    :param message: 
    :return: 
    """
    status_code = mbills_api.capture_sale(transaction.transaction_id, capture_amount=capture_amount, message=message)

    transaction.status = status_code
    transaction.save()

    return transaction


def void_transaction(transaction, message=''):
    """
    Void transaction.
    :param transaction: 
    :param message: 
    :return: 
    """
    status_code = mbills_api.void_preauthorization(transaction.transaction_id, message=message)

    transaction.status = status_code
    transaction.save()

    return transaction


def refund_transaction(transaction, refund_amount):
    """
    Refund transaction.

    :param transaction:
    :param refund_amount:
    :return:
    """
    status_code = mbills_api.refund_transaction(transaction.transaction_id, refund_amount=refund_amount)

    transaction.refunded = True
    transaction.refunded_amount = refund_amount
    transaction.save()

    return transaction


def update_transaction_status(transaction):
    """
    Update the transaction status and return the refreshed transcation object
    :param transaction: 
    :return: 
    """
    tx_json = mbills_api.fetch_transaction_status(transaction_id=transaction.transaction_id)

    return _update_transaction_from_data(transaction, tx_json)


def handle_webhook(json_data):
    """
    Fetch the transaction from the database and update it's status.

    If the transaction does not exist in the database this function will 
    raise a DoesNotExist exception on the MBillsTransaction object.

    If the received webhook data server signature is not valid it will raise SignatureValidationException exception.

    :param json_data: data received from the webhook
    :return: 
    """
    if not mbills_api.base.verify_response(json_data):
        raise SignatureValidationException('Server signature verification has failed')

    transaction = MBillsTransaction.objects.get(transaction_id=json_data.get('transactionid'))

    return _update_transaction_from_data(transaction, json_data)


def _update_transaction_from_data(transaction, json_data):
    if transaction.status != TX_PAID:  # Do not update if it's already paid - final state
        # Update the fields...
        transaction.status = json_data.get('status', transaction.status)
        transaction.settled_amount = Decimal(json_data.get('settledamount', 0) / 100.0)
        transaction.fees = Decimal(json_data.get('fees', 0) / 100.0)
        transaction.transaction_type = json_data.get('transactiontype', transaction.transaction_type)

        transaction.save()

    return transaction

# Configure the class only once...better than loading everytime a request is made... Create a new transaction object.       :param amount:      :param purpose:      :param payment_reference:      :param order_id:      :param channel_id:      :param capture:      :return: Capture existing transaction.      :param transaction:      :param capture_amount:      :param message:      :return: Void transaction.     :param transaction:      :param message:      :return: Refund transaction.      :param transaction:     :param refund_amount:     :return: Update the transaction status and return the refreshed transcation object     :param transaction:      :return: Fetch the transaction from the database and update it's status.      If the transaction does not exist in the database this function will      raise a DoesNotExist exception on the MBillsTransaction object.      If the received webhook data server signature is not valid it will raise SignatureValidationException exception.      :param json_data: data received from the webhook     :return: # Do not update if it's already paid - final state # Update the fields...