import sform

from staff.preprofile.models import PersonAdoptApplication, STATUS
from staff.preprofile.repository import Repository
from staff.rfid.exceptions import DoesNotExist
from staff.rfid.constants import OWNER
from staff.rfid.controllers import Badges


class PersonForm(sform.SForm):
    login = sform.CharField(state=sform.REQUIRED)
    rfid_code = sform.CharField(max_length=128, state=sform.REQUIRED)


class AdoptForm(sform.SForm):
    persons = sform.GridField(sform.FieldsetField(PersonForm))

    def clean(self):
        result = []
        error_dict = {}

        def update_errors(idx, field, message, code):
            error_dict[('persons', idx, field)] = [sform.ValidationError(message, code=code)]

        for index, person in enumerate(self.cleaned_data['persons']):
            login = person['login']
            rfid_code = person['rfid_code']

            if self._login_already_used_for_adoption(login):
                update_errors(
                    index,
                    'login',
                    'Login already used for adoption',
                    'login_already_used',
                )
                continue

            if self._rfid_code_already_used_for_adoption(rfid_code):
                update_errors(
                    index,
                    'rfid_code',
                    'Wrong rfid code',
                    'wrong_rfid_code',
                )
                continue

            if not self._candidate_with_rfid_exists(rfid_code):
                update_errors(
                    index,
                    'rfid_code',
                    'Wrong rfid code',
                    'wrong_rfid_code',
                )
                continue

            if not self._preprofile_can_be_adopted(rfid_code):
                update_errors(
                    index,
                    'rfid_code',
                    'Preprofile for this rfid cannot be adopted at this time',
                    'wrong_rfid_code',
                )
                continue

            result.append(person)

        self._errors = error_dict
        cleaned_data = {'persons': result}
        return cleaned_data

    def _login_already_used_for_adoption(self, login):
        return (
            PersonAdoptApplication.objects
            .filter(login=login, status__in=[STATUS.NEW, STATUS.FAILED])
            .exists()
        )

    def _rfid_code_already_used_for_adoption(self, rfid_code):
        return PersonAdoptApplication.objects.filter(rfid_code=rfid_code).exists()

    def _candidate_with_rfid_exists(self, rfid_code):
        try:
            Badges().get(rfid__code=rfid_code, owner=OWNER.CANDIDATE)
            return True
        except DoesNotExist:
            return False

    def _preprofile_can_be_adopted(self, rfid_code):
        badge = Badges().get(rfid__code=rfid_code, owner=OWNER.CANDIDATE)
        controller = Repository(self.base_initial['adopter']).existing(badge.preprofile_id)
        return controller.can_be_adopted()
