from flask_restful import Resource, reqparse, abort, fields, marshal_with
import json
import os
import random
import string


class ClientsFile:
    file = "clients.json"
    mtime = 0
    clients = {}
    parser = None

    def __init__(self):
        self.get()
        self.setup_request_parser()

    def get(self):
        try:
            mtime = os.path.getmtime(self.file)
            if mtime > self.mtime:
                with open(self.file, "r") as client_file:
                    self.clients = json.load(client_file)
        except FileNotFoundError as error:
            self.clients = {}
            self.save()

        return self.clients

    def find(self, client_id):
        self.get()
        if client_id in self.clients:
            client = self.clients[client_id]
        else:
            client = None
        return client

    def add(self, client):
        client.secret = self.secret()
        self.clients[client["client_id"]] = client
        self.save()
        return client

    def remove(self, client):
        del self.clients[client["client_id"]]
        self.save()

    def update(self, client_updates):
        client = self.find(client_updates["client_id"])
        client.update(client_updates)
        self.clients[client["client_id"]] = client
        self.save()
        return client

    def save(self):
        try:
            with open(self.file, "w") as client_file:
                client_file.write(json.dumps(self.clients, indent=2))
                return True
        except OSError as error:
            print(str(error))
            return False

    def secret(self, chars=36):
        res = "".join(
            random.choices(
                string.ascii_lowercase + string.ascii_uppercase + string.digits, k=chars
            )
        )
        return str(res)

    def setup_request_parser(self):
        parser = reqparse.RequestParser()
        parser.add_argument(
            "client_id", type=str, help="A unique name to identify the client"
        )
        parser.add_argument(
            "client_name", type=str, help="A human friendly name to identify the client"
        )
        parser.add_argument(
            "subscription",
            type=str,
            help="A dot delimited string identify topics to subscribe to",
        )
        self.parser = parser

    def parse(self):
        return self.parser.parse_args()


resource_fields = {
    "client_id": fields.String,
    "client_name": fields.String,
    "subscription": fields.String,
}

clients_file = ClientsFile()

# Client
class Client(Resource):
    @marshal_with(resource_fields)
    def get(self, client_id):
        client = clients_file.find(client_id)
        if not client:
            abort(404, message="No client with id: {}".format(client_id))
        return client

    def delete(self, todo_id):
        client = clients_file.find(client_id)
        if not client:
            abort(404, message="No client with id: {}".format(client_id))
        else:
            clients_file.remove(client)
        return client, 204

    def put(self, client_id):
        args = clients_file.parse()
        client = clients_file.find(client_id)
        if not client:
            abort(404, message="No client with id: {}".format(client_id))
        else:
            client = clients_file.update(args)
        return client, 201


# ClientList
class ClientList(Resource):
    def get(self):
        return {
            client_id: (client, client.pop("secret", None))[0]
            for client_id, client in clients_file.get().items()
        }

    def post(self):
        args = clients_file.parse()
        client = clients_file.find(args["client_id"])
        if client:
            abort(403, message="Duplicate client id: {}".format(client_id))
        else:
            client = clients_file.add(args)
        return client, 201