import logging

from flask import request, flash, abort, Response

from flask_admin import expose
from flask_admin.babel import gettext, ngettext, lazy_gettext
from flask_admin.model import BaseModelView
from flask_admin.model.form import wrap_fields_in_fieldlist
from flask_admin.model.fields import ListEditableFieldList
from flask_admin._compat import iteritems, string_types

import mongoengine
import gridfs
from mongoengine.connection import get_db
from bson.objectid import ObjectId

from flask_admin.actions import action
from .filters import FilterConverter, BaseMongoEngineFilter
from .form import get_form, CustomModelConverter
from .typefmt import DEFAULT_FORMATTERS
from .tools import parse_like_term
from .helpers import format_error
from .ajax import process_ajax_references, create_ajax_loader
from .subdoc import convert_subdocuments

# Set up logger
log = logging.getLogger("flask-admin.mongo")


SORTABLE_FIELDS = set((
    mongoengine.StringField,
    mongoengine.IntField,
    mongoengine.FloatField,
    mongoengine.BooleanField,
    mongoengine.DateTimeField,
    mongoengine.ComplexDateTimeField,
    mongoengine.ObjectIdField,
    mongoengine.DecimalField,
    mongoengine.ReferenceField,
    mongoengine.EmailField,
    mongoengine.UUIDField,
    mongoengine.URLField
))


class ModelView(BaseModelView):
    """
        MongoEngine model scaffolding.
    """

    column_filters = None
    """
        Collection of the column filters.

        Can contain either field names or instances of
        :class:`flask_admin.contrib.mongoengine.filters.BaseMongoEngineFilter`
        classes.

        Filters will be grouped by name when displayed in the drop-down.

        For example::

            class MyModelView(BaseModelView):
                column_filters = ('user', 'email')

        or::

            from flask_admin.contrib.mongoengine.filters import BooleanEqualFilter

            class MyModelView(BaseModelView):
                column_filters = (BooleanEqualFilter(column=User.name, name='Name'),)

        or::

            from flask_admin.contrib.mongoengine.filters import BaseMongoEngineFilter

            class FilterLastNameBrown(BaseMongoEngineFilter):
                def apply(self, query, value):
                    if value == '1':
                        return query.filter(self.column == "Brown")
                    else:
                        return query.filter(self.column != "Brown")

                def operation(self):
                    return 'is Brown'

            class MyModelView(BaseModelView):
                column_filters = [
                    FilterLastNameBrown(
                        column=User.last_name, name='Last Name',
                        options=(('1', 'Yes'), ('0', 'No'))
                    )
                ]
    """

    model_form_converter = CustomModelConverter
    """
        Model form conversion class. Use this to implement custom
        field conversion logic.

        Custom class should be derived from the
        `flask_admin.contrib.mongoengine.form.CustomModelConverter`.

        For example::

            class MyModelConverter(AdminModelConverter):
                pass


            class MyAdminView(ModelView):
                model_form_converter = MyModelConverter
    """

    object_id_converter = ObjectId
    """
        Mongodb ``_id`` value conversion function. Default is `bson.ObjectId`.
        Use this if you are using String, Binary and etc.

        For example::

            class MyModelView(BaseModelView):
                object_id_converter = int

        or::

            class MyModelView(BaseModelView):
                object_id_converter = str
    """

    filter_converter = FilterConverter()
    """
        Field to filter converter.

        Override this attribute to use a non-default converter.
    """

    column_type_formatters = DEFAULT_FORMATTERS
    """
        Customized type formatters for MongoEngine backend
    """

    allowed_search_types = (mongoengine.StringField,
                            mongoengine.URLField,
                            mongoengine.EmailField)
    """
        List of allowed search field types.
    """

    form_subdocuments = None
    """
        Subdocument configuration options.

        This field accepts dictionary, where key is field name and value is either dictionary or instance of the
        `flask_admin.contrib.EmbeddedForm`.

        Consider following example::

            class Comment(db.EmbeddedDocument):
                name = db.StringField(max_length=20, required=True)
                value = db.StringField(max_length=20)