import logging
import waffle

from django.template import loader
from django.utils import timezone

from intranet.femida.src.candidates.choices import (
    SUBMISSION_STATUSES,
    REFERENCE_STATUSES,
    SUBMISSION_SOURCES,
    EXTERNAL_SUBMISSION_SOURCES,
)
from intranet.femida.src.candidates.models import Candidate
from intranet.femida.src.candidates.submissions.controllers import SubmissionController
from intranet.femida.src.core.switches import TemporarySwitch
from intranet.femida.src.core.workflow import Action, Workflow
from intranet.femida.src.staff.achievements import ApprovedReferenceAchievement, DontGiveAnyAchievement
from intranet.femida.src.startrek.operations import IssueTransitionOperation
from intranet.femida.src.startrek.utils import (
    StartrekError,
    TransitionEnum,
    TransitionDoesNotExist,
    ResolutionEnum,
)

logger = logging.getLogger(__name__)


class SubmissionAction(Action):

    valid_statuses = (
        SUBMISSION_STATUSES.new,
    )

    def has_permission(self):
        return not self.instance.is_internship


class CreateCandidateAction(SubmissionAction):

    def has_permission(self):
        if not super().has_permission():
            return False

        # Для ротаций, если кандидат с таким логином есть, нового создавать запрещаем
        if self.instance.source == SUBMISSION_SOURCES.rotation:
            return not (
                Candidate.objects
                .alive()
                .filter(login=self.instance.rotation.created_by.username)
                .exists()
            )

        return True

    def perform(self, **params):
        controller = SubmissionController(submission=self.instance, initiator=self.user)
        controller.create_candidate()
        return self.instance


class MergeCandidateAction(SubmissionAction):

    def perform(self, **params):
        controller = SubmissionController(submission=self.instance, initiator=self.user)
        controller.merge_into_candidate(params['candidate'])
        return self.instance


class HandleAction(SubmissionAction):

    def perform(self, **params):
        controller = SubmissionController(submission=self.instance, initiator=self.user)
        duplicate = params.pop('duplicate', None)
        is_rejection = params.pop('is_rejection', False)
        if duplicate is None:
            controller.create_candidate(is_rejection)
        else:
            controller.merge_into_candidate(duplicate, is_rejection)
        if is_rejection:
            text = params['text']
            schedule_time = params.get('schedule_time')
            controller.create_rejection_message(text, schedule_time)
        return self.instance


class RejectAction(SubmissionAction):
    """
    В случае спама мы хотим закрыть отклик, но кандидата не создавать
    """
    def has_permission(self):
        if self.instance.source in EXTERNAL_SUBMISSION_SOURCES:
            return not self.instance.is_internship
        # Отклики-рекомендации и отклики-ротации можно отклонять только отдельными действиями.
        # На этапе обработки самого отклика это сделать уже нельзя
        return False

    def perform(self, **params):
        controller = SubmissionController(submission=self.instance, initiator=self.user)
        controller.reject()
        return self.instance


class ReferenceActionBase(Action):
    """
    Базовая логика обработки рекомендации.

    Обработка рекомендации протекает следующим образом.
    Объекты процесса обработки рекомендации: Reference, CandidateSubmission, тикет в REFERENCE.
    Порядок действий при обработке рекомендации:
    1. Координатор обрабатывает Reference. Действия над Submission не доступны.
      Тикет REFERENCE не подлежит изменениям.
    Итог: reference обработан, Submission переведен из статуса `draft` в `new` или `closed`,
      действия на Submission становятся доступны.
      REFERENCE-тикет переведен в статус `validated` или `closed`.
    2. Рекрутер обрабатывает Submission. Экшен `reject` не доступен.
    Итог: Submission закрыт, по нему создан или смержен кандидат.
      REFERENCE-тикет переведен в статус `inProgress`.
    """
    issue_tag = None
    issue_transition = None
    reference_target_status = None
    submission_target_status = None
    achievement_cls = DontGiveAnyAchievement

    def is_status_correct(self):
        is_reference = self.instance.source == SUBMISSION_SOURCES.reference
        is_submission_pending = self.instance.status == SUBMISSION_STATUSES.draft
        return is_reference and is_submission_pending

    def has_permission(self):
        return self.user.is_reference_coordinator

    def perform(self, **params):
        self._update_issue(params)
        self._update_instance()

        # TODO: Убрать проверку свитча после релиза FEMIDA-7118
        if waffle.switch_is_active(TemporarySwitch.GREEN_REFERENCE_ACHIEVEMENTS):
            self.achievement_cls(usernames=[self.instance.reference.created_by.username]).give_all_delay()
        return self.instance

    def _update_issue(self, params):
        comment_base = params.get('comment')
        issue_fields = {
            'tags': {
                'add': self.issue_tag,
            },
        }
        if self.issue_transition == TransitionEnum.close:
            issue_fields['resolution'] = ResolutionEnum.fixed
        if comment_base:
            context = self.get_context()
            context['comment_base'] = comment_base
            issue_fields['comment'] = loader.render_to_string(
                template_name='startrek/references/reference-processed.wiki',
                context=context,
            )

        operation = IssueTransitionOperation(self.instance.reference.startrek_key)
        try:
            operation(self.issue_transition, **issue_fields)
        except TransitionDoesNotExist as exc:
            self.raise_error(exc.message)
        except StartrekError:
            operation.delay(self.issue_transition, **issue_fields)

    def _update_instance(self):
        self.instance.reference.status = self.reference_target_status
        self.instance.reference.processed_by = self.user
        self.instance.reference.processed_at = timezone.now()
        self.instance.reference.save(
            update_fields=('status', 'modified', 'processed_by', 'processed_at'),
        )

        self.instance.status = self.submission_target_status
        self.instance.save(update_fields=('status', 'modified'))


class ReferenceApproveAction(ReferenceActionBase):

    issue_tag = 'approve'
    issue_transition = TransitionEnum.validated
    reference_target_status = REFERENCE_STATUSES.approved
    submission_target_status = SUBMISSION_STATUSES.new
    achievement_cls = ApprovedReferenceAchievement


class ReferenceApproveWithoutBenefitsAction(ReferenceActionBase):

    issue_tag = 'approve-without-benefits'
    issue_transition = TransitionEnum.validated
    reference_target_status = REFERENCE_STATUSES.approved_without_benefits
    submission_target_status = SUBMISSION_STATUSES.new
    achievement_cls = ApprovedReferenceAchievement


class ReferenceRejectAction(ReferenceActionBase):

    issue_tag = 'reject'
    issue_transition = TransitionEnum.close
    reference_target_status = REFERENCE_STATUSES.rejected
    submission_target_status = SUBMISSION_STATUSES.closed

    def perform(self, **params):
        self.instance = super().perform(**params)
        controller = SubmissionController(submission=self.instance, initiator=self.user)
        controller.reject()
        return self.instance


class SubmissionWorkflow(Workflow):

    ACTION_MAP = {
        'handle': HandleAction,
        'reject': RejectAction,
        'approve_reference': ReferenceApproveAction,
        'approve_reference_without_benefits': ReferenceApproveWithoutBenefitsAction,
        'reject_reference': ReferenceRejectAction,
    }
