"""Снимок разговора с ботом: все входящие сообщения сценария + раскладка кнопок.

Отличие от tg_client: тот отдаёт последний текст, а для оценки интерфейса нужны
ВСЕ сообщения ответа (боты часто шлют два-три подряд) и структура клавиатуры —
сколько кнопок, как разложены по рядам, какой длины подписи.

Запуск (на mini, где живут сессия и прокси):
    TG_PROXY_URL=http://127.0.0.1:2081 python probe.py <bot_username> <out.json> [шаг ...]

Шаг — либо текст команды («/start»), либо «tap:Подпись кнопки».
"""
import asyncio, json, os, sys, time

sys.path.insert(0, "/tmp")
from tg_client import TgWebClient  # noqa: E402

BOT, OUT = sys.argv[1], sys.argv[2]
STEPS = sys.argv[3:] or ["/start"]


async def snapshot(pg, since_mid: int):
    """Все входящие пузыри новее since_mid: текст, кнопки, ряды."""
    js = """
    (since) => {
      const out = [];
      document.querySelectorAll('.bubbles-inner .bubble.is-in[data-mid]').forEach(b => {
        const mid = Number(b.getAttribute('data-mid'));
        if (mid <= since) return;
        const msg = b.querySelector('.message');
        const text = msg ? (msg.innerText || '').trim() : '';
        // ряды инлайн-клавиатуры: каждый .reply-markup-row — строка кнопок
        const rows = [...b.querySelectorAll('.reply-markup-row')].map(r =>
          [...r.querySelectorAll('.reply-markup-button')].map(x => (x.innerText || '').trim()));
        const flat = [...b.querySelectorAll('.reply-markup-button')].map(x => (x.innerText || '').trim());
        out.push({ mid, text, rows, buttons: rows.length ? rows : (flat.length ? [flat] : []) });
      });
      return out;
    }"""
    return await pg.evaluate(js, since_mid)


async def last_bubble(pg):
    """Отпечаток последнего входящего пузыря: текст + подписи кнопок."""
    return await pg.evaluate("""() => {
      const b = [...document.querySelectorAll('.bubbles-inner .bubble.is-in[data-mid]')].pop();
      if (!b) return '';
      const t = (b.querySelector('.message')?.innerText || '').trim();
      const k = [...b.querySelectorAll('.reply-markup-button')].map(x => x.innerText.trim()).join('|');
      return t + '##' + k;
    }""")


async def main():
    rec = {"bot": BOT, "steps": []}
    async with TgWebClient(headless=True) as tg:
        await tg.open_chat(BOT)
        await tg.wait_for_count_stable()
        for step in STEPS:
            before = await tg.incoming_count()
            if step.startswith("tap:"):
                label = step[4:]
                btn = tg.page.locator(f'.reply-markup-button:has-text("{label}")').last
                await btn.click()
                t0 = time.time()          # клик мгновенный — отсчёт сразу
                action = {"kind": "tap", "label": label}
            else:
                # Отсчёт СТРОГО после того, как сообщение ушло: Playwright набирает
                # текст посимвольно, и включённый в замер набор давал ровно ~4.4с
                # на каждом шаге — это выглядело как «все боты тормозят».
                await tg.send(step)
                t0 = time.time()
                action = {"kind": "send", "text": step}
            # Инлайн-кнопка чаще РЕДАКТИРУЕТ сообщение, а не шлёт новое: ждать
            # только роста data-mid — значит не дождаться ничего. Поэтому ждём
            # либо новый пузырь, либо изменение последнего.
            fingerprint = await last_bubble(tg.page)
            last, stable_since = before, None
            edited = False
            while time.time() - t0 < 25:
                await asyncio.sleep(0.25)
                cur = await tg.incoming_count()
                now_fp = await last_bubble(tg.page)
                if cur != last or (not edited and now_fp != fingerprint):
                    if now_fp != fingerprint and cur == last:
                        edited = True
                    last, stable_since = cur, None
                elif cur > before or edited:
                    stable_since = stable_since or time.time()
                    if time.time() - stable_since > 1.5:
                        break
            msgs = await snapshot(tg.page, before)
            if not msgs and edited:
                msgs = await snapshot(tg.page, before - 1)
                for m in msgs:
                    m["edited_in_place"] = True
            rec["steps"].append({**action, "latency_s": round(time.time() - t0, 2), "messages": msgs})
            print(f"  {step!r}: {len(msgs)} сообщ., {round(time.time()-t0,1)}с")
    json.dump(rec, open(OUT, "w"), ensure_ascii=False, indent=2)
    print("записано в", OUT)


asyncio.run(main())
