"""Механическая мерка снимка разговора. Считает то, что можно посчитать,
и не притворяется, что судит вкус.

Запуск: python measure.py <snapshot.json> [snapshot.json ...]

Пороги выведены из ограничений самого Telegram и телефонного экрана,
а не из вкуса: подпись кнопки шире ~20 символов режется на 360px,
три кнопки в ряду на телефоне не помещаются, сообщение длиннее 700
символов на экране телефона превращается в стену.
"""
import json, re, sys

EMOJI = re.compile(
    "[\U0001F1E0-\U0001F6FF\U0001F900-\U0001FAFF←-⇿☀-➿⬀-⯿️⁉‼]"
)
# Обороты, по которым текст читается как сгенерированный маркетинг.
SLOP = [
    "всего за пару кликов", "буквально за", "легко и просто", "просто и удобно",
    "давайте начнём", "давай начнём", "добро пожаловать в мир",
    "мы рады приветствовать", "не упусти", "прямо сейчас", "уже сегодня",
    "революционн", "уникальн", "лучший помощник", "твой личный помощник",
]
LIMITS = {"btn_label": 20, "btns_per_row": 2, "msg_chars": 700, "latency_s": 3.0}

# Telegram Web подставляет в подпись кнопки служебный глиф иконки (Private Use Area)
# и отделяет его переводом строки. Это делает DOM, а не бот: считать за дефект нельзя.
PUA = re.compile("[\uE000-\uF8FF]")


def label_of(raw: str) -> str:
    return PUA.sub("", raw).replace("\n", " ").strip()


def check(snap):
    out, bot = [], snap["bot"]
    for st in snap["steps"]:
        who = st.get("text") or ("tap:" + st.get("label", ""))
        if st["latency_s"] > LIMITS["latency_s"]:
            out.append((bot, who, "медленный ответ",
                        f"{st['latency_s']}с до первого сообщения при пороге {LIMITS['latency_s']}"))
        msgs = st["messages"]
        if not msgs:
            out.append((bot, who, "нет ответа", "бот промолчал — пользователь не знает, услышали ли его"))
        if len(msgs) > 2:
            out.append((bot, who, "очередь сообщений",
                        f"{len(msgs)} сообщений подряд — лента прыгает, читать нечего"))
        for m in msgs:
            body = re.sub(r"\n\d{1,2}:\d{2}\s*[AP]M\s*$", "", m["text"]).strip()
            n = len(body)
            if n > LIMITS["msg_chars"]:
                out.append((bot, who, "стена текста", f"{n} символов в одном сообщении"))
            # Эмодзи внутри «кавычек» — это ИМЯ КНОПКИ, а не маркер списка:
            # подпись в тексте обязана совпадать с подписью на клавиатуре
            # буква в букву, иначе человек не найдёт кнопку глазами.
            e = len(EMOJI.findall(re.sub(r"«[^»]{0,40}»", "", body)))
            if e >= 4:
                out.append((bot, who, "эмодзи вместо структуры",
                            f"{e} эмодзи на {n} символов — маркеры списка сделаны картинками"))
            low = body.lower()
            for s in SLOP:
                if s in low:
                    out.append((bot, who, "штампованный оборот", f"«{s}»"))
            if body and n < 40 and not m["buttons"]:
                out.append((bot, who, "пустое сообщение", f"«{body[:40]}» — ни смысла, ни кнопок"))
            for row in m["buttons"]:
                if len(row) > LIMITS["btns_per_row"]:
                    out.append((bot, who, "тесный ряд кнопок",
                                f"{len(row)} в ряду: {row} — на телефоне подписи режутся"))
                for b in row:
                    lbl = label_of(b)
                    if len(lbl) > LIMITS["btn_label"]:
                        out.append((bot, who, "длинная подпись кнопки",
                                    f"«{lbl}» — {len(lbl)} симв. при пороге {LIMITS['btn_label']}"))
    return out


def vocabulary(snaps):
    """Одно ли слово у одного действия во всех ботах семьи."""
    verbs = {}
    for s in snaps:
        for st in s["steps"]:
            for m in st["messages"]:
                for row in m["buttons"]:
                    for b in row:
                        w = re.sub(r"^[^\wА-Яа-я]+", "", label_of(b)).split()
                        if w:
                            verbs.setdefault(w[0].lower(), set()).add(s["bot"])
    return verbs


if __name__ == "__main__":
    snaps = [json.load(open(p, encoding="utf-8")) for p in sys.argv[1:]]
    total = 0
    for s in snaps:
        f = check(s)
        total += len(f)
        print(f"\n═══ {s['bot']}: {len(f)} находок")
        for _, who, kind, detail in f:
            print(f"  [{kind}] на {who}: {detail}")
    if len(snaps) > 1:
        print("\n═══ единообразие семьи (первое слово подписи → в каких ботах)")
        for w, bots in sorted(vocabulary(snaps).items()):
            if len(bots) > 1:
                print(f"  общее: «{w}» — {', '.join(sorted(bots))}")
    print(f"\nВСЕГО: {total}")
