import urllib2
import urlparse
from xml.etree import ElementTree
from django.http import HttpResponse
from tastypie.authentication import Authentication
from tastypie.authorization import Authorization
from tastypie.resources import Resource
from tastypie.serializers import Serializer
from tastypie.throttle import CacheThrottle
from corehq.apps.grapevine.forms import GrapevineBackendForm
from corehq.apps.sms.util import clean_phone_number
from corehq.apps.sms.mixin import SMSBackend
from couchdbkit.ext.django.schema import *
from xml.sax.saxutils import escape, unescape
from django.conf import settings
from corehq.apps.sms.api import incoming as incoming_sms
import logging

logger = logging.getLogger(__name__)

TEMPLATE = """<?xml version="1.0" encoding="UTF-8"?>
    <gviSmsMessage>
        <affiliateCode>{affiliate_code}</affiliateCode>
        <authenticationCode>{auth_code}</authenticationCode>
        <messageType>text</messageType>
        <recipientList>
            <message>{message}</message>
            <recipient>
                <msisdn>{msisdn}</msisdn>
            </recipient>
        </recipientList>
    </gviSmsMessage>"""


class GrapevineBackend(SMSBackend):
    affiliate_code = StringProperty()
    authentication_code = StringProperty()

    @classmethod
    def get_api_id(cls):
        return "GVI"

    @classmethod
    def get_generic_name(cls):
        return "Grapevine"

    @classmethod
    def get_template(cls):
        return "grapevine/backend.html"

    @classmethod
    def get_form_class(cls):
        return GrapevineBackendForm

    def send(self, msg, delay=True, *args, **kwargs):
        phone_number = clean_phone_number(msg.phone_number)
        text = msg.text.encode("utf-8")

        data = TEMPLATE.format(
            affiliate_code=escape(self.affiliate_code),
            auth_code=escape(self.authentication_code),
            message=escape(text),
            msisdn=escape(phone_number)
        )

        url = "http://www.gvi.bms9.vine.co.za/httpInputhandler/ApplinkUpload"
        req = urllib2.Request(url, data)
        response = urllib2.urlopen(req, timeout=settings.SMS_GATEWAY_TIMEOUT)
        resp = response.read()


class SmsMessage(object):
    phonenumber = ''
    text = ''

    def __init__(self, phonenumber=None, text=None):
        self.phonenumber = phonenumber
        self.text = unescape(text) if text else ''

    @property
    def is_complete(self):
        return bool(self.phonenumber)


class UrlencodedDeserializer(Serializer):
    formats = ['json', 'jsonp', 'xml', 'yaml', 'html', 'plist', 'urlencode']
    content_types = {
        'json': 'application/json',
        'jsonp': 'text/javascript',
        'xml': 'application/xml',
        'yaml': 'text/yaml',
        'html': 'text/html',
        'plist': 'application/x-plist',
        'urlencode': 'application/x-www-form-urlencoded',
    }

    def from_urlencode(self, data,options=None):
        """ handles basic form encoded url posts """
        qs = dict((k, v if len(v)>1 else v[0] )
            for k, v in urlparse.parse_qs(data).iteritems())

        return qs

    def to_urlencode(self,content):
        pass


class SimpleApiAuthentication(Authentication):
    def is_authenticated(self, request, **kwargs):
        user = self.get_identifier(request)
        key = request.GET.get('apikey')

        expected_key = getattr(settings, 'SIMPLE_API_KEYS', {}).get(user)
        if not expected_key:
            logger.warning("No apikey defined for user '%s'" % user)
        return expected_key and key == expected_key

    def get_identifier(self, request):
        return request.GET.get('apiuser', 'nouser')


class GrapevineResource(Resource):
    """
    Handles grapevine messaging requests

    * incoming SMS
        <gviSms>
            <smsDateTime>2013-10-29T12:55:58</smsDateTime>
            <gatewayIdentifier>vodacomPremMGA2Rx1</gatewayIdentifier>
            <cellNumber>27827891099</cellNumber>
            <smsLocation>30665</smsLocation>
            <content>Another test</content>
        </gviSms>

    * Replies to SMS
        <gviSmsResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <responseDateTime>2013-10-29T13:19:07</responseDateTime>
            <recipient>
                <msisdn>27827891099</msisdn>
            </recipient>
            <responseType>reply</responseType>
            <response>Test reply</response>
        </gviSmsResponse>

    * SMS Status reports (not currently implemented)
        <gviSmsResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <responseDateTime>2013-10-29T13:15:52</responseDateTime>
            <submitDateTime>2013-10-29T13:15:49</submitDateTime>
            <recipient>
                <msisdn>27827891099</msisdn>
            </recipient>
            <responseType>receipt</responseType>
            <status>
                <code>0</code>
                <reason>Message is delivered to destination. stat:DELIVRD</reason>
            </status>
        </gviSmsResponse>

    smsDateTime: The date and time when the original SMS arrived at GVI's SMS gateway.
    gatewayIdentifier: Identifies the network and the rate of the SMS.
    cellNumber: The number (in international MSISDN format) of the mobile phone that sent the SMS.
    smsLocation: The short code to which the SMS was sent.
    content: The message text of the SMS message.
    """
    class Meta:
        resource_name = 'sms'
        object_class = SmsMessage
        authorization = Authorization()
        allowed_methods = ['post']
        serializer = UrlencodedDeserializer()
        throttle = CacheThrottle(throttle_at=600, timeframe=60, expiration=86400)
        authentication = SimpleApiAuthentication()

    def detail_uri_kwargs(self, bundle_or_obj):
        return {}

    def full_hydrate(self, bundle):
        if not bundle.data or not bundle.data.get('XML'):
            return bundle

        root = ElementTree.fromstring(bundle.data['XML'])
        if root.tag == 'gviSms':
            date_string = root.find('smsDateTime').text
            phone_number = root.find('cellNumber').text
            content_text = root.find('content').text
            bundle.obj = SmsMessage(phone_number, content_text)

        elif root.tag == 'gviSmsResponse':
            date_string = root.find('responseDateTime').text
            phone_number = root.find('recipient/msisdn').text
            resp_type = root.find('responseType').text  # receipt, reply or error

            if resp_type == 'reply':
                response_text = root.find('response').text
                bundle.obj = SmsMessage(phone_number, response_text)

        return bundle

    def obj_create(self, bundle, request=None, **kwargs):
        bundle = self.full_hydrate(bundle)

        if bundle.obj.is_complete:
            incoming_sms(bundle.obj.phonenumber, bundle.obj.text, GrapevineBackend.get_api_id())

        return bundle

    def post_list(self, request, **kwargs):
        super(GrapevineResource, self).post_list(request, **kwargs)
        # respond with 200 OK instead of 201 CREATED
        return HttpResponse()


import urllib2
import urlparse
from xml.etree import ElementTree
from django.http import HttpResponse
from tastypie.authentication import Authentication
from tastypie.authorization import Authorization
from tastypie.resources import Resource
from tastypie.serializers import Serializer
from tastypie.throttle import CacheThrottle
from corehq.apps.grapevine.forms import GrapevineBackendForm
from corehq.apps.sms.util import clean_phone_number
from corehq.apps.sms.mixin import SMSBackend
from couchdbkit.ext.django.schema import *
from xml.sax.saxutils import escape, unescape
from django.conf import settings
from corehq.apps.sms.api import incoming as incoming_sms
import logging

logger = logging.getLogger(__name__)

TEMPLATE = """<?xml version="1.0" encoding="UTF-8"?>
    <gviSmsMessage>
        <affiliateCode>{affiliate_code}</affiliateCode>
        <authenticationCode>{auth_code}</authenticationCode>
        <messageType>text</messageType>
        <recipientList>
            <message>{message}</message>
            <recipient>
                <msisdn>{msisdn}</msisdn>
            </recipient>
        </recipientList>
    </gviSmsMessage>"""


class GrapevineBackend(SMSBackend):
    affiliate_code = StringProperty()
    authentication_code = StringProperty()

    @classmethod
    def get_api_id(cls):
        return "GVI"

    @classmethod
    def get_generic_name(cls):
        return "Grapevine"

    @classmethod
    def get_template(cls):
        return "grapevine/backend.html"

    @classmethod
    def get_form_class(cls):
        return GrapevineBackendForm

    def send(self, msg, delay=True, *args, **kwargs):
        phone_number = clean_phone_number(msg.phone_number)
        text = msg.text.encode("utf-8")

        data = TEMPLATE.format(
            affiliate_code=escape(self.affiliate_code),
            auth_code=escape(self.authentication_code),
            message=escape(text),
            msisdn=escape(phone_number)
        )

        url = "http://www.gvi.bms9.vine.co.za/httpInputhandler/ApplinkUpload"
        req = urllib2.Request(url, data)
        response = urllib2.urlopen(req, timeout=settings.SMS_GATEWAY_TIMEOUT)
        resp = response.read()


class SmsMessage(object):
    phonenumber = ''
    text = ''

    def __init__(self, phonenumber=None, text=None):
        self.phonenumber = phonenumber
        self.text = unescape(text) if text else ''

    @property
    def is_complete(self):
        return bool(self.phonenumber)


class UrlencodedDeserializer(Serializer):
    formats = ['json', 'jsonp', 'xml', 'yaml', 'html', 'plist', 'urlencode']
    content_types = {
        'json': 'application/json',
        'jsonp': 'text/javascript',
        'xml': 'application/xml',
        'yaml': 'text/yaml',
        'html': 'text/html',
        'plist': 'application/x-plist',
        'urlencode': 'application/x-www-form-urlencoded',
    }

    def from_urlencode(self, data,options=None):
        """ handles basic form encoded url posts """
        qs = dict((k, v if len(v)>1 else v[0] )
            for k, v in urlparse.parse_qs(data).iteritems())

        return qs

    def to_urlencode(self,content):
        pass


class SimpleApiAuthentication(Authentication):
    def is_authenticated(self, request, **kwargs):
        user = self.get_identifier(request)
        key = request.GET.get('apikey')

        expected_key = getattr(settings, 'SIMPLE_API_KEYS', {}).get(user)
        if not expected_key:
            logger.warning("No apikey defined for user '%s'" % user)
        return expected_key and key == expected_key

    def get_identifier(self, request):
        return request.GET.get('apiuser', 'nouser')


class GrapevineResource(Resource):
    """
    Handles grapevine messaging requests

    * incoming SMS
        <gviSms>
            <smsDateTime>2013-10-29T12:55:58</smsDateTime>
            <gatewayIdentifier>vodacomPremMGA2Rx1</gatewayIdentifier>
            <cellNumber>27827891099</cellNumber>
            <smsLocation>30665</smsLocation>
            <content>Another test</content>
        </gviSms>

    * Replies to SMS
        <gviSmsResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <responseDateTime>2013-10-29T13:19:07</responseDateTime>
            <recipient>
                <msisdn>27827891099</msisdn>
            </recipient>
            <responseType>reply</responseType>
            <response>Test reply</response>
        </gviSmsResponse>

    * SMS Status reports (not currently implemented)
        <gviSmsResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <responseDateTime>2013-10-29T13:15:52</responseDateTime>
            <submitDateTime>2013-10-29T13:15:49</submitDateTime>
            <recipient>
                <msisdn>27827891099</msisdn>
            </recipient>
            <responseType>receipt</responseType>
            <status>
                <code>0</code>
                <reason>Message is delivered to destination. stat:DELIVRD</reason>
            </status>
        </gviSmsResponse>

    smsDateTime: The date and time when the original SMS arrived at GVI's SMS gateway.
    gatewayIdentifier: Identifies the network and the rate of the SMS.
    cellNumber: The number (in international MSISDN format) of the mobile phone that sent the SMS.
    smsLocation: The short code to which the SMS was sent.
    content: The message text of the SMS message.
    """
    class Meta:
        resource_name = 'sms'
        object_class = SmsMessage
        authorization = Authorization()
        allowed_methods = ['post']
        serializer = UrlencodedDeserializer()
        throttle = CacheThrottle(throttle_at=600, timeframe=60, expiration=86400)
        authentication = SimpleApiAuthentication()

    def detail_uri_kwargs(self, bundle_or_obj):
        return {}

    def full_hydrate(self, bundle):
        if not bundle.data or not bundle.data.get('XML'):
            return bundle

        root = ElementTree.fromstring(bundle.data['XML'])
        if root.tag == 'gviSms':
            date_string = root.find('smsDateTime').text
            phone_number = root.find('cellNumber').text
            content_text = root.find('content').text
            bundle.obj = SmsMessage(phone_number, content_text)

        elif root.tag == 'gviSmsResponse':
            date_string = root.find('responseDateTime').text
            phone_number = root.find('recipient/msisdn').text
            resp_type = root.find('responseType').text  # receipt, reply or error

            if resp_type == 'reply':
                response_text = root.find('response').text
                bundle.obj = SmsMessage(phone_number, response_text)

        return bundle

    def obj_create(self, bundle, request=None, **kwargs):
        bundle = self.full_hydrate(bundle)

        if bundle.obj.is_complete:
            incoming_sms(bundle.obj.phonenumber, bundle.obj.text, GrapevineBackend.get_api_id())

        return bundle

    def post_list(self, request, **kwargs):
        super(GrapevineResource, self).post_list(request, **kwargs)
        # respond with 200 OK instead of 201 CREATED
        return HttpResponse()


<?xml version="1.0" encoding="UTF-8"?>     <gviSmsMessage>         <affiliateCode>{affiliate_code}</affiliateCode>         <authenticationCode>{auth_code}</authenticationCode>         <messageType>text</messageType>         <recipientList>             <message>{message}</message>             <recipient>                 <msisdn>{msisdn}</msisdn>             </recipient>         </recipientList>     </gviSmsMessage> handles basic form encoded url posts Handles grapevine messaging requests      * incoming SMS         <gviSms>             <smsDateTime>2013-10-29T12:55:58</smsDateTime>             <gatewayIdentifier>vodacomPremMGA2Rx1</gatewayIdentifier>             <cellNumber>27827891099</cellNumber>             <smsLocation>30665</smsLocation>             <content>Another test</content>         </gviSms>      * Replies to SMS         <gviSmsResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">             <responseDateTime>2013-10-29T13:19:07</responseDateTime>             <recipient>                 <msisdn>27827891099</msisdn>             </recipient>             <responseType>reply</responseType>             <response>Test reply</response>         </gviSmsResponse>      * SMS Status reports (not currently implemented)         <gviSmsResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">             <responseDateTime>2013-10-29T13:15:52</responseDateTime>             <submitDateTime>2013-10-29T13:15:49</submitDateTime>             <recipient>                 <msisdn>27827891099</msisdn>             </recipient>             <responseType>receipt</responseType>             <status>                 <code>0</code>                 <reason>Message is delivered to destination. stat:DELIVRD</reason>             </status>         </gviSmsResponse>      smsDateTime: The date and time when the original SMS arrived at GVI's SMS gateway.     gatewayIdentifier: Identifies the network and the rate of the SMS.     cellNumber: The number (in international MSISDN format) of the mobile phone that sent the SMS.     smsLocation: The short code to which the SMS was sent.     content: The message text of the SMS message. # receipt, reply or error # respond with 200 OK instead of 201 CREATED