# -*- coding: utf-8 -*-

from restart.parsers import Parser
from restart.renderers import Renderer
from restart.exceptions import Forbidden, NotImplemented, BadRequest
from wechatpy import parse_message, create_reply
from wechatpy.crypto import WeChatCrypto
from wechatpy.utils import check_signature
from wechatpy.exceptions import (
    InvalidSignatureException,
    InvalidAppIdException
)

from .interface import Interface
from .config import config


class CheckSignatureMiddleware(object):
    """The middleware class for checking signature."""

    def process_request(self, request):
        signature = request.args.get('signature', '')
        timestamp = request.args.get('timestamp', '')
        nonce = request.args.get('nonce', '')

        try:
            check_signature(config.TOKEN, signature, timestamp, nonce)
        except InvalidSignatureException:
            raise Forbidden()


class XMLParser(Parser):
    """The XML parser class for messages."""

    content_type = 'text/xml'

    def parse(self, stream, content_type, content_length, context=None):
        data = stream.read()

        request = context['request']
        encrypt_type = request.args.get('encrypt_type', 'raw')
        msg_signature = request.args.get('msg_signature', '')
        timestamp = request.args.get('timestamp', '')
        nonce = request.args.get('nonce', '')

        # In encryption mode
        if encrypt_type != 'raw':
            crypto = WeChatCrypto(config.TOKEN, config.ENCODING_AES_KEY,
                                  config.APP_ID)
            try:
                data = crypto.decrypt_message(data, msg_signature,
                                              timestamp, nonce)
            except (InvalidSignatureException, InvalidAppIdException):
                raise BadRequest()

        msg = parse_message(data)
        return msg


class XMLRenderer(Renderer):
    """The XML renderer class for replies."""

    content_type = 'text/xml'
    format_suffix = 'xml'

    def render(self, data, context=None):
        request = context['request']
        encrypt_type = request.args.get('encrypt_type', 'raw')
        timestamp = request.args.get('timestamp', '')
        nonce = request.args.get('nonce', '')

        # In encryption mode
        if encrypt_type != 'raw':
            crypto = WeChatCrypto(config.TOKEN, config.ENCODING_AES_KEY,
                                  config.APP_ID)
            data = crypto.encrypt_message(data, nonce, timestamp)

        return data


class WeChat(Interface):
    """The class that represents a WeChat interface."""

    #: The WeChat configuration
    token = None
    encoding_aes_key = None
    app_id = None

    #: The message handler mapping
    handlers = {
        'text': 'on_text',
        'image': 'on_image',
        'voice': 'on_voice',
        'video': 'on_video',
        'shortvideo': 'on_shortvideo',
        'location': 'on_location',
        'link': 'on_link',
        'event': 'on_event',
    }

    #: The parser classes used for parsing request data.
    parser_classes = (XMLParser,)

    #: The renderer classes used for rendering response data.
    renderer_classes = (XMLRenderer,)

    #: The resource-level middleware classes
    middleware_classes = (CheckSignatureMiddleware,)

    def __init__(self, action_map):
        # Assure that the configuration is loaded
        self.load_config()
        super(WeChat, self).__init__(action_map)

    @classmethod
    def load_config(cls):
        if not getattr(cls, '_has_loaded', False):
            config.TOKEN = cls.token
            config.ENCODING_AES_KEY = cls.encoding_aes_key
            config.APP_ID = cls.app_id

            cls._has_loaded = True

    def message_not_handled(self, message):
        raise NotImplemented()

    def get(self, request):
        return request.args.get('echostr', '')

    def post(self, request):
        message = request.data
        handler_name = self.handlers.get(message.type, '')
        if callable(handler_name):
            handler = handler_name
        else:
            handler = getattr(self, handler_name, self.message_not_handled)
        reply = handler(message)
        result = create_reply(reply, message)
        return result.render()
