from unittest.mock import call, patch

import pytest
from telegram import error

from apps.base.models import UserNotificationPolicy, UserNotificationPolicyLogRecord
from apps.telegram.client import TelegramClient
from apps.telegram.models import TelegramMessage, TelegramToUserConnector


@patch.object(
    TelegramClient,
    "send_raw_message",
    side_effect=error.BadRequest(TelegramClient.BadRequestMessage.MESSAGE_TO_BE_REPLIED_NOT_FOUND),
)
@pytest.mark.django_db
def test_personal_connector_replied_message_not_found(
    mock_send_message,
    make_organization_and_user,
    make_telegram_user_connector,
    make_user_notification_policy,
    make_alert_receive_channel,
    make_alert_group,
    make_alert,
    make_telegram_message,
):
    # set up a user with Telegram account connected
    organization, user = make_organization_and_user()
    make_telegram_user_connector(user)
    notification_policy = make_user_notification_policy(
        user,
        UserNotificationPolicy.Step.NOTIFY,
        notify_by=UserNotificationPolicy.NotificationChannel.TELEGRAM,
        important=False,
    )

    # create an alert group with an existing Telegram message in user's DM
    alert_receive_channel = make_alert_receive_channel(organization)
    alert_group = make_alert_group(alert_receive_channel)
    make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
    telegram_message = make_telegram_message(
        alert_group=alert_group,
        message_type=TelegramMessage.PERSONAL_MESSAGE,
        chat_id=str(user.telegram_connection.telegram_chat_id),
    )

    # make sure no exception is raised when replying to the message that has been deleted
    user.telegram_connection.notify(alert_group=alert_group, notification_policy=notification_policy)
    mock_send_message.assert_called_once_with(
        chat_id=telegram_message.chat_id,
        text="One more notification about this 👆",
        reply_to_message_id=telegram_message.message_id,
    )


@pytest.mark.parametrize(
    "side_effect,notification_error_code,reason",
    [
        (
            error.Unauthorized(TelegramClient.UnauthorizedMessage.BOT_WAS_BLOCKED_BY_USER),
            UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_TELEGRAM_BOT_IS_DELETED,
            TelegramToUserConnector.NotificationErrorReason.BOT_BLOCKED_BY_USER,
        ),
        (
            error.Unauthorized(TelegramClient.UnauthorizedMessage.INVALID_TOKEN),
            UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_TELEGRAM_TOKEN_ERROR,
            TelegramToUserConnector.NotificationErrorReason.INVALID_TOKEN,
        ),
        (
            error.Unauthorized(TelegramClient.UnauthorizedMessage.USER_IS_DEACTIVATED),
            UserNotificationPolicyLogRecord.ERROR_NOTIFICATION_TELEGRAM_USER_IS_DEACTIVATED,
            TelegramToUserConnector.NotificationErrorReason.USER_WAS_DISABLED,
        ),
    ],
)
@pytest.mark.django_db
def test_personal_connector_send_link_to_channel_message_handle_exceptions(
    side_effect,
    notification_error_code,
    reason,
    make_organization_and_user,
    make_telegram_user_connector,
    make_user_notification_policy,
    make_alert_receive_channel,
    make_alert_group,
):
    # set up a user with Telegram account connected
    organization, user = make_organization_and_user()
    user_connector = make_telegram_user_connector(user)
    notification_policy = make_user_notification_policy(
        user,
        UserNotificationPolicy.Step.NOTIFY,
        notify_by=UserNotificationPolicy.NotificationChannel.TELEGRAM,
        important=False,
    )

    # create an alert group with an existing Telegram message in user's DM
    alert_receive_channel = make_alert_receive_channel(organization)
    alert_group = make_alert_group(alert_receive_channel)

    assert not user.personal_log_records.exists()

    with patch.object(TelegramClient, "send_message", side_effect=side_effect) as mock_send_message:
        user_connector.send_link_to_channel_message(alert_group, notification_policy)

    mock_send_message.assert_called_once()
    log_records = user.personal_log_records.filter(alert_group=alert_group)
    assert log_records.count() == 1
    assert log_records.first().notification_error_code == notification_error_code
    assert log_records.first().reason == reason


@patch.object(TelegramClient, "send_message")
@pytest.mark.django_db
def test_personal_connector_send_link_to_channel_message(
    mock_send_message,
    make_organization_and_user,
    make_telegram_user_connector,
    make_user_notification_policy,
    make_alert_receive_channel,
    make_alert_group,
    make_alert,
    make_telegram_channel,
    make_telegram_message,
):
    # set up a user with Telegram account connected
    organization, user = make_organization_and_user()
    make_telegram_user_connector(user)
    notification_policy = make_user_notification_policy(
        user,
        UserNotificationPolicy.Step.NOTIFY,
        notify_by=UserNotificationPolicy.NotificationChannel.TELEGRAM,
        important=False,
    )

    # create an alert group with an existing Telegram message in channel
    alert_receive_channel = make_alert_receive_channel(organization)
    alert_group = make_alert_group(alert_receive_channel)
    make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
    telegram_channel = make_telegram_channel(organization, is_default_channel=True)
    make_telegram_message(
        alert_group=alert_group,
        message_type=TelegramMessage.ALERT_GROUP_MESSAGE,
        chat_id=str(telegram_channel.channel_chat_id),
    )

    user.telegram_connection.send_link_to_channel_message(alert_group, notification_policy)
    mock_send_message.assert_called_once_with(
        chat_id=user.telegram_connection.telegram_chat_id,
        message_type=TelegramMessage.LINK_TO_CHANNEL_MESSAGE,
        alert_group=alert_group,
    )

    log_record = notification_policy.personal_log_records.last()
    assert log_record.type == UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_SUCCESS


@patch.object(TelegramClient, "send_message")
@pytest.mark.django_db
def test_personal_connector_send_full_alert_group(
    mock_send_message,
    make_organization_and_user,
    make_telegram_user_connector,
    make_user_notification_policy,
    make_alert_receive_channel,
    make_alert_group,
    make_alert,
):
    # set up a user with Telegram account connected
    organization, user = make_organization_and_user()
    make_telegram_user_connector(user)
    notification_policy = make_user_notification_policy(
        user,
        UserNotificationPolicy.Step.NOTIFY,
        notify_by=UserNotificationPolicy.NotificationChannel.TELEGRAM,
        important=False,
    )

    # create an alert group with an existing Telegram message in channel
    alert_receive_channel = make_alert_receive_channel(organization)
    alert_group = make_alert_group(alert_receive_channel)
    make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)

    user.telegram_connection.send_full_alert_group(alert_group, notification_policy)
    mock_send_message.assert_called_once_with(
        chat_id=user.telegram_connection.telegram_chat_id,
        message_type=TelegramMessage.PERSONAL_MESSAGE,
        alert_group=alert_group,
    )

    log_record = notification_policy.personal_log_records.last()
    assert log_record.type == UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_SUCCESS


@patch.object(TelegramToUserConnector, "_nudge_about_alert_group_message")
@pytest.mark.django_db
def test_personal_connector_send_full_alert_group_second_time(
    mock_nudge_about_alert_group_message,
    make_organization_and_user,
    make_telegram_user_connector,
    make_user_notification_policy,
    make_alert_receive_channel,
    make_alert_group,
    make_alert,
    make_telegram_message,
):
    # set up a user with Telegram account connected
    organization, user = make_organization_and_user()
    connector = make_telegram_user_connector(user)
    notification_policy = make_user_notification_policy(
        user,
        UserNotificationPolicy.Step.NOTIFY,
        notify_by=UserNotificationPolicy.NotificationChannel.TELEGRAM,
        important=False,
    )

    # create an alert group with an existing Telegram message in channel
    alert_receive_channel = make_alert_receive_channel(organization)
    alert_group = make_alert_group(alert_receive_channel)
    make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)
    make_telegram_message(
        alert_group=alert_group,
        message_type=TelegramMessage.PERSONAL_MESSAGE,
        chat_id=connector.telegram_chat_id,
    )
    user.telegram_connection.send_full_alert_group(alert_group, notification_policy)
    mock_nudge_about_alert_group_message.assert_called_once()
    log_record = notification_policy.personal_log_records.last()
    assert log_record.type == UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_SUCCESS


@patch.object(TelegramClient, "send_message", side_effect=[error.BadRequest("error"), None])
@pytest.mark.django_db
def test_personal_connector_send_full_alert_group_formatting_error(
    mock_send_message,
    make_organization_and_user,
    make_telegram_user_connector,
    make_user_notification_policy,
    make_alert_receive_channel,
    make_alert_group,
    make_alert,
):
    # set up a user with Telegram account connected
    organization, user = make_organization_and_user()
    make_telegram_user_connector(user)
    notification_policy = make_user_notification_policy(
        user,
        UserNotificationPolicy.Step.NOTIFY,
        notify_by=UserNotificationPolicy.NotificationChannel.TELEGRAM,
        important=False,
    )

    # create an alert group with an existing Telegram message in channel
    alert_receive_channel = make_alert_receive_channel(organization)
    alert_group = make_alert_group(alert_receive_channel)
    make_alert(alert_group=alert_group, raw_request_data=alert_receive_channel.config.example_payload)

    user.telegram_connection.send_full_alert_group(alert_group, notification_policy)

    failed_call, notification_call = mock_send_message.mock_calls
    assert failed_call == call(
        chat_id=user.telegram_connection.telegram_chat_id,
        message_type=TelegramMessage.PERSONAL_MESSAGE,
        alert_group=alert_group,
    )
    assert notification_call == call(
        chat_id=user.telegram_connection.telegram_chat_id,
        message_type=TelegramMessage.FORMATTING_ERROR,
        alert_group=alert_group,
    )

    log_record = notification_policy.personal_log_records.last()
    assert log_record.type == UserNotificationPolicyLogRecord.TYPE_PERSONAL_NOTIFICATION_FAILED
