"""Только кнопки в боте, без команд с косой чертой (R16).

Владелец сказал дословно: «требуется чтобы были только кнопки в боте без
команд, так удобнее и перед глазами».

Проверяются ключевые правила:
1. Кнопки снизу («Список», «Следующий», «Пауза», «Помощь») разбираются как команды.
2. Команды с косой чертой (/status, /list, /pause, /resume, /help) НЕ разбираются
   как команды и возвращают None в parse_command (рычаг порчи).
3. /start остаётся точкой входа для приветствия и привязки чата.
4. Обычный текст остаётся не-командой.
5. В тексте помощи и уведомлений нет упоминаний команд с косой чертой.
"""

from __future__ import annotations

import json
import subprocess
import sys
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).resolve().parent.parent))

from chekan import proof, telegram  # noqa: E402
from chekan.store import Run, now  # noqa: E402

TOKEN = "1234567890:AAHfake-token-for-tests-0123456789x"
OWNER = 424242


class FakeTelegram:
    def __init__(self):
        self.updates: list[dict] = []
        self.sent: list[dict] = []
        self._next = 1

    def say(self, text: str, chat: int = OWNER) -> dict:
        upd = {"update_id": self._next, "message": {"chat": {"id": chat}, "text": text}}
        self._next += 1
        self.updates.append(upd)
        return upd

    def __call__(self, token: str, method: str, params: dict | None = None, timeout: int = 10):
        params = dict(params or {})
        if method == "getMe":
            return {"username": "ceh_test_bot"}
        if method == "sendMessage":
            self.sent.append(params)
            return {"message_id": 100 + len(self.sent), "chat": {"id": params.get("chat_id")}}
        if method == "getUpdates":
            offset = int(params.get("offset") or 0)
            return [u for u in self.updates if u["update_id"] >= offset]
        return {}


@pytest.fixture
def tg(monkeypatch) -> FakeTelegram:
    fake = FakeTelegram()
    monkeypatch.setattr(telegram, "call", fake)
    return fake


@pytest.fixture
def repo(tmp_path: Path) -> Path:
    root = tmp_path / "proj"
    root.mkdir()
    subprocess.run(["git", "init", "-q"], cwd=root, check=True)
    subprocess.run(["git", "config", "user.email", "t@t"], cwd=root, check=True)
    subprocess.run(["git", "config", "user.name", "t"], cwd=root, check=True)
    (root / "app.py").write_text("print('ok')\n", encoding="utf-8")
    subprocess.run(["git", "add", "-A"], cwd=root, check=True)
    subprocess.run(["git", "commit", "-qm", "init"], cwd=root, check=True)
    return root


@pytest.fixture
def run(repo: Path) -> Run:
    r = Run.create(repo, "Тестовый прогон", "normal", "T1")
    r.add_requirement("R16", "только кнопки")
    r.add_task("01", "задача 1", ["R16"], verify="true")
    (repo / ".chekan" / "active").write_text(r.slug + "\n", encoding="utf-8")
    return r


@pytest.fixture(autouse=True)
def cfg(tmp_path: Path, monkeypatch):
    monkeypatch.setenv(telegram.CONFIG_ENV, str(tmp_path / "cfg" / "telegram.json"))
    telegram.save({"token": TOKEN, "bot": "ceh_test_bot", "owner_id": str(OWNER)})


def _runs_root(run: Run) -> Path:
    return run.root / ".chekan"


# --- Разбор только кнопок ---


@pytest.mark.parametrize(
    "text,expected",
    [
        ("Список", "list"),
        ("список", "list"),
        ("  СПИСОК  ", "list"),
        ("Следующий", "next"),
        ("следующий", "next"),
        ("Пауза", "pause"),
        ("пауза", "pause"),
        ("Помощь", "help"),
        ("помощь", "help"),
        ("/start", "start"),
        ("/start@ceh_test_bot", "start"),
    ],
)
def test_parse_command_accepts_buttons_and_start(text, expected):
    """Кнопки разбираются как команды, регистр и пробелы игнорируются."""
    assert telegram.parse_command(text) == expected


@pytest.mark.parametrize(
    "slash_cmd",
    [
        "/status",
        "/status@ceh_test_bot",
        "/list",
        "/list@ceh_test_bot",
        "/pause",
        "/resume",
        "/help",
        "/dance",
    ],
)
def test_parse_command_rejects_slash_commands(slash_cmd):
    """Рычаг порчи: команды с косой чертой больше не разбираются."""
    assert telegram.parse_command(slash_cmd) is None, f"{slash_cmd} не должен быть командой"


def test_plain_text_and_empty_strings_are_not_commands():
    assert telegram.parse_command("обычный текст ответа") is None
    assert telegram.parse_command("да, всё верно") is None
    assert telegram.parse_command("") is None
    assert telegram.parse_command("   ") is None


# --- Кнопки снизу в разметке и ответах ---


def test_keyboard_contains_all_four_buttons():
    markup = json.loads(telegram.keyboard())
    buttons = [btn for row in markup.get("keyboard", []) for btn in row]
    assert telegram.BTN_LIST in buttons
    assert telegram.BTN_NEXT in buttons
    assert telegram.BTN_PAUSE in buttons
    assert telegram.BTN_HELP in buttons


def test_help_text_does_not_contain_slash_commands():
    """Рычаг порчи: в подсказке не должно быть команд со слэшем."""
    assert "/status" not in telegram.HELP
    assert "/pause" not in telegram.HELP
    assert "/resume" not in telegram.HELP
    assert "/list" not in telegram.HELP
    assert "/next" not in telegram.HELP
    assert "/help" not in telegram.HELP
    assert "«Список»" in telegram.HELP
    assert "«Следующий»" in telegram.HELP
    assert "«Пауза»" in telegram.HELP
    assert "«Помощь»" in telegram.HELP


def test_pause_text_does_not_contain_slash_command(run: Run):
    """В сообщении о паузе не должно быть /resume."""
    text = telegram.pause_text(run, paused=True)
    assert "/resume" not in text


# --- Выполнение команд через нажатие кнопок ---


def test_button_list_executes_runs_text(tg: FakeTelegram, run: Run):
    telegram.connect(TOKEN)
    telegram.update(chat_id=OWNER)
    _, reply = telegram.handle_update(tg.say("Список"), _runs_root(run))
    assert "Прогоны проекта" in reply
    assert "Тестовый прогон" in reply


def test_button_help_returns_help(tg: FakeTelegram, run: Run):
    telegram.connect(TOKEN)
    telegram.update(chat_id=OWNER)
    _, reply = telegram.handle_update(tg.say("Помощь"), _runs_root(run))
    assert "Чекан на связи" in reply


def test_button_pause_pauses_run(tg: FakeTelegram, run: Run, repo: Path):
    telegram.connect(TOKEN)
    telegram.update(chat_id=OWNER)
    _, reply = telegram.handle_update(tg.say("Пауза"), _runs_root(run))
    assert "паузе" in reply
    assert Run.load(repo).is_paused()
