# coding=utf-8
"""Admin panel for vocabularies."""
from __future__ import absolute_import, division, print_function, \
    unicode_literals

from flask import current_app, g, redirect, render_template, request

from abilian.i18n import _, _l
from abilian.services import get_service
from abilian.web import url_for, views
from abilian.web.action import Glyphicon
from abilian.web.admin import AdminPanel
from abilian.web.nav import BreadcrumbItem

from .forms import EditForm

_MARKER = object()


class ViewBase(object):
    title = _l('Vocabulary entry')
    base_template = "admin/_base.html"
    Form = EditForm
    Model = None

    def prepare_args(self, args, kwargs):
        if self.Model is None:
            self.Model = kwargs.get('Model')
            if hasattr(self.Model, '__form__'):
                self.Form = getattr(self.Model, '__form__')
        return args, kwargs

    def index_url(self):
        return url_for('.vocabularies')


class Edit(ViewBase, views.ObjectEdit):

    def prepare_args(self, args, kwargs):
        args, kwargs = ViewBase.prepare_args(self, args, kwargs)
        return views.ObjectEdit.prepare_args(self, args, kwargs)

    def view_url(self):
        return url_for(
            '.vocabularies_model',
            group=self.Model.Meta.group or '_',
            Model=self.Model.Meta.name,
        )


class Create(views.ObjectCreate, Edit):

    def init_object(self, args, kwargs):
        args, kwargs = super(Create, self).init_object(args, kwargs)
        self.obj.active = True  # do this because default value seems ignored?
        return args, kwargs


class Delete(ViewBase, views.ObjectDelete):
    pass


class VocabularyPanel(AdminPanel):
    """Vocabularies administration."""
    id = 'vocabularies'
    label = _l('Vocabularies')
    icon = 'list'

    def voc_edit_url(self, item):
        return url_for(
            '.' + self.id + '_edit',
            group=item.Meta.group or '_',
            Model=item.Meta.name,
            object_id=item.id,
        )

    def get(self):
        svc = get_service('vocabularies')
        vocabularies = svc.grouped_vocabularies
        ctx = {
            'service': svc,
            'url_for_voc_edit': self.voc_edit_url,
            'icon_checked': Glyphicon('check'),
            'vocabularies': vocabularies,
        }
        return render_template('admin/vocabularies.html', **ctx)

    def post(self):
        data = request.form
        group = data.get('group', '').strip()
        Model = data.get('Model', '').strip()
        return_to = data.get('return_to')
        return_endpoint = '.vocabularies'
        return_args = {}

        if return_to not in (None, 'group', 'model'):
            return_to = None

        def do_return():
            return redirect(url_for(return_endpoint, **return_args))

        if not Model:
            return do_return()

        if not group or group == '_':
            # default group
            group = None

        svc = get_service('vocabularies')
        Model = svc.get_vocabulary(name=Model, group=group)
        if not Model:
            return do_return()

        if return_to is not None:
            return_endpoint += '_' + return_to

        if return_to == 'group':
            return_args['group'] = group or '_'
        elif return_to == 'model':
            return_args['group'] = Model.Meta.group or '_'
            return_args['Model'] = Model.Meta.name

        if 'up' in data:
            cmp_op = Model.position.__lt__
            cmp_order = Model.position.desc()
            object_id = int(data.get('up'))
        elif 'down' in data:
            cmp_op = Model.position.__gt__
            cmp_order = Model.position.asc()
            object_id = int(data.get('down'))
        else:
            return do_return()

        session = current_app.db.session()
        query = Model.query.with_lockmode('update')
        item = query.get(object_id)
        other = query \
            .filter(cmp_op(item.position)) \
            .order_by(cmp_order) \
            .first()

        if other is not None:
            # switch positions
            # we have to work around unique constraint on 'position', since we cannot
            # write new positions simultaneously
            # "-1" is added to avoid breaking when one position==0
            pos = other.position
            other.position = -item.position - 1
            item.position = -pos - 1
            session.flush()
            item.position = pos
            other.position = -other.position - 1
            session.commit()

        return do_return()

    def group_view(self, group):
        if group is None:
            group = ''
        svc = get_service('vocabularies')
        groups = svc.grouped_vocabularies
        vocabularies = groups.get(group)

        ctx = {
            'service': svc,
            'url_for_voc_edit': self.voc_edit_url,
            'icon_checked': Glyphicon('check'),
            'vocabularies': {
                group: vocabularies,
            },
            'edit_return_to': 'group',
        }
        return render_template('admin/vocabularies.html', **ctx)

    def model_view(self, Model, group=None):
        svc = get_service('vocabularies')

        ctx = {
            'service': svc,
            'url_for_voc_edit': self.voc_edit_url,
            'icon_checked': Glyphicon('check'),
            'vocabularies': {
                Model.Meta.group: [Model],
            },
            'edit_return_to': 'model',
        }
        return render_template('admin/vocabularies.html', **ctx)

    def install_additional_rules(self, add_url_rule):
        panel_endpoint = '.' + self.id
        group_base = '/<string:group>/'
        add_url_rule(group_base, endpoint='group', view_func=self.group_view)
        # models
        base = group_base + '<string:Model>/'
        add_url_rule(base, endpoint='model', view_func=self.model_view)

        edit_view = Edit.as_view('edit', view_endpoint=panel_endpoint)
        add_url_rule(base + '<int:object_id>', view_func=edit_view)
        add_url_rule(
            base + 'new',
            view_func=Create.as_view('new', view_endpoint=panel_endpoint),
        )

    def url_value_preprocess(self, endpoint, view_args):
        Model = view_args.pop('Model', None)
        group = view_args.pop('group', _MARKER)

        if group == '_':
            # "General" group
            group = None

        if group is not _MARKER:
            view_args['group'] = group

        if Model is not None:
            svc = get_service('vocabularies')
            Model = svc.get_vocabulary(name=Model, group=group)
            g.breadcrumb.append(BreadcrumbItem(
                label=Model.Meta.group if group else _('Global'),
                url=url_for('.vocabularies_group', group=group or '_'),
            ))
            g.breadcrumb.append(
                BreadcrumbItem(
                    label=Model.Meta.label,
                    url=url_for(
                        '.vocabularies_model',
                        group=group or '_',
                        Model=Model.Meta.name,
                    ),
                ),
            )
            view_args['Model'] = Model
