from typing import List
import logging

from staff.lib.utils.qs_values import extract_related
from staff.lib.forms.staff_form_grid import StaffFormGrid
from staff.map.controllers.room_common import update_room_common, create_room_common, delete_room_common
from staff.person.models import Staff

from staff.map.models import Room, ROOM_TYPES
from staff.map.edit.objects import RoomCtl
from staff.map.errors import MapApiError, item_does_not_exist_by_id
from staff.map.forms.room import BindRoomForm


logger = logging.getLogger(__name__)

ROOM_FIELDS = (
    'num', 'room_type', 'name', 'name_en', 'additional',
    'coord_x', 'coord_y', 'floor', 'floor__office', 'geometry'
)


class RoomError(MapApiError):
    pass


class FloorError(MapApiError):
    pass


def _get_room_data(room_id):
    with item_does_not_exist_by_id(Room)(logger=logger, message_params=[room_id], raise_e=RoomError):
        room = (
            Room.objects.values(*ROOM_FIELDS)
            .exclude(room_type=ROOM_TYPES.CONFERENCE)
            .get(id=room_id, intranet_status=1)
        )
        room.update(extract_related(room, 'floor_'))
        return room


def _get_first_available_num(floor_id):
    # первый свободный num среди неудаленных
    default_value = ''
    existed_nums = (
        Room.objects.active()
        .filter(floor_id=floor_id)
        .exclude(num=None)
        .values_list('num', flat=True)
        .order_by('num')
    )
    supposed = 0
    try:
        for supposed, existed in enumerate(existed_nums, existed_nums[0]):
            if supposed != existed:
                return supposed
    except IndexError:
        return default_value
    return supposed + 1


def get_initial(room_id=None, floor_id=None):
    if room_id:
        return _get_room_data(room_id)
    else:
        return {'num': _get_first_available_num(floor_id)}


def update_room(room_id, data, author):
    with item_does_not_exist_by_id(Room)(logger=logger, message_params=[room_id], raise_e=RoomError):
        room = Room.objects.get(id=room_id, intranet_status=1)

    update_room_common(RoomCtl, RoomError, room, data, author)


def create_room(data, author) -> RoomCtl:
    data['description'] = data.pop('additional', '')
    return create_room_common(RoomCtl, RoomError, data, author)


def delete_room(room_id, author) -> RoomCtl:
    with item_does_not_exist_by_id(Room)(logger=logger, message_params=[room_id], raise_e=RoomError):
        room = Room.objects.get(id=room_id, intranet_status=1)

    return delete_room_common(RoomCtl, RoomError, room, author)


def get_bound_staff(room_id: int) -> List[dict]:
    """
    Получение списка людей, привязанных к комнате (но не к столу в этой комнате)
    """
    with item_does_not_exist_by_id(Room)(logger=logger, message_params=[room_id], raise_e=RoomError):
        room = Room.objects.get(id=room_id, intranet_status=1)

    bound_staff = Staff.objects.filter(room_id=room.id, table=None)
    data = [
        {
            'person': person,
            'person_caption': {
                'first_name': person.first_name,
                'last_name': person.last_name,
            },
        } for person in bound_staff
    ]

    return StaffFormGrid(BindRoomForm, initial=data).as_front_dict()
