from typing import List, Optional
from decimal import Decimal
from datetime import date

import attr

from staff.budget_position.const import FemidaProfessionalLevel
from staff.budget_position.workflow_service.entities.attributes import (
    analytics,
    common,
    credit_management,
    oebs,
    payment,
)
from staff.budget_position.workflow_service.entities.types import (
    BonusId,
    BonusSchemeId,
    BudgetPositionCode,
    GradeId,
    OccupationId,
    ReviewSchemeId,
    RewardSchemeId,
)
from staff.budget_position.workflow_service.entities.attributes import femida


MONTHS_PER_YEAR = 12


@attr.s(auto_attribs=True)
class FemidaData:
    vacancy_id: int = femida.vacancy_id
    vacancy_name: Optional[str] = femida.vacancy_name

    is_offer: bool = femida.is_offer
    is_internal_offer: bool = femida.is_internal_offer
    is_offer_rejection: bool = femida.is_offer_rejection
    is_vacancy: bool = femida.is_vacancy
    is_vacancy_cancellation: bool = femida.is_vacancy_cancellation
    is_assignment_creation: bool = femida.is_assignment_creation

    grade_id: GradeId or None = None
    occupation_id: Optional[OccupationId] = None
    grade_level: Optional[int] = None
    professional_level: FemidaProfessionalLevel or None = None

    office_id: int = analytics.office_id
    geography_url: str or None = None
    department_id: Optional[int] = None
    hr_product_id: int = analytics.hr_product_id
    organization_id: int = analytics.organization_id

    ticket: str or None = None
    salary_ticket: str or None = None
    dismissal_date: str or None = None
    budget_position_code: BudgetPositionCode or None = None

    rate: Decimal = payment.rate
    salary: str = payment.salary
    currency: str = payment.currency
    payment_system: str = payment.payment_system
    wage_system: str = payment.wage_system

    employment_type: str = None
    person_id: int or None = None
    physical_entity_id: int or None = None
    position_id: int = None
    other_payments: str = payment.other_payments
    join_at: date = None
    probation_period_code: int = None
    is_replacement: bool = False
    instead_of_login: str = common.instead_of_login
    contract_term_date: date = None
    contract_period: int = None
    is_internship: bool = False

    bonus_scheme_id: BonusSchemeId or None = None
    review_scheme_id: ReviewSchemeId or None = None
    reward_scheme_id: RewardSchemeId or None = None

    @property
    def has_grade(self) -> bool:
        return bool(self.grade_id)

    @property
    def has_occupation(self) -> bool:
        return bool(self.occupation_id)


@attr.s(auto_attribs=True)
class ProposalChange:
    oebs_date: date = common.oebs_date

    office_id: int = analytics.office_id
    department_id: Optional[int] = None
    organization_id: int = analytics.organization_id
    hr_product_id: int = analytics.hr_product_id
    geography_url: str or None = None
    occupation_id: OccupationId = oebs.occupation_id
    grade_id: int = oebs.grade_id
    grade_level: int = oebs.grade_level
    grade_change: str = oebs.grade_change
    is_internship: bool or None = None

    position_id: int = None

    currency: str = payment.currency
    rate: Decimal = payment.rate
    salary: str = payment.salary
    wage_system: str = payment.wage_system

    review_scheme_id: ReviewSchemeId = None
    bonus_scheme_id: BonusSchemeId = None
    reward_scheme_id: RewardSchemeId = None
    force_recalculate_schemes: bool = None

    @property
    def has_department_change(self) -> bool:
        return self.department_id is not None

    @property
    def has_occupation_change(self) -> bool:
        return self.occupation_id is not None

    @property
    def has_grade_change(self) -> bool:
        return self.grade_change is not None


@attr.s(auto_attribs=True, hash=True)
class ProposalData:
    proposal_id: int
    is_move_from_maternity: bool = False
    vacancy_id: int or None = None
    person_id: int or None = None
    headcount_position_code: BudgetPositionCode or None = None
    is_move_with_budget_position: bool = False
    ticket: str or None = None
    job_ticket_url: str or None = None
    proposal_changes: List[ProposalChange] = attr.Factory(list)

    @property
    def has_any_grade_or_department_or_occupation_change(self) -> bool:
        return any(
            change
            for change in self.proposal_changes
            if change.has_department_change or change.has_grade_change or change.has_occupation_change
        )

    @property
    def has_person_id(self):
        return self.person_id is not None

    @property
    def has_headcount_position_code(self):
        return self.headcount_position_code is not None

    @property
    def has_vacancy_id(self):
        return self.vacancy_id is not None


@attr.s(auto_attribs=True)
class CreditRepaymentData:
    credit_management_id: int = credit_management.credit_management_id
    credit_budget_position: int or None = None
    repayment_budget_position: int or None = None
    vacancy_id: Optional[int] = femida.vacancy_id
    ticket: str or None = None


@attr.s(auto_attribs=True)
class MoveToMaternityData:
    login: str
    responsible_id: int
    department_id: int  # TODO: удалить, когда будем доставать департамент из позиции
    budget_position: Optional[int] = None
    ticket: Optional[str] = None


@attr.s(auto_attribs=True)
class GetSchemeRequest:
    department_id: Optional[int]
    grade_level: int = oebs.grade_level
    occupation_id: str = oebs.occupation_id
    is_internship: bool or None = None
    femida_professional_level: FemidaProfessionalLevel or None = None
    is_main_work_place: bool or None = None
    contract_term: int or None = None
    contract_term_date: date or None = None


@attr.s(auto_attribs=True)
class BonusSchemeRow:
    bonus_id: BonusId
    value_type: str
    value_source: str
    value: float


@attr.s(auto_attribs=True)
class BonusSchemeDetails:
    scheme_id: BonusSchemeId
    name: str
    description: str
    scheme_rows: List[BonusSchemeRow]
    non_review_bonus: Optional[float] = None  # премия вне ревью, кол-во окладов


@attr.s(auto_attribs=True)
class ReviewSchemeDetails:
    scheme_id: ReviewSchemeId
    name: str
    description: str
    schemes_line_id: int
    schemes_line_description: str
    target_bonus: float
    grant_type: str
    grant_type_description: str
    has_review: Optional[bool] = None
    review_bonus: Optional[float] = None


@attr.s(auto_attribs=True)
class InsuranceDetails:
    name: str
    type: str
    ya_insurance: bool


@attr.s(auto_attribs=True)
class RewardSchemeDetails:
    scheme_id: RewardSchemeId
    description: str
    schemes_line_id: int
    category: str
    food: str
    dms: List[InsuranceDetails]
    ai: List[InsuranceDetails]
    dms_group: str
    bank_cards: List[str]
    name: str
    has_food_compensation: bool or None = None
    has_health_insurance: bool or None = None
    has_life_insurance: bool or None = None


@attr.s(auto_attribs=True)
class OccupationDetails:
    """Содержит детали для Occupation, в частности ее группы"""
    review_group: str = None
    bonus_group: str = None
    reward_group: str = None
    occupation_description: str = None
