"""Tests for MockBot and TestClient testing utilities."""

import pytest

from dexfather.conversation import END, ConversationHandler
from dexfather.filters import COMMAND, TEXT
from dexfather.handlers import (
    CallbackQueryHandler,
    CommandHandler,
    InlineQueryHandler,
    MessageHandler,
    PollAnswerHandler,
)
from dexfather.testing import MockBot, TestClient


@pytest.mark.asyncio
async def test_mock_bot_no_http():
    """MockBot creates without network access."""
    bot = MockBot()
    assert bot.token == "mock_test_token"
    assert bot.sent_messages == []
    assert bot._handlers == []


@pytest.mark.asyncio
async def test_mock_bot_records_send_message():
    """MockBot records send_message API calls."""
    bot = MockBot()
    await bot.send_message(1, "hi")
    assert len(bot.sent_messages) == 1
    assert bot.sent_messages[0]["method"] == "sendMessage"
    assert bot.sent_messages[0]["text"] == "hi"
    assert bot.sent_messages[0]["chat_id"] == 1


@pytest.mark.asyncio
async def test_mock_bot_reset():
    """MockBot.reset() clears all recorded messages."""
    bot = MockBot()
    await bot.send_message(1, "hi")
    assert len(bot.sent_messages) == 1
    bot.reset()
    assert bot.sent_messages == []


@pytest.mark.asyncio
async def test_client_send_message():
    """TestClient.send_message dispatches through handlers."""
    bot = MockBot()
    received = []

    @bot.on_message()
    async def on_text(update, bot):
        received.append(update.message.text)
        await bot.send_message(update.message.chat.id, f"echo: {update.message.text}")

    client = TestClient(bot)
    update = await client.send_message("hello")

    assert update.message.text == "hello"
    assert len(received) == 1
    assert received[0] == "hello"
    assert len(bot.sent_messages) == 1
    assert bot.sent_messages[0]["text"] == "echo: hello"


@pytest.mark.asyncio
async def test_client_send_command():
    """TestClient.send_command dispatches to CommandHandler."""
    bot = MockBot()

    @bot.command("start")
    async def on_start(update, bot):
        await bot.send_message(update.message.chat.id, "Welcome!")

    client = TestClient(bot)
    await client.send_command("/start")

    assert len(bot.sent_messages) == 1
    assert bot.sent_messages[0]["method"] == "sendMessage"
    assert bot.sent_messages[0]["text"] == "Welcome!"


@pytest.mark.asyncio
async def test_client_send_callback_query():
    """TestClient.send_callback_query dispatches to CallbackQueryHandler."""
    bot = MockBot()
    received_data = []

    @bot.on_callback_query()
    async def on_callback(update, bot):
        received_data.append(update.callback_query.data)
        await bot.answer_callback_query(update.callback_query.id, text="Done!")

    client = TestClient(bot)
    await client.send_callback_query("confirm:yes")

    assert received_data == ["confirm:yes"]
    assert len(bot.sent_messages) == 1
    assert bot.sent_messages[0]["method"] == "answerCallbackQuery"


@pytest.mark.asyncio
async def test_client_send_inline_query():
    """TestClient.send_inline_query dispatches to InlineQueryHandler."""
    bot = MockBot()
    received_queries = []

    @bot.on_inline_query()
    async def on_inline(update, bot):
        received_queries.append(update.inline_query.query)
        await bot.answer_inline_query(update.inline_query.id, results=[])

    client = TestClient(bot)
    await client.send_inline_query("search term")

    assert received_queries == ["search term"]
    assert len(bot.sent_messages) == 1
    assert bot.sent_messages[0]["method"] == "answerInlineQuery"


@pytest.mark.asyncio
async def test_client_send_poll_answer():
    """TestClient.send_poll_answer dispatches to PollAnswerHandler."""
    bot = MockBot()
    received_polls = []

    @bot.on_poll_answer()
    async def on_poll(update, bot):
        received_polls.append(update.poll_answer.poll_id)

    client = TestClient(bot)
    await client.send_poll_answer("poll_123", [0, 2])

    assert received_polls == ["poll_123"]


@pytest.mark.asyncio
async def test_handler_with_filters_via_client():
    """MessageHandler with filters correctly routes through TestClient."""
    bot = MockBot()
    text_received = []

    async def on_text(update, bot):
        text_received.append(update.message.text)

    bot.add_handler(MessageHandler(
        on_text,
        filters=TEXT & ~COMMAND,
    ))

    client = TestClient(bot)

    # Non-command text should fire
    await client.send_message("hello")
    assert len(text_received) == 1

    # Command should NOT fire (filtered out by ~COMMAND)
    await client.send_message("/start")
    assert len(text_received) == 1  # Still 1


@pytest.mark.asyncio
async def test_conversation_handler_via_client():
    """ConversationHandler state transitions work through TestClient."""
    bot = MockBot()

    # States
    NAME, AGE = range(2)

    async def ask_name(update, bot):
        await bot.send_message(update.message.chat.id, "What's your name?")
        return NAME

    async def process_name(update, bot):
        await bot.send_message(update.message.chat.id, f"Hi {update.message.text}! How old are you?")
        return AGE

    async def process_age(update, bot):
        await bot.send_message(update.message.chat.id, f"Got it, you are {update.message.text}.")
        return END

    conv = ConversationHandler(
        entry_points=[CommandHandler("start", ask_name)],
        states={
            NAME: [MessageHandler(process_name)],
            AGE: [MessageHandler(process_age)],
        },
        fallbacks=[],
    )
    bot.add_handler(conv)

    client = TestClient(bot)

    # Start conversation
    await client.send_command("/start")
    assert len(bot.sent_messages) == 1
    assert bot.sent_messages[0]["text"] == "What's your name?"

    # Send name -- transitions to AGE state
    await client.send_message("Alice")
    assert len(bot.sent_messages) == 2
    assert bot.sent_messages[1]["text"] == "Hi Alice! How old are you?"

    # Send age -- conversation ends
    await client.send_message("25")
    assert len(bot.sent_messages) == 3
    assert bot.sent_messages[2]["text"] == "Got it, you are 25."
