# -*- coding: utf-8 -*-


import logging

from idm.core.management.base import IdmBaseCommand
from idm.core.models import Role
from idm.utils.lock import lock
from idm.utils.replication import use_slave
from idm.utils.command_timestamp import timestamp_context

log = logging.getLogger(__name__)


class Command(IdmBaseCommand):
    USE_LOCK = False

    help = """Запускаем процедуры отзыва из системы для всех ролей,
    которые по каким-то причинам зависли в состоянии depriving.
    И процедуру выдачи ролей в случае, если они подвисли в состоянии approved."""

    STAGES_METHODS = [
        ('poke_depriving', 'poke_hanging_depriving_roles', False),
        ('poke_approved', 'poke_hanging_approved_roles', True),
        ('request_refs', 'request_applicable_ref_roles', False),
        ('deprive_refs', 'deprive_refs_of_inactive_roles', False),
        ('request_or_deprive_personal', 'request_or_deprive_personal_roles', False),
    ]
    STAGES = [pair[0] for pair in STAGES_METHODS]
    STAGES_DICT = {stage_name: (method_name, can_use_slave)
                   for stage_name, method_name, can_use_slave in STAGES_METHODS}

    def add_arguments(self, parser):
        super(Command, self).add_arguments(parser)
        parser.add_argument(
            '--stage', action='store', dest='stage', choices=self.STAGES
        )
        parser.add_argument(
            '--system', action='store', dest='system', type='system', required=False
        )
        parser.add_argument(
            '--retry-failed', action='store_true', dest='retry_failed', required=False
        )
        parser.set_defaults(retry_failed=False)

    def idm_handle(self, *args, **options):
        system = options.get('system')
        system_lock_key = system.slug if system else '-'
        stage = options.get('stage')
        retry_failed = options.get('retry_failed')
        if stage is None:
            stages = self.STAGES
        else:
            stages = (stage,)

        system_comment = (' (for system %s)' % system.slug) if system else ''
        for stage in stages:
            method_name, can_use_slave = self.STAGES_DICT[stage]
            method = getattr(Role.objects, method_name)
            with lock(f'poke_hanging_roles_{system_lock_key}_{stage}') as locked:
                if not locked:
                    return
                try:
                    log.info('Poking hanging roles %s. Stage: %s', system_comment, stage)
                    kwargs = {'system': system}
                    if stage == 'request_or_deprive_personal':
                        kwargs['retry_failed'] = retry_failed
                    with timestamp_context(f'{self.command_name}.{stage}', log):
                        if can_use_slave:
                            with use_slave():
                                method(**kwargs)
                        else:
                            method(**kwargs)
                    log.info('Poked hanging roles %s. Stage: %s', system_comment, stage)
                except Exception:
                    log.exception('Error while poking hanging roles %s. Stage: %s', system_comment, stage)
