import datetime
import logging

from staff.lib.forms.staff_form_grid import StaffFormGrid
from staff.lib.utils.qs_values import extract_related
from staff.person.models import Staff

from staff.map.edit.objects import TableCtl
from staff.map.errors import (
    item_does_not_exist_by_id,
    MultipleTableError,
    TableError,
)
from staff.map.forms.table import (
    BookTableForm,
    OccupyTableForm,
    ReserveTableForm,
)
from staff.map.models import Table


logger = logging.getLogger(__name__)

TABLE_FIELDS = ('num', 'coord_x', 'coord_y', 'floor', 'floor__office')


def get_initial(table_id=None):
    if table_id is None:
        return {}

    with item_does_not_exist_by_id(Table)(
            logger=logger, message_params=[table_id], raise_e=TableError):
        table = (
            Table.objects.values(*TABLE_FIELDS)
            .get(id=table_id, intranet_status=1)
        )
        table.update(extract_related(table, 'floor_'))
        return table


def update_table(table_id, data, author):
    with item_does_not_exist_by_id(Table)(
            logger=logger, message_params=[table_id], raise_e=TableError):
        table = Table.objects.get(id=table_id)

    try:
        ctl = TableCtl(table, author)
        ctl.modify(data)
    except Exception:
        logger.exception('Error trying to update table with id %s, data:', table_id, data)
        raise TableError('cannot-update-table')


def create_table(data, author):
    try:
        return TableCtl.create(data, author)
    except Exception:
        logger.exception('Error trying to create table. Data: %s', data)
        raise TableError('cannot-create-table')


def delete_table(table_id, author):

    with item_does_not_exist_by_id(Table)(
            logger=logger, message_params=[table_id], raise_e=TableError):
        table = Table.objects.get(id=table_id, intranet_status=1)

    try:
        return TableCtl(table, author).delete()
    except Exception:
        logger.exception('Error trying to delete table.')
        raise TableError('cannot-delete-table')


def occupy(table_id, occupation_data, author):
    with item_does_not_exist_by_id(Table)(logger=logger, message_params=[table_id], raise_e=TableError):
        table = Table.objects.get(id=table_id, intranet_status=1)

    table_ctl = TableCtl(table, author)

    def legacy_grid_cleaned_data(cleaned_data):
        return dict(enumerate(cleaned_data))

    try:
        reserve_result = table_ctl.reserve(legacy_grid_cleaned_data(occupation_data['reserve'].cleaned_data))
        occupy_result = table_ctl.occupy(legacy_grid_cleaned_data(occupation_data['occupation'].cleaned_data))
        book_result = table_ctl.book(legacy_grid_cleaned_data(occupation_data['book'].cleaned_data))
    except MultipleTableError:
        raise
    except Exception:
        logger.exception('Error while occupating table %s, data:%s', table_id, occupation_data)
        raise TableError('cannot-occupy-table')
    return {
        'occupied': occupy_result,
        'reserved': reserve_result,
        'booked': book_result,
        'id': table_ctl.table.id,
        'table_num': table_ctl.table.num
    }


def book_table_for_person_controller(
    person: Staff,
    table: Table,
    date_from: datetime.date,
    date_to: datetime.date,
) -> dict:
    table_ctl = TableCtl(table)
    occupied_by = list(table_ctl.occupied_by.values_list('login', flat=True))

    if occupied_by:
        raise MultipleTableError([{'code': 'reserved', 'occupied_by': occupied_by}])

    if person.table and person.table.floor.office == table.floor.office:
        raise MultipleTableError([{'code': 'has_occupation_in_office', 'occupied': person.table.num}])

    return table_ctl.add_booking(person, date_from, date_to)


def get_initial_occupy(table_id):
    with item_does_not_exist_by_id(Table)(
            logger=logger, message_params=[table_id], raise_e=TableError):
        table = Table.objects.get(id=table_id, intranet_status=1)

    def person_caption(p):
        return {
            'first_name': p.first_name,
            'last_name': p.last_name
        }
    table_ctl = TableCtl(table)

    reserve_data, occupation_data, book_data = [{}], [{}], [{}]

    reserved_for = table_ctl.reserved_for
    if reserved_for:
        reserve_data = [
            {
                'person': reserve.staff,
                'description': reserve.description,
                'person_caption': person_caption(reserve.staff),
            }
            for reserve in reserved_for
        ]

    occupied_by = table_ctl.occupied_by
    if occupied_by:
        occupation_data = [
            {
                'person': person,
                'person_caption': person_caption(person),
            } for person in occupied_by
        ]

    booked_for = table_ctl.booked_for
    if booked_for:
        book_data = [
            {
                'person': book.staff,
                'description': book.description,
                'date_from': book.date_from,
                'date_to': book.date_to,
                'person_caption': person_caption(book.staff),
            } for book in booked_for
        ]

    return {
        'reserve': StaffFormGrid(ReserveTableForm, initial=reserve_data).as_front_dict(),
        'occupation': StaffFormGrid(OccupyTableForm, initial=occupation_data).as_front_dict(),
        'book': StaffFormGrid(BookTableForm, initial=book_data).as_front_dict(),
    }
