# -*- coding: utf-8 -*-
#
from __future__ import unicode_literals

import datetime
from collections import OrderedDict

from django.core.exceptions import PermissionDenied
from django.http import Http404
from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _
from rest_framework import exceptions, serializers
from rest_framework.fields import empty
from rest_framework.metadata import SimpleMetadata
from rest_framework.request import clone_request

from common.serializers.fields import TreeChoicesField


class SimpleMetadataWithFilters(SimpleMetadata):
    """Override SimpleMetadata, adding info about filters"""

    methods = {"PUT", "POST", "GET", "PATCH"}
    attrs = [
        "read_only", "label", "help_text",
        "min_length", "max_length", "min_value",
        "max_value", "write_only",
    ]

    def determine_actions(self, request, view):
        """
        For generic class based views we return information about
        the fields that are accepted for 'PUT' and 'POST' methods.
        """
        actions = {}
        view.raw_action = getattr(view, "action", None)
        query_action = request.query_params.get("action", None)
        for method in self.methods & set(view.allowed_methods):
            if hasattr(view, "action_map"):
                view.action = view.action_map.get(method.lower(), view.action)

            if query_action and query_action.lower() != method.lower():
                continue

            view.request = clone_request(request, method)
            try:
                # Test global permissions
                if hasattr(view, "check_permissions"):
                    view.check_permissions(view.request)
                # Test object permissions
                if method == "PUT" and hasattr(view, "get_object"):
                    view.get_object()
            except (exceptions.APIException, PermissionDenied, Http404):
                pass
            else:
                # If user has appropriate permissions for the view, include
                # appropriate metadata about the fields that should be supplied.
                serializer = view.get_serializer()
                actions[method] = self.get_serializer_info(serializer)
            finally:
                view.request = request
        return actions

    def get_field_type(self, field):
        """
        Given a field, return a string representing the type of the field.
        """
        tp = self.label_lookup[field]

        class_name = field.__class__.__name__
        if class_name == "LabeledChoiceField":
            tp = "labeled_choice"
        elif class_name == "JSONField":
            tp = 'json'
        elif class_name == "ObjectRelatedField":
            tp = "object_related_field"
        elif class_name == "ManyRelatedField":
            child_relation_class_name = field.child_relation.__class__.__name__
            if child_relation_class_name == "ObjectRelatedField":
                tp = "m2m_related_field"
        return tp

    @staticmethod
    def set_choices_field(field, field_info):
        field_info["choices"] = [
            {
                "value": choice_value,
                "label": force_str(choice_label, strings_only=True),
            }
            for choice_value, choice_label in dict(field.choices).items()
        ]

    @staticmethod
    def set_tree_field(field, field_info):
        field_info["tree"] = field.tree
        field_info["type"] = "tree"

    @staticmethod
    def set_style_field(field, field_info):
        style = getattr(field, "style", None)
        if not style:
            return

        if isinstance(style, dict):
            if style.get("base_template") == "textarea.html":
                field_info["style"] = "textarea"
            elif style:
                field_info["style"] = style
            return

        field_info["style"] = force_str(style, strings_only=True)

    def get_field_info(self, field):
        """
        Given an instance of a serializer field, return a dictionary
        of metadata about it.
        """
        field_info = OrderedDict()
        field_info["type"] = self.get_field_type(field)
        field_info["required"] = getattr(field, "required", False)

        # Default value
        default = getattr(field, "default", None)
        if default is not None and default != empty:
            if isinstance(default, (str, int, bool, float, datetime.datetime, list)):
                field_info["default"] = default

        for attr in self.attrs:
            value = getattr(field, attr, None)
            if value is not None and value != "":
                field_info[attr] = force_str(value, strings_only=True)

        self.set_style_field(field, field_info)

        if getattr(field, "child", None):
            field_info["child"] = self.get_field_info(field.child)
        elif getattr(field, "fields", None):
            field_info["children"] = self.get_serializer_info(field)

        if isinstance(field, TreeChoicesField):
            self.set_tree_field(field, field_info)
        elif isinstance(field, serializers.ChoiceField):
            self.set_choices_field(field, field_info)

        if field.field_name == 'id':
            field_info['label'] = 'ID'
        if field.field_name == 'org_id':
            field_info['label'] = _('Org ID')

        return field_info

    @staticmethod
    def get_filters_fields(request, view):
        fields = []
        if hasattr(view, "get_filter_fields"):
            fields = view.get_filter_fields(request)
        elif hasattr(view, "filter_fields"):
            fields = view.filter_fields
        elif hasattr(view, "filterset_fields"):
            fields = view.filterset_fields
        elif hasattr(view, "get_filterset_fields"):
            fields = view.get_filterset_fields(request)
        elif hasattr(view, "filterset_class"):
            fields = list(view.filterset_class.Meta.fields) + list(
                view.filterset_class.declared_filters.keys()
            )

        if hasattr(view, "custom_filter_fields"):
            # 不能写 fields += view.custom_filter_fields
            # 会改变 view 的 filter_fields
            fields = list(fields) + list(view.custom_filter_fields)

        if isinstance(fields, dict):
            fields = list(fields.keys())
        return fields

    @staticmethod
    def get_ordering_fields(request, view):
        fields = []
        if hasattr(view, "get_ordering_fields"):
            fields = view.get_ordering_fields(request)
        elif hasattr(view, "ordering_fields"):
            fields = view.ordering_fields
        return fields

    def determine_metadata(self, request, view):
        metadata = super(SimpleMetadataWithFilters, self).determine_metadata(
            request, view
        )
        filterset_fields = self.get_filters_fields(request, view)
        order_fields = self.get_ordering_fields(request, view)

        meta_get = metadata.get("actions", {}).get("GET", {})
        for k, v in meta_get.items():
            if k in filterset_fields:
                v["filter"] = True
            if k in order_fields:
                v["order"] = True
        return metadata
