from itertools import chain
from datetime import datetime
from typing import Tuple

from django.contrib import admin
from django.conf import settings
from django import forms

from staff.lib.forms.fields import GroupField, StaffField, DepartmentField
from staff.lib.forms.widgets import AutocompleteWidget


class BetterThanVanillaCheckboxInput(forms.CheckboxInput):
    def value_from_datadict(self, *args, **kwargs):
        value = super(
            BetterThanVanillaCheckboxInput, self
        ).value_from_datadict(*args, **kwargs)

        if not isinstance(value, (str, bool)):
            value = bool(value)

        return value

    def render(self, name, value, attrs=None):
        if not isinstance(value, (str, bool)):
            value = bool(value)

        return super(
            BetterThanVanillaCheckboxInput, self
        ).render(name, value, attrs)


class BaseIntranetModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        try:
            initial = args[4]
        except IndexError:
            initial = kwargs.get('initial')

        if initial is not None:
            if 'native_lang' not in initial:
                initial['native_lang'] = 'ru'
            if 'intranet_status' not in initial:
                initial['intranet_status'] = 1

        super(BaseIntranetModelForm, self).__init__(*args, **kwargs)


class BaseIntranetAdmin(admin.ModelAdmin):
    form = BaseIntranetModelForm
    required_fields = []
    not_required_fields: Tuple[str, ...] = ()

    def save_model(self, request, obj, form, change):
        if hasattr(obj, 'from_staff_id'):
            obj.from_staff_id = 0

        now = datetime.now()
        if hasattr(obj, 'created_at') and obj.created_at is None:
            obj.created_at = now
        if hasattr(obj, 'modified_at'):
            obj.modified_at = now

        return super(BaseIntranetAdmin, self).save_model(
            request, obj, form, change
        )

    def get_form(self, *args, **kwargs):
        form = super(BaseIntranetAdmin, self).get_form(*args, **kwargs)

        if 'intranet_status' in form.base_fields:
            form.base_fields['intranet_status'] = forms.BooleanField(
                label='Активна',
                required=False,
                widget=BetterThanVanillaCheckboxInput
            )

        required_fields = set(self.required_fields)
        not_required_fields = set(self.not_required_fields)

        required_fields = required_fields - not_required_fields
        all_fields = required_fields | not_required_fields

        for name, field in form.base_fields.items():
            if name in all_fields:
                field.required = name in required_fields

        return form


def _deduplicate(iterable):
    result = []
    for x in iterable:
        if x not in result:
            result.append(x)
    return result


class AllStaffField(StaffField):
    widget = AutocompleteWidget(attrs={
        'class': 'js-autocomplete_staff',
        'onclick': "return {hidden: 'id', is_dismissed: 1}"
    })


class AutocompleteMixin(object):
    AUTOCOMPLE_FIELDS = {
        'staff_field': StaffField,
        'department_field': DepartmentField,
        'group_field': GroupField
    }

    def maybe_autocomplete_formfield(self, db_field):
        field_class = None

        staff_fields = getattr(self, 'staff_field', [])
        group_fields = getattr(self, 'group_field', [])
        department_fields = getattr(self, 'department_field', [])

        if db_field.name == staff_fields or db_field.name in staff_fields:
            if getattr(self, 'show_dismissed', False):
                field_class = AllStaffField
            else:
                field_class = StaffField
        if db_field.name == group_fields or db_field.name in group_fields:
            field_class = GroupField
        if db_field.name == department_fields or db_field.name in department_fields:
            field_class = DepartmentField

        if field_class:
            return field_class(required=False, label=db_field.verbose_name)

    class Media:
        AUTOCOMPLETE_JS_NAMES = ('staff', 'departments', 'groups')
        js = ('//js.static.yandex.net/jquery/1.3.2/_jquery.js', ) + tuple(
            f'//{settings.CENTER_MASTER}/media/js/_js-autocomplete_{field_name}.js'
            for field_name in AUTOCOMPLETE_JS_NAMES
        )


class StaffModelAdmin(BaseIntranetAdmin, AutocompleteMixin):
    """
    Промежуточный класс админки, для моделей, содержащих ForeignKey на
     - django_intranet_stuff.models.staff.Staff
     - django_intranet_staff.models.office.Office
     - django_intranet_staff.models.department.Department

    Добавляет поиск по имени/фамилии из Stuff,
        названию офиса и названию отдела.
    Добавляет фильтрацию по названию офиса и названиям отдела
    Добавляет в админку таких моделей
        кнопку перехода на редактирование соответствующего
        работника, офиса или отдела (шаблон admin/staff_change_form.html)
    """
    # Переопределяется в наследнике, если название другое
    staff_field = 'staff'
    show_dismissed = True

    office_field = 'office'
    department_field = 'department'
    group_field = 'group'

    # change_form_template = 'admin/staff_change_form.html'
    STAFF_SEARCH_FIELDS = [
        'login',
        'first_name',
        'last_name',
        'middle_name',
        'normal_login',
        'first_name_en',
        'last_name_en',
        'en_name',
        'uid',
        'guid',
        'budget_position__code',
        'login_passport',
    ]

    DEPARTMENT_SEARCH_FIELD = [
        'name',
        'name_en',
        'short_name',
        'short_name_en',
        'url'
    ]

    OFFICE_SEARCH_FIELDS = [
        'name',
        'name_en'
    ]

    search_fields = tuple()

    def __init__(self, *args, **kwargs):

        # if field_name in self.list_display:

        if self.staff_field in self.list_display:
            self._add_search(self.staff_field, self.STAFF_SEARCH_FIELDS)

        if self.department_field in self.list_display:
            self._add_search(
                self.department_field, self.DEPARTMENT_SEARCH_FIELD
            )

        if self.office_field in self.list_display:
            self._add_search(self.office_field, self.OFFICE_SEARCH_FIELDS)
            self._add_filter(self.office_field)

        self.list_select_related = True

        super(StaffModelAdmin, self).__init__(*args, **kwargs)

    def _add_search_fields(self, fields):
        if self.search_fields:
            self.search_fields = tuple(
                _deduplicate(chain(self.search_fields, fields))
            )
        else:
            self.search_fields = tuple(fields)

    def _add_filter(self, field):
        if self.list_filter:
            self.list_filter = self.list_filter + (field, )
        else:
            self.list_filter = (field, )

    def _add_search(self, field_name, subfields):
        search_fields = (
            '{field}__{subfield}'.format(
                field=field_name,
                subfield=subfield
            ) for subfield in subfields
        )
        self._add_search_fields(search_fields)

    def login(self, obj):
        staff_instance = getattr(obj, self.staff_field, None)
        return staff_instance.login if staff_instance else ''

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        autocomplete_field = self.maybe_autocomplete_formfield(db_field)
        if autocomplete_field is not None:
            return autocomplete_field

        return super(StaffModelAdmin, self).formfield_for_foreignkey(
            db_field, request, **kwargs)
