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

from wtforms import StringField, validators, RadioField,\
    TextAreaField, IntegerField, SubmitField, BooleanField, SelectField, FloatField
from wtforms.validators import DataRequired
from flask_wtf import FlaskForm

from service_validators import RequiredIf, CountOf, IntValidIf, ABCParent, ABCUser, StaffUser, TypeValidIf
from saas.tools.ssm.SaaSServiceManager import SaaSServiceManager


ctypes = SaaSServiceManager.ctype_tags.keys()


class TestingServiceForm(FlaskForm):
    user_login = StringField('Yandex Login')
    service_name = StringField(u'Имя сервиса',
                               validators=[validators.Length(
                                   min=3, max=20, message=u'Длина имени сервиса должна состоять от 3х до 20 символов'),
                                   validators.Regexp(
                                       message=u'Имя сервиса может состоять только из латинских букв, цифр и символов "_". '
                                               u'Testing сервисы должны оканчиваться суффиксом _t',
                                       regex=r'^[a-z0-9]+[a-z_0-9]+[a-z0-9]+$'),
                                   validators.Regexp(
                                       message=u'Запрещены в имени сервиса слова prestable/stable/test/testing/production/prod/infra/proxy/__',
                                       regex=r'^((?!stable|test|prestable|prod|production|infra|proxy|__).)*$'),
                                   DataRequired(u'Укажите имя сервиса')],
                               description=u'Имя сервера может состоять из латинских букв цифр и символов "_". '
                                           u'Testing сервисы должны оканчиваться суффиксом _t. Пример: lingv_boosting_ssd_t')
    abc_user_service = IntegerField(u'ABC Сервис-владелец (id)',
                                    validators=[
                                        DataRequired(u'Поле не может быть пустым'),
                                        ABCParent('abc_quota_service', u'Сервис не принадлежит к указанному ABC донору ресурсов'),
                                        ABCUser(u'Вы не состоите в данном ABC сервисе')
                                    ],
                                    description=u'ABC id сервиса, который будет использовать поисковый рантайм SaaS. '
                                                u'Как правило это листовой узел в дереве ABC. '
                                                u'Заказчик SaaS-сервиса должен состоять в ABC-сервисе-владельце')
    abc_quota_service = IntegerField(u'ABC Донор ресурсов (id)', validators=[DataRequired(u'Поле не может быть пустым')],
                                     description=u'ABC id высокоуровневого сервиса в Яндексе, который предоставляет ресурсы.'
                                                 u'Например Сервисы Поискового портала (851) или Сервисы Маркета (905).')
    owners_list = StringField(u'Список ответственных',
                              validators=[DataRequired(u'Поле не может быть пустым'),
                                          validators.Regexp(
                                              message='Логин пользователя не должен содержать символа @',
                                              regex=r'^[^@]+$'),
                                          StaffUser(),
                                          ],
                              description=u'Список логинов со staff, за которыми будет закреплен '
                                          u'сервис. Логины перечисляются через запятую. Пример: '
                                          u'user1, user2, user3',
                              )
    service_type = RadioField(u'Тип сервиса',
                              choices=[('search', 'search'), ('kv', 'key_value')],
                              default='search',
                              description=u'Выберите тип сервиса: полнотекстовый поиск или ключ-значение')
    instances_count = IntegerField(u'Количество шардов',
                                   validators=[validators.number_range(
                                       min=1, max=30,
                                       message=u'Укажите количество инстансов от 1 до 30'),
                                       DataRequired(u'Поле не может быть пустым')],
                                   default=1,
                                   description=u'Количество шардов в реплике. Рассчитывается, исходя из планируемого количества данных. '
                                               u'Рекомендуется не более 100Гб данных на 1 шард.'
                                   )
    replicas_per_dc = IntegerField(u'Количество реплик',
                                   validators=[validators.number_range(
                                       min=1,
                                       message=u'Укажите значение от 1 до 10'),
                                       DataRequired(u'Поле не может быть пустым')],
                                   default=1,
                                   description=u'Количество реплик. Рекомендуется не менее двух.')

    comment = TextAreaField(u'Комментарий',
                            validators=[DataRequired(u'Поле не может быть пустым')],
                            description=u'Опишите здесь назначение сервиса, пожелания и комментарии.'
                            )

    submit = SubmitField(u'Отправить')


class PrestableServiceForm(TestingServiceForm):
    service_name = StringField(u'Имя сервиса',
                               validators=[validators.Length(
                                   min=3, max=20, message=u'Длина имени сервиса должна состоять от 3х до 20 символов'),
                                   validators.Regexp(
                                       message=u'Имя сервиса может состоять только из латинских букв, цифр и символов "_".',
                                       regex=r'^[a-z0-9]+[a-z_0-9]+[a-z0-9]+$'),
                                   validators.Regexp(
                                       message=u'Запрещены в имени сервиса слова prestable/stable/test/testing/production/prod/infra/proxy/__',
                                       regex=r'^((?!stable|test|prestable|prod|production|infra|proxy__).)*$'),
                                   DataRequired(u'Укажите имя сервиса')],
                               description=u'Имя сервера может состоять из латинских букв цифр и символов "_". '
                                           u'Пример: lingv_boosding_ssd_1')
    service_tvm = StringField(u'Пользовательский tvm_id', description='Пользовательский tvm_id для хождения в searchproxy. '
                                                                      'В случае, если tvm_id не указан, будет создан тикет в '
                                                                      'очереди SAASSEC c просьбой обосновать отказ.')
    default = True,
    render_kw = {'checked': ''}
    service_saas_tvm = BooleanField(u'Доступ команде SaaS для отладки',
                                    default=True,
                                    render_kw={'checked': ''},
                                    description=u'Разрешить команде SaaS ходить в сервис со своим tvm_id для отладки')
    instances_count = IntegerField(u'Шардов на реплику',
                                   validators=[validators.number_range(
                                       min=1, max=40,
                                       message=u'Укажите количество инстансов от 1 до 40'),
                                       DataRequired(u'Поле не может быть пустым')],
                                   default=1,
                                   description=u'Количество шардов в реплике. Рассчитывается, исходя из планируемого количества данных. '
                                               u'Рекомендуется не более 100Гб данных на 1 шард.'
                                   )
    service_ctype = SelectField(u'Cluster type (ctype) сервиса',
                                choices=[tuple([ctype, ctype]) for ctype in ctypes if ctype not in ['testing'] and not ctype.startswith('stable')],
                                default='prestable',
                                description=u'Выберите cluster type сервиса. Внимание:'
                                            u'не каждый тип сервиса работает в указанном ctype')
    service_type = RadioField(u'Тип сервиса',
                              choices=[('search', 'search'), ('kv', 'key_value'), ('dj', 'dj')],
                              default='search',
                              validators=[TypeValidIf()],
                              description=u'Выберите тип сервиса: полнотекстовый поиск, '
                                          u'ключ-значение или kv-dj')
    memory_requirements = IntegerField(u'Память на слот (Gb)',
                                       description=u'Количество оперативной памяти на шард.',
                                       validators=[validators.number_range(
                                           min=5,
                                           message=u'Укажите значение от 5 до 50Гб'),
                                           DataRequired(u'Поле не может быть пустым')],
                                       default=10)
    cpu_requirements = IntegerField(u'CPU (cores)',
                                    description=u'Количество ядер на шард. Для большинства запросов хватает 2х.',
                                    validators=[validators.number_range(
                                        min=1,
                                        message=u'Укажите значение от 1 до 10'),
                                        DataRequired(u'Поле не может быть пустым')],
                                    default=1)
    replicas_per_dc = IntegerField(u'Количество реплик в локации',
                                   validators=[validators.number_range(
                                       min=1,
                                       message=u'Укажите значение от 1 до 6'),
                                       DataRequired(u'Поле не может быть пустым')],
                                   default=2,
                                   description=u'Количество реплик в локации. Рекомендуется не менее двух.')
    delivery_type = SelectField(u'Способ доставки данных',
                                choices=[('ferryman_snapshot', 'Ferryman batch (варка в YT)'),
                                         ('ferryman_delta', 'Ferryman delta (Бинарные дельты)'),
                                         ('logbroker', 'LogBroker(PQ)'), ('snapshot', 'Самостоятельная варка индекса в YT'),
                                         ('ferryman_mapreduce', 'Ferryman подокументная (deprecated)')],
                                description=u'Ознакомиться с вариантами доставки данных можно на странице '
                                            u'https://wiki.yandex-team.ru/jandekspoisk/SaaS/ в разделе '
                                            u'"Доставка данных до сервиса".',
                                default=('ferryman_mapreduce', 'Ferryman подокументная')
                                )
    yt_cluster = SelectField(u'YT кластер', choices=[('', 'Не выбрано'), ('arnold', 'arnold'), ('hahn', 'hahn')],
                             description=u'Выберете кластер в yt, если выбран yt в способах доставки данных',
                             default='',
                             validators=[RequiredIf(delivery_type=['ferryman_snapshot', 'ferryman_mapreduce', 'ferryman_delta',
                                                                   'snapshot', 'mapreduce'],
                                                    message=u'Выберите yt кластер')]
                             )
    yt_ferryman_format = SelectField(u'Формат таблиц Ferryman',
                                     choices=[('', 'Не выбрано'), ('message', 'message'), ('json', 'json'), ('simple', 'simple'),
                                              ('personal', 'personal'), ('geo_yt', 'geo_yt'), ('query_data', 'query_data')],
                                     description='Укажите формат входных таблиц для Ferryman. Требуется только если выбран Ferryman как '
                                                 'способ доставки данных. Более подробную информацию можно прочитать на вики '
                                                 'https://wiki.yandex-team.ru/jandekspoisk/SaaS/Ferryman/#formatvxodnyxtablic',
                                     default='',
                                     validators=[RequiredIf(delivery_type=['ferryman_snapshot', 'ferryman_mapreduce'],
                                                            message=u'Выберите формат таблиц')
                                                 ]
                                     )
    logbroker_tvm_id = StringField(u'Пользовательский tvm_id', description='Подробности в тикете LOGBROKER-3295',
                                   validators=[RequiredIf(delivery_type=['logbroker'], message='Поле не может быть пустым')]
                                   )
    logbroker_traffic_volume = FloatField(u'Трафик на индексацию (Мбайт/с)',
                                          description='Оптимальное значение 0.1. Значения более 1 Мбайта/сек потребуют'
                                                      'дополнительного согласования',
                                          validators=[RequiredIf(delivery_type=['logbroker'], message='Поле не может быть пустым')],
                                          default=0.1
                                          )


class ProductionServiceForm(PrestableServiceForm):
    service_ctype = SelectField(u'Cluster type (ctype) сервиса',
                                choices=[tuple([ctype, ctype]) for ctype in ctypes if ctype not in ['prestable', 'testing'] and 'prestable' not in ctype],
                                default='stable',
                                description=u'Выберите cluster type сервиса. Внимание, экспериментальная возможность,'
                                            u'не каждый тип сервиса работает в указанном ctype')
    replicas_per_dc = IntegerField(u'Реплик в локации',
                                   validators=[validators.number_range(
                                       min=1,
                                       message=u'Укажите значение от 1 до 6'),
                                       DataRequired(u'Поле не может быть пустым'),
                                       IntValidIf(search_q_99_ms=30, search_q_999_ms=50, operator='<', min_value=3,
                                                  message='Поисковой квантиль слишком низкий, укажите 3 и более реплики на локацию')],
                                   default=2,
                                   description=u'Количество реплик в локации. Рекомендуется не менее двух.')
    memory_requirements = IntegerField(u'Память на слот (Gb)',
                                       description=u'Количество оперативной памяти на шард.',
                                       validators=[validators.number_range(
                                           min=5,
                                           message=u'Укажите значение от 5 до 50Гб'),
                                           DataRequired(u'Поле не может быть пустым')],
                                       default=10)
    cpu_requirements = IntegerField(u'CPU (cores)',
                                    description=u'Количество ядер на шард. Для большинства запросов хватает 2х.',
                                    validators=[validators.number_range(
                                        min=1,
                                        message=u'Укажите значение от 1 до 10'),
                                        DataRequired(u'Поле не может быть пустым')],
                                    default=2)

    # SLA info
    search_rps = StringField(u'Поисковой RPS',
                             description=u'Количество запросов в секунду, которое должен обрабатывать сервис',
                             validators=[DataRequired(u'Поле не может быть пустым'),
                                         CountOf('decimal', message='Неверный формат ввода данных')],
                             render_kw={"placeholder": "Например: 10"})
    index_size = StringField(u'Размер индекса',
                             description=u'Общий объём документов находящихся в сервисе (можно посмотреть, например, '
                                         u'на данные в YT). Ответственным за сервис будет отправлено оповещение при '
                                         u'превышении объёма данных. '
                                         u'Поддерживаются сокращения: Kb - килобайты, Mb - мегабайты, Gb - гигабайты, Тb - терабайты',
                             validators=[DataRequired(u'Поле не может быть пустым'),
                                         CountOf('bytes', message='Неверный формат ввода данных')],
                             render_kw={"placeholder": "Например: 100Gb"})
    maxdocs = StringField(u'Количество документов',
                          description=u'Максимальное количество документов (или объектов для kv-storage). '
                                      u'Ответственным за сервис будет отправлено оповещение, при превышении '
                                      u'количества документов. '
                                      u'Поддерживаются сокращения: K - тысячи, М - миллионы, B - миллиарды',
                          validators=[DataRequired(u'Поле не может быть пустым'),
                                      CountOf('decimal', message='Неверный формат ввода данных')],
                          render_kw={"placeholder": "Например: 10M"})
    search_q_99_ms = IntegerField(u'99й квантиль ответа (ms)',
                                  description=u'Желаемое время ответа в миллисекундах для 99% процентов запросов на '
                                              u'поиск (к поиску или к kv)',
                                  validators=[DataRequired(u'Поле не может быть пустым'),
                                              validators.number_range(min=10, message=u'99й квантиль ответа не может быть менее 10мс')],
                                  render_kw={"placeholder": "Например: 100"})
    search_q_999_ms = IntegerField(u'999й квантиль ответа (ms)',
                                   description=u'Желаемое время ответа в миллисекундах для 99,9% процентов запросов на '
                                               u'поиск (к поиску или к kv)',
                                   validators=[DataRequired(u'Поле не может быть пустым'),
                                               validators.number_range(
                                                   min=15,
                                                   message=u'999й квантиль ответа не может быть менее 15мс'),
                                               ],
                                   render_kw={"placeholder": "Например: 150"})
