import django.conf
import django.http
import hashlib
import binascii


ERRORS = {
    "Duplicate reference number": "Reference number must be unique for each transaction.",
    "Invalid merchant": "The merchant code does not exist.",
    "Invalid parameters": "Some parameter posted to iPay88 is invalid or empty",
    "Overlimit per transaction": "You exceed the amount value per transaction. * For Testing account, only amount RM 1.00 is allowed.",
    "Payment not allowed": "The Payment method you requested is not allowed for this merchant code, please contact ipay88 to enable your payment option.",
    "Permission not allow": "Referrer URL in for your account registered in Ipay88 does not match. Please register your request and response URL with iPay88.",
    "Signature not match": "The Signature generated is incorrect.",
    "Status not approved": "Account was suspended or not active."
}

CURRENCIES = {
    "MYR": "Malaysian Ringgit",
    "USD": "US Dollar",
    "CNY": "Yuan Renminbi"
}

class iPay88NotConfigured(Exception):
    """Raised when a value is missing from the settings file

    Attributes:
        key -- key of the missing setting
    """
    def __init__(self, key):
        self.value = value
        
    def __str__(self):
        return "The following value is missing from your settings file: {key}".format( key=repr(self.key) )
        
class iPay88InvalidContextValue(Exception):
    """Raised when a value is invalid in the arguments passed to the context

    Attributes:
        key -- key of the missing setting
    """
    def __init__(self, key):
        self.value = value
        
    def __str__(self):
        return "The following value is invalid: {key}".format( key=repr(self.key) )

class iPay88:
    def load_config( self ):
        try:
            self.merchant_code = django.conf.settings.MERCHANT_CODE
        except:
            raise iPay88NotConfigured( "MERCHANT_CODE" )
            return
            
        try:
            self.merchant_key = django.conf.settings.MERCHANT_KEY
        except:
            raise iPay88NotConfigured( "MERCHANT_KEY" )
            return
        
    """
        Mandatory arguments: 
        RefNo
        Amount
        Currency
        ProdDesc
        UserName
        UserEmail
        UserContact

        ResponseURL
        BackendURL
        
        Optional arguments
        PaymentId
        Remark
        Lang
        
        MerchantCode will be read from settings
        
        Signature will be generated by the application
    """        
    def create_request_context(  self, **kwargs ):
        context = dict(kwargs)
        # Ensure that the amount is in correct format:
        try:
            amount = float(kwargs["Amount"])
        except ValueError:
            raise ValueError("Invalid amount value")
        else:
            context["Amount"] = '{:20,.2f}'.format(amount).lstrip()
            
        # Ensure currency exists
        if not kwargs["Currency"] in CURRENCIES:
            # Give it a change with values instead
            if not kwargs["Currency"] in CURRENCIES.values():
                raise iPay88InvalidContextValue( "Currency" )
                
            else:
                for k, x in CURRENCIES.items():
                    if x == kwargs["Currency"]:
                        context["Currency"] = k
                        break
                        
        context["MerchantCode"] = self.merchant_code
        
        context["Signature"] = self._make_signature( **context )
    
        return context
        
    def _make_signature( self, **kwargs ):
        """
            MerchantKey (Provided by iPay88 OPSG and share between iPay88 and merchant only)
            MerchantCode
            RefNo
            Amount
            Currency
            
            The fields must set in the following order,
            (MerchantKey & MerchantCode & RefNo & Amount & Currency)
        """
        s = "{key}{code}{ref}{amount}{currency}".format( key=self.merchant_key, code=self.merchant_code, ref=kwargs["RefNo"], amount=kwargs["Amount"].replace(",", "").replace(".",""), currency=kwargs["Currency"] )
        
        h = hashlib.sha1()
        
        h.update( s )
        
        digested = h.digest()
        
        return self._digest_and_64(digested)
        
    def _digest_and_64( self, s ):
        return binascii.b2a_base64(s)[:-1] # Drop the \n at the end
        
    def read_response( self, request ):
        # TODO: Implement HTTP_REFERRER?
        #if request.META["HTTP_REFERRER"] != ""
        # Compare signature
        signature = request.POST["Signature"]
        
        self.load_config()
        
        our_signature = self._make_response_signature( request )
        
        print signature, our_signature
        if signature == our_signature:
            return (request.POST["RefNo"], request.POST["Amount"], request.POST["Status"])
        return None
        
    def _make_response_signature( self, request ):
        """
            1. MerchantKey (Provided by iPay88 OPSG and share between iPay88 and
            merchant only)
            2. MerchantCode
            3. PaymentId
            4. RefNo
            5. Amount
            6. Currency
            7. Status
        """
        values = {
            "key": self.merchant_key,
            "code": self.merchant_code,
            "paymentid": request.POST["PaymentId"],
            "refno": request.POST["RefNo"],
            "amount": request.POST["Amount"].replace(".","").replace(",",""),
            "currency": request.POST["Currency"],
            "status": request.POST["Status"]
        }
            
        s = "{key}{code}{paymentid}{refno}{amount}{currency}{status}".format( **values )
        
        h = hashlib.sha1()
        
        h.update( s )
        
        digested = h.digest()
        
        return self._digest_and_64(digested)
        
"""
    A class which may be extended by the actual backend response view
    
    Defines a "post" only method, which returns a plain text "OK" HttpResponse, as per iPay88 requirements
"""
class BackendResponseView( django.views.generic.View ):
    def post( self, request, *args, **kwargs ):
        return django.http.HttpResponse("OK", content_type="text/plain")