# -*- coding: utf-8; -*-
################################################################################
#
#  Rattail -- Retail Software Framework
#  Copyright © 2010-2020 Lance Edgar
#
#  This file is part of Rattail.
#
#  Rattail is free software: you can redistribute it and/or modify it under the
#  terms of the GNU General Public License as published by the Free Software
#  Foundation, either version 3 of the License, or (at your option) any later
#  version.
#
#  Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
#  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
#  details.
#
#  You should have received a copy of the GNU General Public License along with
#  Rattail.  If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Tailbone Web API - Master View (v2)
"""

from __future__ import unicode_literals, absolute_import

from cornice import resource, Service

from tailbone.api import APIMasterView


class APIMasterView2(APIMasterView):
    """
    Base class for data model REST API views.
    """
    listable = True
    creatable = True
    viewable = True
    editable = True
    deletable = True
    supports_autocomplete = False

    @classmethod
    def establish_method(cls, method_name):
        """
        Establish the given HTTP method for this Cornice Resource.

        Cornice will auto-register any class methods for a resource, if they
        are named according to what it expects (i.e. 'get', 'collection_get'
        etc.).  Tailbone API tries to make things automagical for the sake of
        e.g. Poser logic, but in this case if we predefine all of these methods
        and then some subclass view wants to *not* allow one, it's not clear
        how to "undefine" it per se.  Or at least, the more straightforward
        thing (I think) is to not define such a method in the first place, if
        it was not wanted.

        Enter ``establish_method()``, which is what finally "defines" each
        resource method according to what the subclass has declared via its
        various attributes (:attr:`creatable`, :attr:`deletable` etc.).

        Note that you will not likely have any need to use this
        ``establish_method()`` yourself!  But we describe its purpose here, for
        clarity.
        """
        def method(self):
            internal_method = getattr(self, '_{}'.format(method_name))
            return internal_method()

        setattr(cls, method_name, method)

    def _delete(self):
        """
        View to handle DELETE action for an existing record/object.
        """
        obj = self.get_object()
        self.delete_object(obj)

    def delete_object(self, obj):
        """
        Delete the object, or mark it as deleted, or whatever you need to do.
        """
        # flush immediately to force any pending integrity errors etc.
        self.Session.delete(obj)
        self.Session.flush()

    @classmethod
    def defaults(cls, config):
        cls._defaults(config)

    @classmethod
    def _defaults(cls, config):
        route_prefix = cls.get_route_prefix()
        permission_prefix = cls.get_permission_prefix()
        collection_url_prefix = cls.get_collection_url_prefix()
        object_url_prefix = cls.get_object_url_prefix()

        # first, the primary resource API

        # list/search
        if cls.listable:
            cls.establish_method('collection_get')
            resource.add_view(cls.collection_get, permission='{}.list'.format(permission_prefix))

        # create
        if cls.creatable:
            cls.establish_method('collection_post')
            resource.add_view(cls.collection_post, permission='{}.create'.format(permission_prefix))

        # view
        if cls.viewable:
            cls.establish_method('get')
            resource.add_view(cls.get, permission='{}.view'.format(permission_prefix))

        # edit
        if cls.editable:
            cls.establish_method('post')
            resource.add_view(cls.post, permission='{}.edit'.format(permission_prefix))

        # delete
        if cls.deletable:
            cls.establish_method('delete')
            resource.add_view(cls.delete, permission='{}.delete'.format(permission_prefix))

        # register primary resource API via cornice
        object_resource = resource.add_resource(
            cls,
            collection_path=collection_url_prefix,
            # TODO: probably should allow for other (composite?) key fields
            path='{}/{{uuid}}'.format(object_url_prefix))
        config.add_cornice_resource(object_resource)

        # now for some more "custom" things, which are still somewhat generic

        # autocomplete
        if cls.supports_autocomplete:
            autocomplete = Service(name='{}.autocomplete'.format(route_prefix),
                                   path='{}/autocomplete'.format(collection_url_prefix))
            autocomplete.add_view('GET', 'autocomplete', klass=cls)
            config.add_cornice_service(autocomplete)
