import json
import logging
from typing import Dict, List, Optional

from django.conf import settings

from staff.lib.models.mptt import filter_by_heirarchy
from staff.departments.models import Bonus, Department, DepartmentRoles, DepartmentStaff
from staff.map.models import Placement as PlacementModel
from staff.oebs.models import Review, Reward
from staff.person.models import Staff, Occupation

from staff.budget_position.models import PersonSchemeExceptions
from staff.budget_position.workflow_service import entities

logger = logging.getLogger(__name__)


class StaffService(entities.StaffService):
    def hr_analyst_responsible_for_department(self, department_id: int) -> Optional[entities.Person]:
        roles_in_tree = filter_by_heirarchy(
            DepartmentStaff.objects.filter(role_id=DepartmentRoles.HR_ANALYST_TEMP.value),
            Department.objects.filter(id=department_id),
            filter_prefix='department__',
            by_children=False,
            include_self=True,
        )

        result = roles_in_tree.order_by('-department__level').first()
        if not result:
            return None
        return result and self._staff_to_person(result.staff)

    def default_hr_analyst(self) -> entities.Person:
        return self._staff_to_person(Staff.objects.get(login=settings.DEFAULT_CATALYST_FOR_OEBS))

    def placement_for(self, office_id: int, organization_id: int) -> Optional[entities.Placement]:
        placements = list(
            PlacementModel.objects.filter(organization_id=organization_id, office_id=office_id, active_status=True)
        )

        if len(placements) != 1:
            logger.info(
                '%s placements found for office_id %s and organization_id %s',
                len(placements),
                office_id,
                organization_id,
            )
            return None

        return entities.Placement(
            id=placements[0].pk,
            office_id=placements[0].office_id,
            organization_id=placements[0].organization_id,
        )

    def get_person(self, person_id: entities.PersonId) -> entities.Person:
        return self._staff_to_person(Staff.objects.get(id=person_id))

    def get_person_logins(self, person_ids: List[entities.PersonId]) -> Dict[entities.PersonId, str]:
        return dict(Staff.objects.filter(id__in=person_ids).values_list('id', 'login'))

    def person_departments(self, person_ids: List[entities.PersonId]) -> Dict[entities.PersonId, int]:
        return dict(Staff.objects.filter(id__in=person_ids).values_list('id', 'department_id'))

    def bonus_scheme_details(self, bonus_scheme_id: entities.BonusSchemeId) -> Optional[entities.BonusSchemeDetails]:
        scheme = Bonus.objects.filter(scheme_id=bonus_scheme_id).first()

        if not scheme:
            return None

        scheme_rows = []

        if scheme.scheme_data:
            for row in json.loads(scheme.scheme_data):
                scheme_rows.append(
                    entities.BonusSchemeRow(
                        row['bonus_id'],
                        row['value_type'],
                        row['value_source'],
                        row['value'],
                    ),
                )

        return entities.BonusSchemeDetails(
            scheme_id=scheme.scheme_id,
            name=scheme.name,
            description=scheme.description,
            scheme_rows=scheme_rows,
            non_review_bonus=scheme.non_review_bonus,
        )

    def review_scheme_details(
        self,
        review_scheme_id: entities.ReviewSchemeId,
    ) -> Optional[entities.ReviewSchemeDetails]:
        scheme = Review.objects.filter(scheme_id=review_scheme_id).first()
        if not scheme:
            return None

        return entities.ReviewSchemeDetails(
            scheme_id=scheme.scheme_id,
            name=scheme.name,
            description=scheme.description,
            schemes_line_id=scheme.schemes_line_id,
            schemes_line_description=scheme.schemes_line_description,
            target_bonus=scheme.target_bonus,
            grant_type=scheme.grant_type,
            grant_type_description=scheme.grant_type_description,
        )

    def reward_scheme_details(
        self,
        reward_scheme_id: entities.RewardSchemeId,
    ) -> Optional[entities.RewardSchemeDetails]:
        scheme = Reward.objects.filter(scheme_id=reward_scheme_id).first()

        if not scheme:
            return None

        dms_details: List[entities.InsuranceDetails] = []
        for dms_option in json.loads(scheme.dms):
            dms_details.append(entities.InsuranceDetails(
                name=dms_option.get('name'),
                type=dms_option.get('typeInsurance'),
                ya_insurance=dms_option.get('yaInsurance') == 'Да',
            ))

        ai: List[entities.InsuranceDetails] = []
        if scheme.ai:
            for insurance_option in json.loads(scheme.ai):
                ai.append(entities.InsuranceDetails(
                    name=insurance_option.get('name'),
                    type=insurance_option.get('typeInsurance'),
                    ya_insurance=insurance_option.get('yaInsurance') == 'Да',
                ))

        return entities.RewardSchemeDetails(
            scheme_id=scheme.scheme_id,
            schemes_line_id=scheme.schemes_line_id,
            description=scheme.description,
            category=scheme.category,
            food=scheme.food,
            dms=dms_details,
            ai=ai,
            dms_group=scheme.dms_group,
            bank_cards=json.loads(scheme.bank_cards),
            name=scheme.name,
        )

    def occupation_details(self, occupation_id: entities.OccupationId) -> Optional[entities.OccupationDetails]:
        occupation: Occupation = Occupation.objects.filter(pk=occupation_id).first()
        if not occupation:
            return None

        return entities.OccupationDetails(
            review_group=occupation.group_review,
            bonus_group=occupation.group_bonus,
            reward_group=occupation.group_reward,
            occupation_description=occupation.description,
        )

    def multiple_occupation_groups(
        self,
        occupation_ids: List[entities.OccupationId],
    ) -> Dict[entities.OccupationId, Optional[entities.OccupationDetails]]:
        occupations = (
            Occupation.objects
            .filter(pk__in=occupation_ids)
            .values('pk', 'group_review', 'group_bonus', 'group_reward')
        )

        return {
            occupation['pk']: entities.OccupationDetails(
                review_group=occupation['group_review'],
                bonus_group=occupation['group_bonus'],
                reward_group=occupation['group_reward'],
            )
            for occupation in occupations
        }

    def _staff_to_person(self, staff: Staff) -> entities.Person:
        return entities.Person(staff.id, staff.login, staff.office_id, staff.organization_id)

    def person_scheme_exceptions(
        self,
        person_ids: List[entities.PersonId],
    ) -> Dict[entities.PersonId, entities.PersonSchemeException]:
        fields = ('person_id', 'reward_scheme__scheme_id', 'bonus_scheme__scheme_id', 'review_scheme__scheme_id')

        qs = (
            PersonSchemeExceptions.objects
            .active()
            .filter(person_id__in=person_ids)
            .values_list(*fields)
        )
        return {
            person_id: entities.PersonSchemeException(person_id, bonus_scheme_id, review_scheme_id, reward_scheme_id)
            for person_id, reward_scheme_id, bonus_scheme_id, review_scheme_id in qs
        }
