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

from wtforms import StringField, SubmitField, SelectField, validators, IntegerField, DateTimeField, BooleanField
from wtforms.validators import DataRequired, StopValidation
from flask_wtf import FlaskForm
from saas.tools.ssm.SaaSServiceManager import SaaSServiceManager
from saas.library.python.deploy_manager_api.saas_service import SaasService
from backend.helpers import BlueprintsHelpers
from service_validators import RequiredIf

ctypes = SaaSServiceManager.ctype_tags.keys()


class Authorised(object):
    def __init__(self, message_not_found=None, message_auth_error=None):
        self.message_not_found = message_not_found
        self.message_auth_error = message_auth_error

    def __call__(self, form, field):
        service = field.data
        ctype = form.ctype.data
        sla_info = SaasService(ctype, service).sla_info
        if not sla_info or sla_info.keys() == ['owners', 'responsibles']:
            raise StopValidation(self.message_not_found)
        if form.user_login.data not in sla_info['owners'] and not BlueprintsHelpers.is_saas_admin(form.user_login.data):
            raise StopValidation(self.message_auth_error)


class NotifyCheck(object):
    def __init__(self, message):
        self.message = message

    def __call__(self, form, field):
        if not field.data or not form.repetition_interval.data:
            return

        if int(field.data) > int(form.repetition_interval.data):
            raise StopValidation(self.message)


class ResponsiblesChecker(object):
    def __init__(self, message_not_found=None, message_auth_error=None, message_notify_error=None):
        self.message_not_found = message_not_found
        self.message_auth_error = message_auth_error
        self.message_notify_error = message_notify_error

    def __call__(self, form, field):
        service = form.service.data
        ctype = form.ctype.data
        if not form.notify_time.data:
            if field.data:
                raise StopValidation(self.message_notify_error)
            else:
                return
        responsibles = field.data.replace(' ', '').split(',') if ',' in field.data else [str(field.data), ]
        sla_info = SaasService(ctype, service).sla_info
        if not sla_info or sla_info.keys() == ['owners', 'responsibles']:
            raise StopValidation(self.message_not_found)
        for user in responsibles:
            if user not in sla_info['responsibles'] and not BlueprintsHelpers.is_saas_admin(form.user_login.data):
                raise StopValidation(self.message_auth_error.format(user))


class DcChecker(object):
    def __init__(self, message=None):
        self.message = message

    def __call__(self, form, field):
        for dc in field.data.replace(' ', '').split(','):
            if dc not in ['MAN', 'SAS', 'VLA', 'man', 'sas', 'vla']:
                raise StopValidation(self.message)


class CustomShootingForm(FlaskForm):
    user_login = StringField('Yandex Login')
    ammo_file = StringField(u'Ресурс с готовыми патронами для обстрела',
                            validators=[validators.Regexp(regex=r'^\d*$',
                                                          message='Id ресурса может состоять только из цифр')],
                            description=u'Целое число - идентификатор sandbox ресурса '
                                        u'с готовыми патронами для обстрела '
                                        u'(может быть не указан - тогда патроны будут взяты из логов)')

    service = StringField(u'Имя сервиса',
                          validators=[Authorised('Не удается получить информацию о данном сервисе в указанном ctype',
                                                 'У вас нет прав на обстрел данного сервиса'),
                                      DataRequired(u'Укажите имя сервиса для обстрела')])

    source_service = StringField(u'Сервис-донор логов для патронов',
                                 description=u'Строка - имя сервиса, из которого будут взяты патроны для обстрела, '
                                             u'оставьте это поле пустым, если сервис обстрела совпадает '
                                             u'с сервисом - источником патронов'
                                 )

    ticket = StringField(u'Тикет',
                         validators=[DataRequired(u'Поле не может быть пустым'),
                                     validators.Regexp(
                                         regex=r'[A-Z]+-\d+',
                                         message='Невалидный ticket id, пример: SAASLOADTEST-1')
                                     ],
                         description=u'Id тикета на обстрел сервиса')

    locations = StringField(u'Список локаций для обстрела',
                            validators=[DataRequired(u'Поле не может быть пустым'),
                                        DcChecker('Пожалуйста, укажите локацию(-и) из списка: man, sas, vla')
                                        ],
                            description=u'Список локаций, в которых планируется проводить обстрел '
                                        u'(через запятую, маленькими буквами, пример: sas, man)')

    ctype = SelectField(u'Cluster type (ctype) сервиса',
                        choices=[tuple([ctype, ctype]) for ctype in ctypes],
                        default='prestable',
                        description=u'Выберите cluster type сервиса.')

    target_rps = IntegerField(u'Целевой rps',
                              description=u'Rps, до которого требуется нагрузить каждый из DC')

    store_requests_info = BooleanField(u'Сохранить информацию о неудавшихся запросах в YT & Startrek', default=False)
    requests_info_find_by = StringField(
        u'Выражение для поиска релевантных строк в YT таблицах логов запросов',
        validators=[
            validators.Regexp(
                regex=r'^[\w-]+=[\w-]+$',
                message=u'Поле должно быть заполнено в формате key=value, например: reqinfo=saas-shooting'
            ),
            RequiredIf(
                store_requests_info=[True],
                message=u'Поле не может быть пустым',
            )
        ],
        description=u'CGI ({key}={value}) выражение для поиска релевантных строк в YT таблицах логов запросов. '
                    u'Если используются патроны, сгенерированные задачей MAKE_AMMO_FROM_YT_LOGS, нужно вписать: '
                    u'reqinfo=saas-ammo-task-{task_id}, где task_id — номер задачи в Sandbox.'
    )

    submit = SubmitField(u'Подтвердить')


class RegularShootingForm(FlaskForm):
    user_login = StringField('Yandex Login')

    scheduler_id = IntegerField(u'Идентификатор шедулера в sandbox', render_kw={'readonly': True})
    notify_time = IntegerField(
        u'Время уведомления ответственного (в часах)',
        validators=[
            NotifyCheck('Время до уведомления ответственного '
                        'не может быть больше периодичности стрельб')
        ],
        description=u'Время до старта стрельбы, '
                    u'после которого следует предупредить ответственного'
    )
    repetition_interval = IntegerField(u'Периодичность стрельб (в часах)',
                                       validators=[DataRequired(u'Укажите периодичность стрельб')],
                                       description=u'Периодичность с которой будут запускаться стрельбы')
    start_time = DateTimeField('Дата начала стрельб', format='%d.%m.%Y %H:%M',
                               validators=[DataRequired(u'Укажите дату начала стрельб')],
                               description='Дата и время начала стрельб в формате: 01.01.2000 12:30')

    ammo_file = StringField(u'Ресурс с готовыми патронами для обстрела',
                            validators=[validators.Regexp(regex=r'^\d*$',
                                                          message='Id ресурса может состоять только из цифр')],
                            description=u'Целое число - идентификатор sandbox ресурса с готовыми патронами для обстрела'
                                        u'(может не быть указан - тогда патроны будут взяты из логов)')

    service = StringField(u'Имя сервиса',
                          validators=[Authorised('Не удается получить информацию о данном сервисе в указанном ctype',
                                                 'У вас нет прав на обстрел данного сервиса'),
                                      DataRequired(u'Укажите имя сервиса для обстрела')])

    source_service = StringField(u'Сервис-донор логов для патронов',
                                 description=u'Строка - имя сервиса, из которого будут взяты патроны для обстрела, '
                                             u'оставьте это поле пустым, если сервис обстрела совпадает '
                                             u'с сервисом - источником патронов'
                                 )

    ticket = StringField(u'Тикет',
                         validators=[validators.Regexp(
                             regex=r'([A-Z]+-\d+|^$)',
                             message='Невалидный ticket id')
                         ],
                         render_kw={'placeholder': 'Например: SAASLOADTEST-1'},
                         description=u'Id тикета на обстрел сервиса')

    locations = StringField(u'Список локаций для обстрела',
                            validators=[DataRequired(u'Поле не может быть пустым'),
                                        DcChecker('Пожалуйста, укажите локацию(-и) из списка: man, sas, vla')
                                        ],
                            default='man',
                            render_kw={'placeholder': 'Например: man, sas'},
                            description=u'Список локаций, в которых планируется проводить обстрел')

    ctype = SelectField(u'Cluster type (ctype) сервиса',
                        choices=[tuple([ctype, ctype]) for ctype in ctypes],
                        default='prestable',
                        description=u'Выберите cluster type сервиса.')

    target_rps = IntegerField(u'Целевой rps',
                              description=u'Rps, до которого требуется нагрузить каждый из DC')

    responsibles = StringField(u'Список ответственных за сервис, которым будут приходить уведомления',
                               validators=[ResponsiblesChecker(
                                   'Не удается получить информацию об ответственных даннного сервиса',
                                   'Пользователь {} не входит в список ответственных за сервис',
                                   'Пожалуйста, укажите время предупреждения для указанных ответственных')],
                               description=u'Список людей, ответственных за данные стрельбы (через запятую)')

    status = BooleanField('Перевести в состояние STOPPED')

    send_results_to_uchenki = BooleanField('Отправлять результат в ученьки', default=True)

    submit = SubmitField(u'Подтвердить')
