"""Tests for update handlers and backwards compatibility."""

import pytest

from dexfather.filters import TEXT, COMMAND, Regex
from dexfather.handlers import (
    CallbackQueryHandler,
    ChatJoinRequestHandler,
    ChatMemberHandler,
    ChosenInlineResultHandler,
    CommandHandler,
    InlineQueryHandler,
    MessageHandler,
    PollAnswerHandler,
    TypeHandler,
)
from dexfather.types import (
    CallbackQuery,
    Chat,
    ChatJoinRequest,
    ChatMember,
    ChatMemberUpdated,
    InlineQuery,
    Message,
    PollAnswer,
    Update,
    User,
)


async def _noop(update, bot):
    """No-op callback for handler tests."""
    pass


def test_command_handler_matches(command_update):
    """CommandHandler matches /start command."""
    handler = CommandHandler("start", _noop)
    assert handler.check(command_update) is True


def test_command_handler_rejects_text(text_update):
    """CommandHandler rejects plain text messages."""
    handler = CommandHandler("start", _noop)
    assert handler.check(text_update) is False


def test_message_handler_default(text_update, command_update):
    """Default MessageHandler matches text, skips commands."""
    handler = MessageHandler(_noop)
    assert handler.check(text_update) is True
    assert handler.check(command_update) is False


def test_message_handler_pattern(text_update):
    """MessageHandler with pattern matches regex."""
    handler = MessageHandler(_noop, pattern=r"^hello")
    assert handler.check(text_update) is True


def test_message_handler_pattern_no_match():
    """MessageHandler with pattern rejects non-matching text."""
    update = Update(
        update_id=10,
        message=Message(
            message_id=1,
            chat=Chat(id=1, type="private"),
            text="goodbye world",
            date=0,
        ),
    )
    handler = MessageHandler(_noop, pattern=r"^hello")
    assert handler.check(update) is False


def test_message_handler_filters(text_update):
    """MessageHandler with filters= uses composable filter."""
    handler = MessageHandler(_noop, filters=TEXT & ~COMMAND)
    assert handler.check(text_update) is True


def test_message_handler_filters_allows_commands():
    """MessageHandler with filter mode does not skip commands (filter is truth)."""
    update = Update(
        update_id=10,
        message=Message(
            message_id=1,
            chat=Chat(id=1, type="private"),
            from_user=User(id=1),
            text="/custom arg",
            date=0,
        ),
    )
    handler = MessageHandler(_noop, filters=Regex(r"^/custom"))
    assert handler.check(update) is True


def test_message_handler_pattern_filters_conflict():
    """MessageHandler raises ValueError when both pattern and filters given."""
    with pytest.raises(ValueError, match="Cannot use both"):
        MessageHandler(_noop, pattern="x", filters=TEXT)


def test_callback_query_handler(callback_update):
    """CallbackQueryHandler matches callback query updates."""
    handler = CallbackQueryHandler(_noop)
    assert handler.check(callback_update) is True


def test_callback_query_handler_pattern(callback_update):
    """CallbackQueryHandler with pattern matches data."""
    handler = CallbackQueryHandler(_noop, pattern=r"^confirm:")
    assert handler.check(callback_update) is True

    handler_no = CallbackQueryHandler(_noop, pattern=r"^reject:")
    assert handler_no.check(callback_update) is False


def test_inline_query_handler():
    """InlineQueryHandler matches inline query updates."""
    update = Update(
        update_id=10,
        inline_query=InlineQuery(
            id="iq1",
            from_user=User(id=42),
            query="search term",
        ),
    )
    handler = InlineQueryHandler(_noop)
    assert handler.check(update) is True


def test_type_handler(poll_answer_update):
    """TypeHandler matches on update type field name."""
    handler = TypeHandler("poll_answer", _noop)
    assert handler.check(poll_answer_update) is True


def test_type_handler_no_match(text_update):
    """TypeHandler rejects updates without the target field."""
    handler = TypeHandler("poll_answer", _noop)
    assert handler.check(text_update) is False


def test_poll_answer_handler(poll_answer_update):
    """PollAnswerHandler matches poll_answer updates."""
    handler = PollAnswerHandler(_noop)
    assert handler.check(poll_answer_update) is True


def test_chat_member_handler():
    """ChatMemberHandler matches chat_member updates."""
    update = Update(
        update_id=10,
        chat_member=ChatMemberUpdated(
            chat=Chat(id=1, type="group"),
            from_user=User(id=42),
            date=0,
            old_chat_member=ChatMember(user=User(id=42), status="member"),
            new_chat_member=ChatMember(user=User(id=42), status="administrator"),
        ),
    )
    handler = ChatMemberHandler(_noop)
    assert handler.check(update) is True


def test_chat_join_request_handler():
    """ChatJoinRequestHandler matches chat_join_request updates."""
    update = Update(
        update_id=10,
        chat_join_request=ChatJoinRequest(
            chat=Chat(id=1, type="group"),
            from_user=User(id=42),
            date=0,
        ),
    )
    handler = ChatJoinRequestHandler(_noop)
    assert handler.check(update) is True


@pytest.mark.asyncio
async def test_handler_handle_calls_callback():
    """handler.handle() calls the registered callback."""
    called = False

    async def cb(update, bot):
        nonlocal called
        called = True

    handler = CommandHandler("start", cb)
    update = Update(
        update_id=1,
        message=Message(
            message_id=1,
            chat=Chat(id=1, type="private"),
            text="/start",
            date=0,
        ),
    )
    await handler.handle(update, None)
    assert called is True
