from flask import Flask, request, make_response
import json
import platform
import sys
import hmac
import hashlib
from .version import __version__


class SlackServer(Flask):
    def __init__(self, verification_token, endpoint, emitter, server, secret):
        self.verification_token = verification_token
        self.secret = secret
        self.emitter = emitter
        self.endpoint = endpoint
        self.package_info = self.get_package_info()

        # If a server is passed in, bind the event handler routes to it,
        # otherwise create a new Flask instance.
        if server:
            if isinstance(server, Flask):
                self.bind_route(server)
            else:
                raise TypeError("Server must be an instance of Flask")
        else:
            Flask.__init__(self, __name__)
            self.bind_route(self)

    def get_package_info(self):
        client_name = __name__.split('.')[0]
        client_version = __version__  # Version is returned from version.py

        # Collect the package info, Python version and OS version.
        package_info = {
            "client": "{0}/{1}".format(client_name, client_version),
            "python": "Python/{v.major}.{v.minor}.{v.micro}".format(v=sys.version_info),
            "system": "{0}/{1}".format(platform.system(), platform.release())
        }

        # Concatenate and format the user-agent string to be passed into request headers
        ua_string = []
        for key, val in package_info.items():
            ua_string.append(val)

        return " ".join(ua_string)

    def verify_signature(self, timestamp, signature):
        new_hmac = hmac.new(self.secret, 'v0:' + timestamp + ':' + request.data, hashlib.sha256).hexdigest()
        return hmac.compare_digest(signature.encode('utf-8'), 'v0=' + new_hmac)

    def bind_route(self, server):
        @server.route(self.endpoint, methods=['GET', 'POST'])
        def event():
            # If a GET request is made, return 404.
            if request.method == 'GET':
                return make_response("These are not the slackbots you're looking for.", 404)

            # Parse the request payload into JSON
            event_data = json.loads(request.data.decode('utf-8'))

            # Echo the URL verification challenge code
            if "challenge" in event_data:
                return make_response(
                    event_data.get("challenge"), 200, {"content_type": "application/json"}
                )

            # Verify the request token
            request_token = event_data.get("token")
            if self.verification_token != request_token:
                self.emitter.emit('error', 'invalid verification token')
                return make_response("Request contains invalid Slack verification token", 403)

            # Verify the signature if secret is passed
            if self.secret:
                timestamp = request.headers.get('X-Slack-Request-Timestamp')
                signature = request.headers.get('X-Slack-Signature')
                if not self.verify_signature(timestamp, signature):
                    self.emitter.emit('error', 'signature not verified')
                    return make_response("Request contains a signature that does not match", 403)

            # Parse the Event payload and emit the event to the event listener
            if "event" in event_data:
                event_type = event_data["event"]["type"]
                self.emitter.emit(event_type, event_data)
                response = make_response("", 200)
                response.headers['X-Slack-Powered-By'] = self.package_info
                return response
