from django.utils.translation import ugettext as _
from django.db.models import Q
from django.db.models.functions import Length

from staff.lib.models.base import get_i_field

from staff.person.models import Staff
from staff.groups.models import Group, GROUP_TYPE_CHOICES
from staff.departments.models import Department
from staff.map.models import (
    Table,
    Room,
    TableReserve,
    Office,
    Device,
    ROOM_TYPES,
    DEVICE_TYPES,
    TableBook,
)
from staff.oebs.models import Job

from staff.maillists.models import List

from staff.multic.resources import SearchResource
from staff.umbrellas.models import Umbrella


class StaffResource(SearchResource):
    name = 'staff'
    queryset = Staff.objects.filter(is_dismissed=False)

    required_fields = ('login', 'first_name', 'last_name', 'work_phone', 'department__name', 'work_email')
    search_conditions = (
        'login__istartswith',
        'first_name__istartswith',
        'last_name__istartswith',
        'first_name_en__istartswith',
        'last_name_en__istartswith',
    )
    extra_fields = {
        '_id': {
            'required_fields': ['id'],
        },
        '_text': {
            'required_fields': ['first_name', 'last_name', 'first_name_en', 'last_name_en'],
        },
        'first_name': {
            'required_fields': ['first_name', 'first_name_en', 'native_lang'],
        },
        'last_name': {
            'required_fields': ['last_name', 'last_name_en', 'native_lang'],
        },
        'department__name': {
            'required_fields': ['department__name', 'department__name_en',
                                'department__native_lang'],
        },
        'office_id': {
            'required_fields': ['office_id'],
        },
        'office__city_id': {
            'required_fields': ['office__city_id'],
        }
    }

    def extra__text(self, obj):
        return ' '.join([self.extra_first_name(obj), self.extra_last_name(obj)])

    def extra_first_name(self, obj):
        return get_i_field(obj, 'first_name')

    def extra_last_name(self, obj):
        return get_i_field(obj, 'last_name')

    def extra_department__name(self, obj):
        return get_i_field(obj, 'name', 'department__')

    def extra_office_id(self, obj):
        return obj['office_id']

    def extra_office__city_id(self, obj):
        return obj['office__city_id']


class MaillistResource(SearchResource):
    name = 'maillist'
    queryset = List.objects.all()

    required_fields = ('name', 'email')
    search_conditions = ('name__istartswith', 'email__istartswith')
    extra_fields = {
        '_id': {
            'required_fields': ['id'],
        },
        '_text': {
            'required_fields': ['name'],
        },
    }

    def extra__text(self, obj):
        return str(obj['email'])


class DepartmentResource(SearchResource):
    name = 'department'
    queryset = Department.objects.active()

    required_fields = ('id', 'name', 'bg_color', 'ancestors', 'url')
    search_conditions = ('name__icontains', 'name_en__icontains', 'url__icontains')
    extra_fields = {
        '_id': {'required_fields': ['id']},
        '_text': {'required_fields': ['name']},
        'ancestors': {
            'required_fields': ['lft', 'rght', 'tree_id'],
            'fields': ['id', 'name', 'name_en', 'native_lang'],
        },
        'name': {'required_fields': ['name', 'name_en', 'native_lang']},
    }

    def extra__text(self, obj):
        return str(self.extra_name(obj))

    def extra_name(self, obj):
        return get_i_field(obj, 'name')

    def extra_ancestors(self, obj):
        ancestors = super(DepartmentResource, self).extra_ancestors(obj)
        for ancestor in ancestors:
            ancestor['name'] = get_i_field(ancestor, 'name')
            del ancestor['name_en']
            del ancestor['native_lang']
        return ancestors

    def search(self, queryset):
        query = self.q.strip()

        if queryset.filter(Q(name__iexact=query) | Q(name_en__iexact=query)).exists():
            return (
                queryset
                .filter(Q(name__iexact=query) | Q(name_en__iexact=query))
            )

        full_search_query = Q(name__icontains=query) | Q(name_en__icontains=query) | self.search_word(query)

        return (
            queryset
            .filter(full_search_query)
            .order_by('level')
        )


class ValueStreamResource(DepartmentResource):
    name = 'value_stream'
    required_fields = ('id', 'name', 'ancestors', 'url')
    queryset = Department.valuestreams.active()


class MainProductResource(ValueStreamResource):
    name = 'main_product'
    queryset = ValueStreamResource.queryset.filter(hrproduct__intranet_status=1)


class UmbrellaResource(SearchResource):
    name = 'umbrella'
    required_fields = ('id', 'name', 'issue_key', 'goal_id')
    search_conditions = ('name__icontains', 'issue_key__icontains')
    filter_fields = ('value_stream__url',)
    queryset = Umbrella.objects.active()

    extra_fields = {
        '_id': {
            'required_fields': ['id'],
        },
        '_text': {
            'required_fields': ['name'],
        },
    }

    def extra__text(self, obj):
        return str(obj['name'])


class GeographyResource(DepartmentResource):
    name = 'geography'
    required_fields = ('id', 'name', 'ancestors', 'url')
    queryset = Department.geography.active()


class OebsGeographyResource(DepartmentResource):
    name = 'oebs_geography'
    required_fields = ('id', 'name', 'ancestors', 'url')
    queryset = Department.geography.active().exclude(geography_instance__oebs_code=None)


class GroupResource(SearchResource):
    name = 'group'
    queryset = Group.objects.active()

    required_fields = ('id', 'name')
    search_conditions = ('name__icontains', 'url__icontains')
    extra_fields = {
        '_id': {
            'required_fields': ['id'],
        },
        '_text': {
            'required_fields': ['name'],
        },
        'ancestors': {
            'required_fields': ['lft', 'rght', 'tree_id'],
            'fields': ['id', 'name'],
        }
    }

    def extra__text(self, obj):
        return str(obj['name'])


class DepartmentGroupResource(GroupResource):
    name = 'departmentgroup'
    queryset = GroupResource.queryset.filter(type=GROUP_TYPE_CHOICES.DEPARTMENT)


class ServiceGroupResource(GroupResource):
    name = 'servicegroup'
    queryset = GroupResource.queryset.filter(type=GROUP_TYPE_CHOICES.SERVICE)


class IntranetGroupResource(GroupResource):
    name = 'intranetgroup'
    queryset = GroupResource.queryset.filter(type=GROUP_TYPE_CHOICES.WIKI)


class TableResource(SearchResource):
    name = 'table'
    queryset = Table.objects.active()

    required_fields = ('id', 'floor__office__name', 'name', 'status_display', 'floor_display', 'floor__name')
    search_conditions = {
        str: (
            'staff__login__istartswith',
            'staff__first_name__istartswith',
            'staff__last_name__istartswith',
            'staff__first_name_en__istartswith',
            'staff__last_name_en__istartswith',
        ),
        int: ('id',)
    }

    extra_fields = {
        '_id': {
            'required_fields': ['id'],
        },
        '_text': {
            'required_fields': ['id'],
        },
        'num': {
            'required_fields': ['id'],
        },
        'status_display': {
            'required_fields': ['id'],
        },
        'floor_display': {
            'required_fields': ['floor__num'],
        },
        'name': {
            'required_fields': ['id'],
        },
        'floor__office__name': {
            'required_fields': ['floor__office__name', 'floor__office__name_en', 'floor__office__native_lang'],
        }
    }
    extra_methods_order = (
        '_id',
        '_text',
        'name',
        'occupants_display',
        'status_display',
        'floor_display',
        'floor__office__name',
    )
    fields_post_rename = {
        'floor__office__name': 'office_name',
    }

    def extra__text(self, obj):
        return str(obj['id'])

    def extra_num(self, obj):
        return obj['id']

    def extra_status_display(self, obj):
        """Стол свободен/сидит такой-то"""
        occupants_string = self._build_occupants_string(obj)
        if occupants_string:
            return occupants_string
        else:
            return _('table-free')

    def _build_occupants_string(self, obj):
        """Строка про оккупантов стола"""
        occupants = Staff.objects.filter(table_id=obj['id'])
        occupants = list(map(str, occupants))

        if not occupants:
            occupants_str = ''
        elif len(occupants) == 1:
            occupants_str = occupants[0]
        else:
            first_occupants, last_occupant = occupants[:-1], occupants[-1]
            occupants_str = ', '.join(first_occupants)
            occupants_str += ' ' + _('and') + ' ' + last_occupant

        return occupants_str

    def extra_floor_display(self, obj):
        if obj['floor__num'] is None:
            return _('floor')
        else:
            return '%d %s' % (obj['floor__num'], _('floor'))

    def extra_name(self, obj):
        return '%s %d' % (_('table'), obj['id'])

    def extra_floor__office__name(self, obj):
        return get_i_field(obj, 'name', 'floor__office__')


class TableCanBeBookedResource(SearchResource):
    name = 'table_can_be_booked'
    queryset = (
        Table.objects
        .active()
        .filter(staff=None)
    )

    required_fields = ('id', 'floor__office__name', 'name', 'floor_display', 'floor__name')
    search_conditions = {
        int: ('id',)
    }

    extra_fields = {
        '_id': {
            'required_fields': ['id'],
        },
        '_text': {
            'required_fields': ['id'],
        },
        'num': {
            'required_fields': ['id'],
        },
        'floor_display': {
            'required_fields': ['floor__num'],
        },
        'name': {
            'required_fields': ['id'],
        },
        'floor__office__name': {
            'required_fields': ['floor__office__name', 'floor__office__name_en', 'floor__office__native_lang'],
        }
    }
    extra_methods_order = (
        '_id',
        '_text',
        'name',
        'floor_display',
        'floor__office__name',
    )
    fields_post_rename = {
        'floor__office__name': 'office_name',
    }

    filter_fields = ['floor__office__name', 'room_id', 'floor__office__id']

    def search(self, queryset):
        """
        Фильтрация по поисковой строке.
        Если отсутствуют date_from и date_to то ищем только среди вообще никогда не забронированных
        Если присутствют, ищем только среди столов для которых нет броней в этом интервале
        """
        queryset = super().search(queryset)
        query = Q()
        if not self.filters:
            return queryset.filter(table_book=None)

        if 'date_from' in self.filters:
            query &= Q(date_to__gte=self.filters['date_from'])

        if 'date_to' in self.filters:
            query &= Q(date_from__lte=self.filters['date_to'])

        busy_table_ids = (
            TableBook.objects
            .filter(query)
            .values_list('table_id', flat=True)
        )

        return queryset.exclude(id__in=busy_table_ids)

    def extra__text(self, obj):
        return str(obj['id'])

    def extra_num(self, obj):
        return obj['id']

    def extra_floor_display(self, obj):
        if obj['floor__num'] is None:
            return _('floor')
        else:
            return '%d %s' % (obj['floor__num'], _('floor'))

    def extra_name(self, obj):
        return '%s %d' % (_('table'), obj['id'])

    def extra_floor__office__name(self, obj):
        return get_i_field(obj, 'name', 'floor__office__')


class TableReserveResource(SearchResource):
    name = 'tablereserve'
    queryset = TableReserve.objects.all()

    required_fields = (
        'id',
        'staff__first_name',
        'staff__last_name',
        'staff__login',
        'department__name',
        'table_id',
        'name',
        'status_display',
        'table__floor__office__name',
        'floor_display',
    )

    search_conditions = {
        str: (
            'staff__login__istartswith',
            'staff__first_name__istartswith',
            'staff__last_name__istartswith',
            'staff__first_name_en__istartswith',
            'staff__last_name_en__istartswith',
            'department__name__istartswith',
            'department__name_en__istartswith',
        ),
        int: ('table_id',)
    }
    fields_post_rename = {'table__floor__office__name': 'office_name'}

    extra_fields = {
        '_id': {
            'required_fields': ['id'],
        },
        '_text': {
            'required_fields': ['table_id'],
        },
        'status_display': {
            'required_fields': ['staff__first_name', 'staff__last_name', 'department__name'],
        },
        'floor_display': {
            'required_fields': ['table__floor__num'],
        },
        'name': {
            'required_fields': ['table_id'],
        },
        'staff__first_name': {
            'required_fields': ['staff__first_name', 'staff__first_name_en', 'staff__native_lang'],
        },
        'staff__last_name': {
            'required_fields': ['staff__last_name', 'staff__last_name_en', 'staff__native_lang'],
        },
        'department__name': {
            'required_fields': ['department__name', 'department__name_en', 'department__native_lang'],
        },
        'table__floor__office__name': {
            'required_fields': [
                'table__floor__office__name',
                'table__floor__office__name_en',
                'table__floor__office__native_lang',
            ],
        },
    }

    def extra__text(self, obj):
        return str(obj['table_id'])

    def extra_status_display(self, obj):
        occupants_string = self._build_occupants_string(obj)
        return _('table-reserved') + ': ' + occupants_string

    def extra_floor_display(self, obj):
        if obj['table__floor__num'] is None:
            return _('floor')
        else:
            return '%d %s' % (obj['table__floor__num'], _('floor'))

    def _build_occupants_string(self, obj):
        """Строка про резервы стола"""
        first_name = self.extra_staff__first_name(obj)
        last_name = self.extra_staff__last_name(obj)
        department__name = self.extra_department__name(obj)

        if first_name and last_name:
            return '%s %s' % (first_name, last_name)
        elif department__name:
            return department__name
        else:
            return ''

    def extra_name(self, obj):
        return '%s %d' % (_('table'), obj['table_id'])

    def extra_staff__first_name(self, obj):
        return get_i_field(obj, 'first_name', 'staff__')

    def extra_staff__last_name(self, obj):
        return get_i_field(obj, 'last_name', 'staff__')

    def extra_department__name(self, obj):
        return get_i_field(obj, 'name', 'department__')

    def extra_table__floor__office__name(self, obj):
        return get_i_field(obj, 'name', 'table__floor__office__')


class NewRoomResource(SearchResource):
    name = 'new_room'
    queryset = Room.objects.active().all()

    required_fields = (
        'id',
        'num',
        'name',
        'type_display',
        'name_alternative',
        'floor__name',
        'floor__office__name',
        'floor_display',
        'room_type',
    )
    search_conditions = {
        str: ('name__icontains', 'name_exchange__icontains', 'name_alternative__icontains'),
        int: ('num',)
    }

    extra_fields = {
        '_id': {
            'required_fields': ['id'],
        },
        '_text': {
            'required_fields': ['num', 'name'],
        },
        'floor_display': {
            'required_fields': ['floor__num'],
        },
        'type_display': {
            'required_fields': ['room_type'],
        },
        'floor__office__name': {
            'required_fields': ['floor__office__name', 'floor__office__name_en', 'floor__office__native_lang'],
        }
    }
    fields_post_rename = {
        'floor__office__name': 'office_name',
    }

    filter_fields = ['floor__office__name', 'room_type', 'floor__office__id']

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        if self.filters is not None and 'room_type' in self.filters:
            room_type = self.filters['room_type']
            room_types_dict = ROOM_TYPES.as_dict()
            if room_type in room_types_dict:
                self.filters['room_type'] = room_types_dict[room_type]

    def extra__text(self, obj):
        return obj['name']

    def extra_type_display(self, obj):
        return str(ROOM_TYPES[obj['room_type']])

    def extra_floor_display(self, obj):
        if obj['floor__num'] is None:
            return _('floor')
        else:
            return '%d %s' % (obj['floor__num'], _('floor'))

    def extra_floor__office__name(self, obj):
        return get_i_field(obj, 'name', 'floor__office__')


class RoomResource(SearchResource):
    name = 'room'
    queryset = Room.objects.active().exclude(room_type=ROOM_TYPES.COWORKING)

    required_fields = (
        'id',
        'num',
        'name',
        'type_display',
        'name_alternative',
        'floor__name',
        'floor__office__name',
        'floor_display',
        'room_type',
    )
    search_conditions = {
        str: ('name__icontains', 'name_exchange__icontains', 'name_alternative__icontains'),
        int: ('num',)
    }

    extra_fields = {
        '_id': {
            'required_fields': ['id'],
        },
        '_text': {
            'required_fields': ['num', 'name'],
        },
        'floor_display': {
            'required_fields': ['floor__num'],
        },
        'type_display': {
            'required_fields': ['room_type'],
        },
        'floor__office__name': {
            'required_fields': ['floor__office__name', 'floor__office__name_en', 'floor__office__native_lang'],
        }
    }
    fields_post_rename = {
        'floor__office__name': 'office_name',
    }

    def extra__text(self, obj):
        return obj['name']

    def extra_type_display(self, obj):
        return str(ROOM_TYPES[obj['room_type']])

    def extra_floor_display(self, obj):
        if obj['floor__num'] is None:
            return _('floor')
        else:
            return '%d %s' % (obj['floor__num'], _('floor'))

    def extra_floor__office__name(self, obj):
        return get_i_field(obj, 'name', 'floor__office__')


class CoworkingRoomResource(RoomResource):
    name = 'coworking_room'
    queryset = Room.objects.active().filter(room_type=ROOM_TYPES.COWORKING)


class DeviceResource(SearchResource):
    name = 'equipment'
    queryset = Device.objects.active_without_security_devices()

    required_fields = ('name', 'name_dns', 'floor__name', 'floor__office__name', 'floor_display', 'type_display')
    search_conditions = ('name__istartswith', 'name_dns__icontains')

    extra_fields = {
        '_id': {
            'required_fields': ['id'],
        },
        '_text': {
            'required_fields': ['name'],
        },
        'floor_display': {
            'required_fields': ['floor__num'],
        },
        'type_display': {
            'required_fields': ['type'],
        },
        'floor__office__name': {
            'required_fields': ['floor__office__name', 'floor__office__name_en', 'floor__office__native_lang'],
        }
    }
    fields_post_rename = {
        'floor__office__name': 'office_name',
    }

    def extra__text(self, obj):
        return str(obj['name'])

    def extra_type_display(self, obj):
        return str(DEVICE_TYPES[obj['type']])

    def extra_floor_display(self, obj):
        if obj['floor__num'] is None:
            return _('floor')
        else:
            return '%d %s' % (obj['floor__num'], _('floor'))

    def extra_floor__office__name(self, obj):
        return get_i_field(obj, 'name', 'floor__office__')


class OfficeResource(SearchResource):
    name = 'office'
    queryset = Office.objects.active()

    required_fields = ('id', 'name',)
    search_conditions = {
        str: (
            'name__icontains',
            'name_en__icontains',
        ),
    }
    extra_fields = {
        '_id': {
            'required_fields': ['id'],
        },
        '_text': {
            'required_fields': ['name', 'name_en', 'native_lang'],
        },
        'city__name': {
            'required_fields': ['city__name', 'city__name_en', 'city__native_lang'],
        },
        'city__country__name': {
            'required_fields': ['city__country__name', 'city__country__name_en', 'city__country__native_lang'],
        },
        'name': {
            'required_fields': ['name', 'name_en', 'native_lang'],
        },
    }
    fields_post_rename = {
        'city__country__name': 'country_name',
    }

    def extra__text(self, obj):
        return self.extra_name(obj)

    def extra_name(self, obj):
        return get_i_field(obj, 'name')

    def extra_city__name(self, obj):
        return get_i_field(obj, 'name', 'city__')

    def extra_city__country__name(self, obj):
        return get_i_field(obj, 'name', 'city__country__')


class JobsResource(SearchResource):
    name = 'job'
    queryset = Job.objects.filter(is_deleted_from_oebs=False)
    required_fields = ('code',)

    search_conditions = {
        str: (
            'code__istartswith',
            'name__icontains',
            'name_en__icontains',
        ),
    }

    extra_fields = {
        '_id': {
            'required_fields': ['code'],
        },
        '_text': {
            'required_fields': ['name', 'name_en', ],
        },
    }
    fields_post_rename = {}

    @property
    def i_name(self):
        return 'name' + ('_en' if self.request.LANGUAGE_CODE != 'ru' else '')

    def extra__id(self, obj):
        return obj['code']

    def extra__text(self, obj):
        return self.extra_name(obj)

    def extra_name(self, obj):
        return obj[self.i_name]

    def search(self, queryset):
        return (
            super(JobsResource, self)
            .search(queryset)
            .order_by(Length(self.i_name).asc())
        )
