import json

import sform

from constance import config
from django.db.models import Case, When, Q, Value, CharField, OuterRef, Subquery
from django.db.models.functions import Concat
from django.utils.translation import ugettext_lazy as _
from model_utils import Choices

from intranet.femida.src.candidates.choices import CANDIDATE_STATUSES
from intranet.femida.src.candidates.contacts import normalize_phone
from intranet.femida.src.candidates.filters.fields.base import (
    Condition,
    FieldFilter,
    register_filter,
)
from intranet.femida.src.candidates.filters.fields.base.equal import EqualFilter, Equal
from intranet.femida.src.candidates.filters.fields.base.range import DateRangeFilter
from intranet.femida.src.candidates.filters.fields.base.text import Contains
from intranet.femida.src.candidates.helpers import candidate_is_current_employee_subquery
from intranet.femida.src.candidates.models import (
    Candidate,
    CandidateContact,
    CandidateScoring,
    ScoringCategory,
)


CANDIDATE_EMPLOYEE_TYPES = Choices(
    ('current', _('candidate.employee_type.current')),
    ('former', _('candidate.employee_type.former')),
)


class CandidateStatusFilterForm(sform.SForm):

    is_active = sform.NullBooleanField(default=True)

    def clean(self):
        cleaned_data = super().clean()
        if cleaned_data['is_active']:
            return {'status': CANDIDATE_STATUSES.in_progress}
        return {'status': CANDIDATE_STATUSES.closed}


class CandidateScoringCondition(Condition):

    def get_annotations(self):
        scoring_category = self.cleaned_data.get('scoring_category')
        scoring_version_by_scoring_category = json.loads(config.ACTUAL_CANDIDATE_SCORING_VERSIONS)

        qs = self.filter.get_queryset()
        qs = qs.filter(
            candidate=OuterRef('id'),
            scoring_category=scoring_category,
            version=scoring_version_by_scoring_category.get(str(scoring_category.id)),
        )
        return {
            'scoring_value': Subquery(qs.values('scoring_value')),
        }

    def q(self):
        return Q(scoring_value__isnull=False)


class CandidateNameCondition(Condition):

    def get_annotations(self):
        return {
            'concatenated_name': Concat('first_name', Value(' '), 'last_name'),
            'concatenated_name_reversed': Concat('last_name', Value(' '), 'first_name'),
        }

    def q(self):
        name = self.cleaned_data['name']
        name = ' '.join(name.split())
        return Q(concatenated_name__icontains=name) | Q(concatenated_name_reversed__icontains=name)


@register_filter
class CandidateNameFilter(FieldFilter):

    condition_classes = [CandidateNameCondition]
    model_class = Candidate
    form_fields = {
        'name': sform.CharField(state=sform.REQUIRED),
    }


class CandidateContactFilterContains(FieldFilter):

    condition_classes = [Contains]
    model_class = CandidateContact
    model_value_attr = 'normalized_account_id'
    form_fields = {
        'value': sform.CharField(state=sform.REQUIRED),
    }


# на фронте важен тип phone, чтобы навесить маску на поле
class PhoneFiled(sform.CharField):

    def clean(self, new_value, old_value, required, trim_on_empty, base_initial, base_data):
        value = super().clean(
            new_value, old_value, required, trim_on_empty, base_initial, base_data
        )
        normalized_value = normalize_phone(value)
        return normalized_value if normalized_value else value

    @property
    def type_name(self):
        return 'phone'


# email и телефон лежат в одной таблице в одном поле, но для фронта это разные фильтры
@register_filter
class CandidatePhoneFilter(CandidateContactFilterContains):

    form_fields = {
        'value': PhoneFiled(state=sform.REQUIRED),
    }


@register_filter
class CandidateEmailFilter(CandidateContactFilterContains):
    pass


@register_filter
class CandidateLoginFilter(FieldFilter):

    condition_classes = [Equal]
    model_class = Candidate
    model_candidate_attr = None
    model_value_attr = 'login'
    form_fields = {
        'login': sform.CharField(state=sform.REQUIRED),
    }


@register_filter
class CandidateCreatedFilter(DateRangeFilter):

    model_class = Candidate
    model_candidate_attr = None
    date_field = 'created'


@register_filter
class CandidateModifiedFilter(DateRangeFilter):

    model_class = Candidate
    model_candidate_attr = None
    date_field = 'modified'


@register_filter
class CandidateEmployeeTypeFilter(EqualFilter):

    model_class = Candidate
    model_candidate_attr = 'id'
    form_fields = {
        'employee_type': sform.ChoiceField(choices=CANDIDATE_EMPLOYEE_TYPES, state=sform.REQUIRED),
    }

    @classmethod
    def get_queryset(cls):
        return (
            Candidate.unsafe
            .annotate(is_current_employee=candidate_is_current_employee_subquery)
            .annotate(
                employee_type=Case(
                    When(is_current_employee=True, then=Value(CANDIDATE_EMPLOYEE_TYPES.current)),
                    When(
                        Q(is_current_employee=False) & ~Q(login=''),
                        then=Value(CANDIDATE_EMPLOYEE_TYPES.former),
                    ),
                    output_field=CharField(),
                ),
            )
        )


@register_filter
class CandidateStatusFilter(EqualFilter):

    condition_classes = [Equal]
    model_class = Candidate
    model_candidate_attr = None
    form_base = CandidateStatusFilterForm


@register_filter
class CandidateScoringFilter(FieldFilter):

    condition_classes = [CandidateScoringCondition]
    model_class = Candidate
    form_fields = {
        'scoring_category': sform.ModelChoiceField(
            queryset=ScoringCategory.objects.filter(is_active=True),
            label_extractor=lambda x: x.name,
            state=sform.REQUIRED,
        ),
    }

    @classmethod
    def get_queryset(cls):
        return CandidateScoring.objects.all()
