import json
from unittest.mock import patch

import pytest
from django.urls import reverse
from firebase_admin.exceptions import FirebaseError
from rest_framework import status
from rest_framework.test import APIClient

from apps.mobile_app.fcm_relay import FCMRelayThrottler, _get_message_from_request_data, fcm_relay_async
from apps.mobile_app.models import FCMDevice
from apps.mobile_app.tasks.new_alert_group import _get_fcm_message


@pytest.mark.django_db
def test_fcm_relay_disabled(
    settings,
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_public_api_token,
):
    settings.FCM_RELAY_ENABLED = False

    organization, user, token = make_organization_and_user_with_plugin_token()
    _, token = make_public_api_token(user, organization)

    client = APIClient()
    url = reverse("mobile_app:fcm_relay")

    response = client.post(url, HTTP_AUTHORIZATION=token)
    assert response.status_code == status.HTTP_404_NOT_FOUND


@pytest.mark.django_db
def test_fcm_relay_post(
    settings,
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_public_api_token,
):
    settings.FCM_RELAY_ENABLED = True

    organization, user, token = make_organization_and_user_with_plugin_token()
    _, token = make_public_api_token(user, organization)

    client = APIClient()
    url = reverse("mobile_app:fcm_relay")

    data = {
        "token": "test_registration_id",
        "data": {},
        "apns": {},
    }

    response = client.post(url, data=data, format="json", HTTP_AUTHORIZATION=token)
    assert response.status_code == status.HTTP_200_OK


@pytest.mark.django_db
def test_fcm_relay_ratelimit(
    settings,
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_public_api_token,
):
    settings.FCM_RELAY_ENABLED = True

    organization, user, token = make_organization_and_user_with_plugin_token()
    _, token = make_public_api_token(user, organization)

    client = APIClient()
    url = reverse("mobile_app:fcm_relay")

    data = {
        "token": "test_registration_id",
        "data": {},
        "apns": {},
    }

    with patch.object(FCMRelayThrottler, "rate", "0/m"):
        response = client.post(url, data=data, format="json", HTTP_AUTHORIZATION=token)
        assert response.status_code == status.HTTP_429_TOO_MANY_REQUESTS


@pytest.mark.django_db
def test_fcm_relay_async_retry():
    # check that FirebaseError is raised when send_message returns it so Celery task can retry
    with patch.object(
        FCMDevice, "send_message", return_value=FirebaseError(code="test_error_code", message="test_error_message")
    ):
        with pytest.raises(FirebaseError):
            fcm_relay_async(token="test_token", data={}, apns={})


def test_get_message_from_request_data():
    token = "test_token"
    data = {"test_data_key": "test_data_value"}
    apns = {"headers": {"apns-priority": "10"}, "payload": {"aps": {"thread-id": "test_thread_id"}}}
    android = {"priority": "high"}
    message = _get_message_from_request_data(token, data, apns, android)

    assert message.token == "test_token"
    assert message.data == {"test_data_key": "test_data_value"}
    assert message.apns.headers == {"apns-priority": "10"}
    assert message.apns.payload.aps.thread_id == "test_thread_id"
    assert message.android.priority == "high"


@pytest.mark.django_db
def test_fcm_relay_serialize_deserialize(
    make_organization_and_user, make_alert_receive_channel, make_alert_group, make_alert
):
    organization, user = make_organization_and_user()
    device = FCMDevice.objects.create(user=user, registration_id="test_device_id")

    alert_receive_channel = make_alert_receive_channel(organization=organization)
    alert_group = make_alert_group(alert_receive_channel)
    make_alert(alert_group=alert_group, raw_request_data={})

    # Imitate sending a message to the FCM relay endpoint
    original_message = _get_fcm_message(alert_group, user, device, critical=False)
    request_data = json.loads(str(original_message))

    # Imitate receiving a message from the FCM relay endpoint
    relayed_message = _get_message_from_request_data(
        request_data["token"], request_data["data"], request_data["apns"], request_data["android"]
    )

    # Check that the message is the same after serialization and deserialization
    assert json.loads(str(original_message)) == json.loads(str(relayed_message))
