"""Bridging module between Django Oscar and gateway module."""
import logging

from oscar.apps.payment import exceptions as oscar_exceptions

from helcim import exceptions as helcim_exceptions, gateway

LOG = logging.getLogger(__name__)

def remap_oscar_billing_address(address):
    """Remaps Oscar billing address dictionary to Helcim dictionary.

        Parameters:
            address (dict): A dictionary of the billing address details
                provided by Django Oscar.

        Returns:
            dict: Billing address details formatted for
                django-helcim.
    """
    if address.get('first_name') and address.get('last_name'):
        contact_name = '{} {}'.format(
            address.get('first_name'), address.get('last_name')
        )
    elif address.get('first_name'):
        contact_name = address.get('first_name')
    elif address.get('last_name'):
        contact_name = address.get('last_name')
    else:
        contact_name = None

    return {
        'billing_contact_name': contact_name,
        'billing_street_1': address.get('line1'),
        'billing_street_2': address.get('line2'),
        'billing_city': address.get('line4'),
        'billing_province': address.get('state'),
        'billing_postal_code': address.get('postcode'),
        'billing_country': address.get('country'),
        'billing_phone': address.get('phone_number'),
    }

def remap_oscar_credit_card(card):
    """Remaps Oscar credit card object as Helcim dictionary.

        Parameters:
            card (obj): A credit card object provided by Django Oscar.

        Returns:
            dict: Credit card details formatted for django-helcim.
    """
    if card.expiry_date:
        cc_expiry = card.expiry_date.strftime('%m%y')
    else:
        cc_expiry = None

    return {
        'cc_name': card.name,
        'cc_number': card.number,
        'cc_expiry': cc_expiry,
        'cc_cvv': card.ccv,
    }

class BaseCardTransactionBridge():
    """Base class to bridge Oscar and Helcim transactions.

        Parameters:
            order_number (str): Order number for the transaction.
            amount (dec): The transaction total.
            card (obj): Instance of the Oscar bankcard class.
            billing_address (dict): The billing address information.
    """
    def __init__(self, order_number, amount, card, billing_address=None):
        transaction_details = {
            'order_number': order_number,
            'amount': amount,
        }

        if billing_address:
            transaction_details.update(
                remap_oscar_billing_address(billing_address)
            )

        transaction_details.update(remap_oscar_credit_card(card))

        self.transaction_details = transaction_details

class PurchaseBridge(BaseCardTransactionBridge):
    """Class to bridge Oscar and Helcim purchase transactions."""
    def process(self):
        """Attempts to process Purchase with transaction details.

            Raises:
                GatewayError: An Oscar error raised when there was an
                    error with the payment API.
                PaymentError: An Oscar error raised when there was an
                    error processing the payment.
        """

        purchase_instance = gateway.Purchase(**self.transaction_details)

        try:
            return purchase_instance.process()
        except helcim_exceptions.ProcessingError as error:
            raise oscar_exceptions.GatewayError(str(error))
        except helcim_exceptions.PaymentError as error:
            raise oscar_exceptions.PaymentError(str(error))
        except helcim_exceptions.DjangoError:
            LOG.exception(
                'Purchase complete, but errors occured while saving '
                'transaction to database'
            )
        except helcim_exceptions.HelcimError as error:
            raise oscar_exceptions.GatewayError(str(error))

class PreauthorizeBridge(BaseCardTransactionBridge):
    """Class to bridge Oscar and Helcim preauthorization transactions."""
    def process(self):
        """Attempts to process Preauthorize with transaction details.

            Raises:
                GatewayError: An Oscar error raised when there was an
                    error with the payment API.
                PaymentError: An Oscar error raised when there was an
                    error processing the payment.
        """

        preauth_instance = gateway.Preauthorize(**self.transaction_details)

        try:
            return preauth_instance.process()
        except helcim_exceptions.ProcessingError as error:
            raise oscar_exceptions.GatewayError(str(error))
        except helcim_exceptions.PaymentError as error:
            raise oscar_exceptions.PaymentError(str(error))
        except helcim_exceptions.DjangoError:
            LOG.exception(
                'Preauthorization complete, but errors occured while saving '
                'transaction to database'
            )
        except helcim_exceptions.HelcimError as error:
            raise oscar_exceptions.GatewayError(str(error))

class RefundBridge(BaseCardTransactionBridge):
    """Class to bridge Oscar and Helcim refund transactions."""
    def process(self):
        """Attempts to process Refund with transaction details.

            Raises:
                GatewayError: An Oscar error raised when there was an
                    error with the payment API.
                PaymentError: An Oscar error raised when there was an
                    error processing the payment.
        """

        refund_instance = gateway.Refund(**self.transaction_details)

        try:
            return refund_instance.process()
        except helcim_exceptions.ProcessingError as error:
            raise oscar_exceptions.GatewayError(str(error))
        except helcim_exceptions.PaymentError as error:
            raise oscar_exceptions.PaymentError(str(error))
        except helcim_exceptions.DjangoError:
            LOG.exception(
                'Refund complete, but errors occured while saving '
                'transaction to database'
            )
        except helcim_exceptions.HelcimError as error:
            raise oscar_exceptions.GatewayError(str(error))

class VerificationBridge(BaseCardTransactionBridge):
    """Class to bridge Oscar and Helcim Verification transactions."""
    def process(self):
        """Attempts to process Verification with transaction details.

            Raises:
                GatewayError: An Oscar error raised when there was an
                    error with the payment API.
                PaymentError: An Oscar error raised when there was an
                    error processing the payment.
        """

        verification_instance = gateway.Verification(
            **self.transaction_details
        )

        try:
            return verification_instance.process()
        except helcim_exceptions.ProcessingError as error:
            raise oscar_exceptions.GatewayError(str(error))
        except helcim_exceptions.PaymentError as error:
            raise oscar_exceptions.PaymentError(str(error))
        except helcim_exceptions.DjangoError:
            LOG.exception(
                'Verification complete, but errors occured while saving '
                'transaction to database'
            )
        except helcim_exceptions.HelcimError as error:
            raise oscar_exceptions.GatewayError(str(error))

class CaptureBridge():
    """Class to bridge Oscar and Helcim Capture transactions.

        Parameters:
            transaction_id (int): the Helcim preauthorization
                 transaction ID to capture.
    """
    def __init__(self, transaction_id):
        transaction_details = {
            'transaction_id': transaction_id,
        }

        self.transaction_details = transaction_details

    def process(self):
        """Attempts to process Capture with transaction details.

            Raises:
                GatewayError: An Oscar error raised when there was an
                    error with the payment API.
                PaymentError: An Oscar error raised when there was an
                    error processing the payment.
        """

        capture_instance = gateway.Capture(
            **self.transaction_details
        )

        try:
            return capture_instance.process()
        except helcim_exceptions.ProcessingError as error:
            raise oscar_exceptions.GatewayError(str(error))
        except helcim_exceptions.PaymentError as error:
            raise oscar_exceptions.PaymentError(str(error))
        except helcim_exceptions.DjangoError:
            LOG.exception(
                'Capture complete, but errors occured while saving '
                'transaction to database'
            )
            return None
        except helcim_exceptions.HelcimError as error:
            raise oscar_exceptions.GatewayError(str(error))
