import uuid
from datetime import date
import logging
from uuid import UUID

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


logger = logging.getLogger(__name__)


class CreateWorkflowForCreditRepayment:
    def __init__(
        self,
        repository: entities.WorkflowRepositoryInterface,
        budget_positions_repository: entities.BudgetPositionsRepository,
    ):
        self._repository = repository
        self._budget_positions_repo = budget_positions_repository

    def create(self, data: entities.CreditRepaymentData) -> UUID:
        logger.info('Creating workflow')
        workflow = self._create_workflow(data)
        assert len(workflow.changes) == 2
        workflow.vacancy_id = data.vacancy_id
        workflow.credit_management_id = data.credit_management_id

        logger.info('Created workflow %s with id %s', workflow.code, workflow.id)
        self._repository.save(workflow)
        return workflow.id

    def _create_changes_for_workflow(self, data: entities.CreditRepaymentData):
        change1 = entities.Change(
            budget_position=self._budget_positions_repo.budget_position_by_code(data.credit_budget_position),
            effective_date=date.today(),
            remove_budget_position=True,
            ticket=data.ticket,
        )

        change2 = entities.Change(
            budget_position=self._budget_positions_repo.budget_position_by_code(data.repayment_budget_position),
            effective_date=date.today(),
            remove_budget_position=True,
            ticket=data.ticket,
        )

        return [change1, change2]

    def _create_workflow(self, data: entities.CreditRepaymentData) -> entities.AbstractWorkflow:
        repayment_statuses = self._budget_positions_repo.assignments_statuses_by_codes([data.repayment_budget_position])

        error_message = (
            f'Expected exactly one assignment for bp {data.repayment_budget_position}'
            f', but found {len(repayment_statuses)}'
        )
        assert len(repayment_statuses) == 1, error_message

        repayment_status = BudgetPositionAssignmentStatus(repayment_statuses.pop())
        assert repayment_status in (
            BudgetPositionAssignmentStatus.VACANCY_PLAN,
            BudgetPositionAssignmentStatus.VACANCY_OPEN,
        )

        credit_statuses = self._budget_positions_repo.assignments_statuses_by_codes([data.credit_budget_position])
        error_message = (
            f'Expected exactly one assignment for bp {data.repayment_budget_position}'
            f', but found {len(repayment_statuses)}'
        )
        assert len(credit_statuses) == 1, error_message
        credit_status = BudgetPositionAssignmentStatus(credit_statuses.pop())
        assert credit_status == BudgetPositionAssignmentStatus.RESERVE

        if repayment_status == BudgetPositionAssignmentStatus.VACANCY_OPEN:
            result = entities.workflows.credit_management.WorkflowCreditManagementWithVacancy(
                uuid.uuid1(),
                self._create_changes_for_workflow(data),
            )
            return result

        result = entities.workflows.credit_management.WorkflowCreditManagement(
            uuid.uuid1(),
            self._create_changes_for_workflow(data),
        )
        return result
