from socketserver import StreamRequestHandler, TCPServer
from src import endpoints
from src import schema
from src import db
import json


class RequestHandler(StreamRequestHandler):
    """
    Receieves and processes json input; dispatches input to the
    requested endpoint, or responds with error objects.
    """


    def reply(self, obj):
        self.wfile.write(bytes(json.dumps(obj), "utf8"))


    def handle(self):
        try:
            request = json.loads(str(self.rfile.read(), "utf8"))
            endpoint = request.get("method")

            if endpoint not in endpoints.endpoints:
                return self.reply(schema.error(2, "Invalid endpoint"))

            # check to make sure all the arguments for endpoint are provided
            elif any([key not in request for key in endpoints.endpoints[endpoint]]):
                return self.reply(schema.error(3, "{} requires: {}".format(
                    endpoint, ", ".join(endpoints.endpoints[endpoint]))))

            elif endpoint not in endpoints.authless:
                if not request.get("user"):
                    return self.reply(schema.error(4, "No username provided."))

                user = db.user_resolve(request["user"])
                request["user"] = user

                if not user:
                    return self.reply(schema.error(5, "User not registered"))

                elif endpoint != "check_auth" and not \
                     db.user_auth(user, request.get("auth_hash")):
                     return self.reply(schema.error(6, "Authorization failed."))

            # post_ids are always returned as integers, but for callers who
            # provide them as something else, try to convert them.
            if isinstance(request.get("post_id"), (float, str)):
                try: request["post_id"] = int(request["post_id"])
                except Exception:
                    return schema.error(3, "Non-numeric post_id")

            # exception handling is now passed to the endpoints;
            # anything unhandled beyond here is a code 1
            self.reply(eval("endpoints." + endpoint)(request))

        except json.decoder.JSONDecodeError as E:
            return self.reply(schema.error(0, str(E)))

        except Exception as E:
            return self.reply(schema.error(1, str(E)))


def run(host, port):
    server = TCPServer((host, port), RequestHandler)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        print("bye")
        server.server_close()
