# coding: utf-8
import re


TEMPLATE_CHAR = re.compile(r'#')
IGNORED_CHARS = re.compile(r"""[.,!"':;]""")
SPEC_CHARS = re.compile(r"""[\[\]!+]""")
IGNORED_MAX_COUNT = 15

def _drop_antiwords(phrase):
    antiword_start_idx = phrase.find(" -")
    if antiword_start_idx > 0:
        return phrase[: antiword_start_idx].strip()
    return phrase

def _process_phrase(phrase):
    return SPEC_CHARS.sub('', _drop_antiwords(phrase)).strip()

def _has_template(text):
    return text.count('#') >= 2

def _size_of(text):
    u"""размер текста без учета пунктуации с ограниченим
        по количеству игнорируемых символов"""
    _, replacements = IGNORED_CHARS.subn('', text)
    return len(text) - min(replacements, IGNORED_MAX_COUNT)


def try_format_template(text, phrase, max_size):
    u"""Пытается подставить `phrase` в `text` вместо шаблона (#{to_replace}#) с контролем по `max_size`.

    Signature:
    (Utf8?, Utf8?, Uint?) -> Utf8?

    Note:
    Известные ограничения (на 2017-09-19):
    * Основной заголовок - 35
    * Вспомогательный заголовок - 30
    * Тело баннера - 81
    * При подсчете длины после подстановок не учитывается до 15 символов пунктуации.
    * !Парность # не проверяется, избыток затирается!
    * `phrase` очищается от `SPEC_CHARS`
    * минус-слова из `phrase` отбрасываются

    [Подробнее про шаблоны](https://yandex.ru/support/direct/features/ad-templates.html?lang=ru)
    """
    if text is None:
        return None

    if phrase is None:
        return TEMPLATE_CHAR.sub('', text).strip()

    phrase = _process_phrase(phrase)
    if not phrase:
        return TEMPLATE_CHAR.sub('', text).strip()

    if not isinstance(text, unicode):
        raise TypeError("type of `text` is not unicode")
    if not isinstance(phrase, unicode):
        raise TypeError("type of `phrase` is not unicode")
    if max_size and max_size < 0:
        raise ValueError("`max_size` is < 0")

    if not _has_template(text):
        return text.strip()

    formated_text = re.sub(r'(#.*?#)', phrase, text)
    formated_text = TEMPLATE_CHAR.sub('', formated_text).strip()

    if max_size is None:
        return formated_text
    if max_size and (_size_of(formated_text) <= max_size):
        return formated_text

    return TEMPLATE_CHAR.sub('', text).strip()


assert try_format_template(u"купить -слова", None, 100) == u"купить -слова"
assert try_format_template(u"Купить ##", u"добра -слова", 100) == u"Купить добра"
assert try_format_template(u"Купить ##", u"добра [или зло] -слова", 100) == u"Купить добра или зло"
assert try_format_template(u"##", u"Купить в Москве-Питере всякого добра -слова -много", 100) \
    == u"Купить в Москве-Питере всякого добра"
assert try_format_template(u"купить", "", 100) == u"купить"
assert try_format_template(u"купить", u"+слова", 100) == u"купить"
assert try_format_template(u"купить #####", u"df", 100) == u"купить dfdf"
assert try_format_template(u"купить ##", u"слова", 1) == u"купить"
assert try_format_template(u"купить ##!!!", u"+слова", 12) == u"купить слова!!!"
assert try_format_template(u"купить ##!!!", u"слова", 11) == u"купить !!!"
assert try_format_template(u"купить #test#!!!", u"!слова", 100) == u"купить слова!!!"
assert try_format_template(u"купить #Test#", u"слова продать", 1) == u"купить Test"

