from staff.lib.exceptions import ErrorWithStatusCode

from staff.person.models import Staff

from staff.person.models import Bicycle
from staff.person_profile.errors import (
    log_does_not_exist_staff_login,
)

import logging
logger = logging.getLogger('person_profile.controllers.bibycles')


class BicyclesError(ErrorWithStatusCode):
    pass


def get_bicycles_initial(login):
    return (
        Bicycle.objects
        .values('id', 'plate', 'description')
        .filter(owner__login=login)
        .order_by('position')
    )


def update_bicycles(login, new_bicycles_data):
    with log_does_not_exist_staff_login(logger=logger, message_params=[login], raise_e=BicyclesError):
        staff_id = Staff.objects.values_list('id', flat=True).get(login=login)

    old_ids = set(
        Bicycle.objects
        .values_list('id', flat=True)
        .filter(owner__login=login)
    )

    new_plates = [d['plate'] for d in new_bicycles_data if not d['id']]
    if Bicycle.objects.filter(plate__in=new_plates).exclude(owner__login=login).exists():
        raise BicyclesError()

    for position, form_data in enumerate(new_bicycles_data):
        pk = form_data.get('id')
        bicycle_data = {
            'owner_id': staff_id,
            'plate': form_data['plate'],
            'description': form_data['description'],
            'position': position,
        }

        if pk is not None:
            bicycle_data['id'] = pk
            old_ids.discard(pk)

        try:
            Bicycle(**bicycle_data).save(
                force_insert=not bool(pk),
                force_update=bool(pk)
            )
        except Exception as e:
            logger.exception('Cannot save bicycle data: %s', bicycle_data)
            raise BicyclesError(e)

    if old_ids:
        for bicycle in Bicycle.objects.filter(id__in=old_ids):
            try:
                bicycle.delete()
            except Bicycle.DoesNotExist as e:
                logger.exception('There are no such bicycle: %s', bicycle)
                raise BicyclesError(e)
