import flask

from infosystem.common import exception, utils
# TODO(samueldmq): find a better name to this
# from infosystem.common.subsystem import manager as m


class Controller(object):

    def __init__(self, manager, resource_wrap, collection_wrap):
        self.manager = manager
        self.resource_wrap = resource_wrap
        self.collection_wrap = collection_wrap

    def _filters_parse(self):
        filters = {
            k: flask.request.args.get(k) for k in flask.request.args.keys()}
        # TODO(samueldmq): fix this to work in a better way
        for k, v in filters.items():
            if v == 'true':
                filters[k] = True
            elif v == 'false':
                filters[k] = False
            elif v == 'null':
                filters[k] = None

        return filters

    def _filter_args(self, filters):
        filter_args = {k: v for k, v in filters.items() if '.' in k}

        return filter_args

    def _filters_cleanup(self, filters):
        _filters_cleanup = filters

        filter_args = self._filter_args(_filters_cleanup)
        # clean up original filters
        for k in filter_args.keys():
            # NOTE(samueldmq): I'm not sure I can pop
            # in the list comprehesion above...
            _filters_cleanup.pop(k)

        return _filters_cleanup

    def _get_include_dict(self, query_arg, filter_args):
        lists = [li.split('.') for li in query_arg.split(',')]
        include_dict = {}
        for list in lists:
            current = include_dict
            for i in range(len(list)):
                if list[i] in current:
                    current[list[i]].update({
                        list[i + 1]: {}} if i < (len(list) - 1) else {})
                else:
                    current[list[i]] = {
                        list[i + 1]: {}} if i < (len(list) - 1) else {}
                current = current[list[i]]

        for k, v in filter_args.items():
            list = k.split('.')
            current = include_dict
            for i in list[:-1]:  # last element is the attribute to filter on
                try:
                    current = current[i]
                except AttributeError:
                    # ignore current filter,
                    # entity to filter on is not included
                    continue
            current[list[-1]] = v

        return include_dict

    def _get_include_dicts(self):
        filters = self._filters_parse()

        include_args = filters.pop('include', None)
        filter_args = self._filter_args(filters)

        include_dict = self._get_include_dict(
            include_args, filter_args) if include_args else {}

        return include_dict

    def create(self):
        data = flask.request.get_json()

        try:
            if data:
                entity = self.manager.create(**data)
            else:
                entity = self.manager.create()
        except exception.InfoSystemException as exc:
            return flask.Response(response=exc.message,
                                  status=exc.status)

        response = {self.resource_wrap: entity.to_dict()}

        return flask.Response(response=utils.to_json(response),
                              status=201,
                              mimetype="application/json")

    def get(self, id):
        try:
            entity = self.manager.get(id=id)

            include_dicts = self._get_include_dicts()

            entity_dict = entity.to_dict(include_dict=include_dicts)
        except exception.InfoSystemException as exc:
            return flask.Response(response=exc.message,
                                  status=exc.status)

        response = {self.resource_wrap: entity_dict}

        return flask.Response(response=utils.to_json(response),
                              status=200,
                              mimetype="application/json")

    def list(self):
        filters = self._filters_parse()
        filters = self._filters_cleanup(filters)

        try:
            entities = self.manager.list(**filters)
        except exception.InfoSystemException as exc:
            return flask.Response(response=exc.message,
                                  status=exc.status)

        include_dicts = self._get_include_dicts()

        collection = []
        for entity in entities:
            if isinstance(entity, dict):
                collection.append(entity)
            else:
                try:
                    collection.append(
                        entity.to_dict(include_dict=include_dicts))
                except AssertionError:
                    # ignore current entity, filter mismatch
                    pass

        response = {self.collection_wrap: collection}

        return flask.Response(response=utils.to_json(response),
                              status=200,
                              mimetype="application/json")

    def update(self, id):
        data = flask.request.get_json()

        try:
            entity = self.manager.update(**data)
        except exception.InfoSystemException as exc:
            return flask.Response(response=exc.message,
                                  status=exc.status)

        response = {self.resource_wrap: entity.to_dict()}

        return flask.Response(response=utils.to_json(response),
                              status=200,
                              mimetype="application/json")

    def delete(self, id):
        try:
            self.manager.delete(id=id)
        except exception.InfoSystemException as exc:
            return flask.Response(response=exc.message,
                                  status=exc.status)

        return flask.Response(response=None,
                              status=204,
                              mimetype="application/json")
