import logging
from typing import Optional, List
from uuid import UUID
from datetime import date
from staff.lib import waffle

from staff.budget_position.const import PUSH_STATUS, PositionType
from staff.budget_position.workflow_service.entities.abstract_workflow import AbstractWorkflow
from staff.budget_position.workflow_service.entities.change import Change
from staff.budget_position.workflow_service.entities.workflows.date_provider import DateProvider

logger = logging.getLogger(__name__)


class Workflow1_1(AbstractWorkflow):
    code = '1.1'
    description = 'Attaches vacancy to existing position'
    should_push_new_department_to_femida = True


class Workflow1_2(AbstractWorkflow):
    code = '1.2'
    description = 'Creates vacancy position with credit position'
    should_push_new_bp_to_femida = True

    def get_next_change_for_sending(self) -> Optional[Change]:
        assert len(self.changes) == 2

        if any(change.push_status == PUSH_STATUS.ERROR for change in self.changes):
            return None

        def change_key(change):
            return {
                PositionType.NEGATIVE_RESERVE: 0,
                PositionType.VACANCY: 1,
            }[change.position_type]

        changes = sorted(self.changes, key=change_key)

        if changes[0].push_status is None:
            return changes[0]

        if changes[1].push_status is None:
            changes[1].linked_budget_position_id = changes[0].new_budget_position.id
            return changes[1]

        return None


class Workflow1_3(AbstractWorkflow):
    code = '1.3'
    description = 'Moves budget position to empty state (vacancy cancellation)'
    should_be_pushed_automatically = True


class Workflow2_1(AbstractWorkflow):
    code = '2.1'
    description = 'Attaches vacancy to occupied position'
    should_push_new_department_to_femida = True

    def _get_last_valid_date(self, current: date) -> date:
        return date(current.year, current.month, self._date_provider.get_month_last_day(current) - 1)

    def _fix_dismissal_date_if_needed(self, date_provider: DateProvider) -> None:
        self._date_provider = date_provider or DateProvider()
        for change in self.changes:
            current = self._date_provider.today()

            if change.dismissal_date is None or change.dismissal_date < current:
                if current.day >= self._date_provider.get_month_last_day(current) - 1:
                    current = self._date_provider.add(current, months=1)
                change.dismissal_date = self._get_last_valid_date(current)
            if change.dismissal_date.day == self._date_provider.get_month_last_day(change.dismissal_date):
                next_month = self._date_provider.add(change.dismissal_date, months=1)
                change.dismissal_date = self._get_last_valid_date(next_month)

    def _do_not_use_schemes(self):
        for change in self.changes:
            change.review_scheme_id = None
            change.bonus_scheme_id = None
            change.reward_scheme_id = None

    def __init__(self, workflow_id: UUID, changes: List[Change], date_provider: DateProvider = None) -> None:
        super().__init__(workflow_id, changes)
        self._fix_dismissal_date_if_needed(date_provider or DateProvider())  # STAFF-14985
        self._do_not_use_schemes()  # STAFF-15007


class Workflow5_1(AbstractWorkflow):
    code = '5.1'
    description = 'Moves budget position from vacancy open to offer workflow'
    should_push_new_department_to_femida = True

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.should_push_new_department_to_femida = waffle.switch_is_active('enable_department_pushing_after')


class Workflow5_1_1(AbstractWorkflow):
    code = '5.1.1'
    description = 'Creates assignment to budget position'
    should_be_pushed_automatically = True
    should_be_pushed_to_oebs_hire = True


class Workflow5_2(AbstractWorkflow):
    code = '5.2'
    description = 'Moves budget position from offer to vacancy open (offer rejection)'
    should_be_pushed_automatically = True


class Workflow5_3(AbstractWorkflow):
    code = '5.3'
    description = 'Internal offer with date completion'
    should_push_new_department_to_femida = True

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
