from unittest.mock import patch

import pytest
from django.test.utils import override_settings
from django.urls import reverse
from rest_framework import status
from rest_framework.response import Response
from rest_framework.test import APIClient

from apps.api.permissions import LegacyAccessControlRole
from apps.base.messaging import BaseMessagingBackend
from apps.base.tests.messaging_backend import TestOnlyBackend


@pytest.mark.django_db
@pytest.mark.parametrize(
    "role,expected_status",
    [
        (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
        (LegacyAccessControlRole.EDITOR, status.HTTP_403_FORBIDDEN),
        (LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
        (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN),
    ],
)
def test_alert_receive_channel_template_update_permissions(
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_alert_receive_channel,
    role,
    expected_status,
):
    organization, user, token = make_organization_and_user_with_plugin_token(role=role)
    alert_receive_channel = make_alert_receive_channel(organization)
    client = APIClient()

    url = reverse("api-internal:alert_receive_channel_template-detail", kwargs={"pk": alert_receive_channel.pk})
    with patch(
        "apps.api.views.alert_receive_channel_template.AlertReceiveChannelTemplateView.update",
        return_value=Response(
            status=status.HTTP_200_OK,
        ),
    ):
        response = client.put(url, format="json", **make_user_auth_headers(user, token))
        assert response.status_code == expected_status

        response = client.patch(url, format="json", **make_user_auth_headers(user, token))
        assert response.status_code == expected_status


@pytest.mark.django_db
@pytest.mark.parametrize(
    "role,expected_status",
    [
        (LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
        (LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
        (LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
        (LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN),
    ],
)
def test_alert_receive_channel_template_detail_permissions(
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_alert_receive_channel,
    role,
    expected_status,
):
    organization, user, token = make_organization_and_user_with_plugin_token(role=role)
    alert_receive_channel = make_alert_receive_channel(organization)
    client = APIClient()

    url = reverse("api-internal:alert_receive_channel_template-detail", kwargs={"pk": alert_receive_channel.pk})

    with patch(
        "apps.api.views.alert_receive_channel_template.AlertReceiveChannelTemplateView.retrieve",
        return_value=Response(
            status=status.HTTP_200_OK,
        ),
    ):
        response = client.get(url, format="json", **make_user_auth_headers(user, token))

    assert response.status_code == expected_status


@pytest.mark.django_db
def test_alert_receive_channel_template_include_additional_backend_templates(
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_alert_receive_channel,
):
    organization, user, token = make_organization_and_user_with_plugin_token()
    alert_receive_channel = make_alert_receive_channel(
        organization,
        messaging_backends_templates={"TESTONLY": {"title": "the-title", "message": "the-message", "image_url": "url"}},
    )
    client = APIClient()

    url = reverse(
        "api-internal:alert_receive_channel_template-detail", kwargs={"pk": alert_receive_channel.public_primary_key}
    )

    response = client.get(url, format="json", **make_user_auth_headers(user, token))

    assert response.status_code == status.HTTP_200_OK
    templates_data = response.json()
    assert templates_data["testonly_title_template"] == "the-title"
    assert templates_data["testonly_message_template"] == "the-message"
    assert templates_data["testonly_image_url_template"] == "url"


@pytest.mark.django_db
def test_alert_receive_channel_template_include_additional_backend_templates_using_defaults(
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_alert_receive_channel,
):
    organization, user, token = make_organization_and_user_with_plugin_token()
    alert_receive_channel = make_alert_receive_channel(organization, messaging_backends_templates=None)
    client = APIClient()

    url = reverse(
        "api-internal:alert_receive_channel_template-detail", kwargs={"pk": alert_receive_channel.public_primary_key}
    )

    response = client.get(url, format="json", **make_user_auth_headers(user, token))

    assert response.status_code == status.HTTP_200_OK
    templates_data = response.json()
    assert templates_data["testonly_title_template"] == alert_receive_channel.get_default_template_attribute(
        "TESTONLY", "title"
    )
    assert templates_data["testonly_message_template"] == alert_receive_channel.get_default_template_attribute(
        "TESTONLY", "message"
    )
    assert templates_data["testonly_image_url_template"] == alert_receive_channel.get_default_template_attribute(
        "TESTONLY", "image_url"
    )


@pytest.mark.django_db
def test_update_alert_receive_channel_backend_template_invalid_template(
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_alert_receive_channel,
):
    organization, user, token = make_organization_and_user_with_plugin_token()
    alert_receive_channel = make_alert_receive_channel(organization, messaging_backends_templates=None)
    client = APIClient()

    url = reverse(
        "api-internal:alert_receive_channel_template-detail", kwargs={"pk": alert_receive_channel.public_primary_key}
    )

    response = client.put(
        url, format="json", data={"testonly_title_template": "{{ wrong"}, **make_user_auth_headers(user, token)
    )

    assert response.status_code == status.HTTP_400_BAD_REQUEST
    assert response.json() == {"testonly_title_template": "invalid template"}


@pytest.mark.django_db
def test_update_alert_receive_channel_backend_template_set_default_template(
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_alert_receive_channel,
):
    organization, user, token = make_organization_and_user_with_plugin_token()
    # create alert_receive_channel with non-default values for TESTONLY messaging backend templates
    testonly_templates = {"TESTONLY": {"title": "non-default", "message": "non-default", "image_url": "non-default"}}
    alert_receive_channel = make_alert_receive_channel(organization, messaging_backends_templates=testonly_templates)

    client = APIClient()

    url = reverse(
        "api-internal:alert_receive_channel_template-detail", kwargs={"pk": alert_receive_channel.public_primary_key}
    )

    # update templates with empty string, which mean templates are default
    response = client.put(
        url,
        format="json",
        data={"testonly_title_template": "", "testonly_message_template": "", "testonly_image_url_template": ""},
        **make_user_auth_headers(user, token),
    )

    assert response.status_code == status.HTTP_200_OK
    alert_receive_channel.refresh_from_db()
    assert alert_receive_channel.messaging_backends_templates["TESTONLY"] == {
        "title": "",
        "message": "",
        "image_url": "",
    }

    # check if internal api returns default values
    response = client.get(
        url,
        format="json",
        **make_user_auth_headers(user, token),
    )

    assert response.status_code == status.HTTP_200_OK
    # WEB_TEMPLATE is default for templates from messaging backends
    default_title = alert_receive_channel.INTEGRATION_TO_DEFAULT_WEB_TITLE_TEMPLATE[alert_receive_channel.integration]
    default_message = alert_receive_channel.INTEGRATION_TO_DEFAULT_WEB_MESSAGE_TEMPLATE[
        alert_receive_channel.integration
    ]
    default_image_url = alert_receive_channel.INTEGRATION_TO_DEFAULT_WEB_IMAGE_URL_TEMPLATE[
        alert_receive_channel.integration
    ]

    assert response.json()["testonly_title_template"] == default_title
    assert response.json()["testonly_message_template"] == default_message
    assert response.json()["testonly_image_url_template"] == default_image_url


@pytest.mark.django_db
def test_update_alert_receive_channel_legacy_template_set_default_template(
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_alert_receive_channel,
):
    organization, user, token = make_organization_and_user_with_plugin_token()
    alert_receive_channel = make_alert_receive_channel(organization, messaging_backends_templates=None)
    client = APIClient()

    url = reverse(
        "api-internal:alert_receive_channel_template-detail", kwargs={"pk": alert_receive_channel.public_primary_key}
    )

    # set non-default templates
    alert_receive_channel.slack_title_template = "non-default-template"
    alert_receive_channel.slack_message_template = "non-default-template"
    alert_receive_channel.slack_image_url_template = "non-default-template"
    alert_receive_channel.save()

    # update templates with empty string, which mean templates are default
    response = client.put(
        url,
        format="json",
        data={"slack_title_template": "", "slack_message_template": "", "slack_image_url_template": ""},
        **make_user_auth_headers(user, token),
    )

    assert response.status_code == status.HTTP_200_OK
    alert_receive_channel.refresh_from_db()
    assert alert_receive_channel.slack_title_template == ""
    assert alert_receive_channel.slack_message_template == ""
    assert alert_receive_channel.slack_image_url_template == ""

    # check if internal api returns default values
    response = client.get(
        url,
        format="json",
        **make_user_auth_headers(user, token),
    )

    assert response.status_code == status.HTTP_200_OK

    default_title = alert_receive_channel.INTEGRATION_TO_DEFAULT_SLACK_TITLE_TEMPLATE[alert_receive_channel.integration]
    default_message = alert_receive_channel.INTEGRATION_TO_DEFAULT_SLACK_MESSAGE_TEMPLATE[
        alert_receive_channel.integration
    ]
    default_image_url = alert_receive_channel.INTEGRATION_TO_DEFAULT_SLACK_IMAGE_URL_TEMPLATE[
        alert_receive_channel.integration
    ]

    assert response.json()["slack_title_template"] == default_title
    assert response.json()["slack_message_template"] == default_message
    assert response.json()["slack_image_url_template"] == default_image_url


@pytest.mark.django_db
def test_update_alert_receive_channel_backend_template_update_values(
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_alert_receive_channel,
):
    organization, user, token = make_organization_and_user_with_plugin_token()
    alert_receive_channel = make_alert_receive_channel(
        organization,
        messaging_backends_templates={
            "TESTONLY": {"title": "the-title", "message": "some-message"},
            "OTHER": {"title": "some-title"},
        },
    )
    client = APIClient()

    url = reverse(
        "api-internal:alert_receive_channel_template-detail", kwargs={"pk": alert_receive_channel.public_primary_key}
    )

    # patch messaging backends to add OTHER as a valid backend
    with patch(
        "apps.api.serializers.alert_receive_channel.get_messaging_backends",
        return_value=[("TESTONLY", TestOnlyBackend()), ("OTHER", BaseMessagingBackend())],
    ):
        response = client.put(
            url, format="json", data={"testonly_title_template": "updated-title"}, **make_user_auth_headers(user, token)
        )

    assert response.status_code == status.HTTP_200_OK
    alert_receive_channel.refresh_from_db()
    assert alert_receive_channel.messaging_backends_templates["TESTONLY"] == {
        "title": "updated-title",
        "message": "some-message",
    }
    assert alert_receive_channel.messaging_backends_templates["OTHER"] == {"title": "some-title"}


@pytest.mark.django_db
def test_preview_alert_receive_channel_backend_templater(
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_alert_receive_channel,
    make_channel_filter,
    make_alert_group,
    make_alert,
):
    organization, user, token = make_organization_and_user_with_plugin_token()
    alert_receive_channel = make_alert_receive_channel(organization)
    default_channel_filter = make_channel_filter(alert_receive_channel, is_default=True)
    alert_group = make_alert_group(alert_receive_channel, channel_filter=default_channel_filter)
    make_alert(alert_group=alert_group, raw_request_data={"title": "alert!"})
    client = APIClient()

    url = reverse(
        "api-internal:alert_receive_channel-preview-template", kwargs={"pk": alert_receive_channel.public_primary_key}
    )

    data = {
        "template_body": "title: {{ payload.title }}",
        "template_name": "testonly_title_template",
    }
    response = client.post(url, format="json", data=data, **make_user_auth_headers(user, token))

    assert response.status_code == status.HTTP_200_OK
    assert response.json() == {"preview": "title: alert!", "is_valid_json_object": False}


@pytest.mark.django_db
def test_alert_receive_channel_template_is_valid_json_check(
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_alert_receive_channel,
    make_channel_filter,
    make_alert_group,
    make_alert,
):
    organization, user, token = make_organization_and_user_with_plugin_token()
    alert_receive_channel = make_alert_receive_channel(organization)
    default_channel_filter = make_channel_filter(alert_receive_channel, is_default=True)
    alert_group = make_alert_group(alert_receive_channel, channel_filter=default_channel_filter)
    make_alert(alert_group=alert_group, raw_request_data={"title": "alert!"})
    client = APIClient()

    url = reverse(
        "api-internal:alert_receive_channel-preview-template", kwargs={"pk": alert_receive_channel.public_primary_key}
    )

    # template which should produce valid json string
    data = {
        "template_body": "{{ payload | tojson }}",
        "template_name": "alert_group_multi_label",
    }
    response = client.post(url, format="json", data=data, **make_user_auth_headers(user, token))

    assert response.status_code == status.HTTP_200_OK
    assert response.json()["is_valid_json_object"] is True

    # template which produce not avalid json string
    data = {
        "template_body": "{{ payload.title }}",
        "template_name": "alert_group_multi_label",
    }
    response = client.post(url, format="json", data=data, **make_user_auth_headers(user, token))

    assert response.status_code == status.HTTP_200_OK
    assert response.json()["is_valid_json_object"] is False


@pytest.mark.django_db
def test_preview_alert_group_labels(
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_alert_receive_channel,
    make_channel_filter,
    make_alert_group,
    make_alert,
):
    organization, user, token = make_organization_and_user_with_plugin_token()
    alert_receive_channel = make_alert_receive_channel(organization)
    default_channel_filter = make_channel_filter(alert_receive_channel, is_default=True)
    alert_group = make_alert_group(alert_receive_channel, channel_filter=default_channel_filter)
    make_alert(alert_group=alert_group, raw_request_data={"labels": {"1": "2"}})

    client = APIClient()
    url = reverse(
        "api-internal:alert_receive_channel-preview-template",
        kwargs={"pk": alert_receive_channel.public_primary_key},
    )

    data = {
        "template_body": "{{ payload.labels | tojson }}",
        "template_name": "alert_group_multi_label",
    }
    response = client.post(url, format="json", data=data, **make_user_auth_headers(user, token))

    assert response.status_code == status.HTTP_200_OK
    assert response.json() == {"preview": '{"1": "2"}', "is_valid_json_object": True}


@pytest.mark.django_db
def test_update_alert_receive_channel_templates(
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_alert_receive_channel,
):
    def template_update_func(template):
        return f"{template}_updated"

    organization, user, token = make_organization_and_user_with_plugin_token()
    alert_receive_channel = make_alert_receive_channel(
        organization,
        messaging_backends_templates={"TESTONLY": {"title": "the-title", "message": "the-message", "image_url": "url"}},
    )
    client = APIClient()

    url = reverse(
        "api-internal:alert_receive_channel_template-detail", kwargs={"pk": alert_receive_channel.public_primary_key}
    )
    # Get response from templates endpoint to get initial templates data
    response = client.get(url, format="json", **make_user_auth_headers(user, token))

    assert response.status_code == status.HTTP_200_OK
    existing_templates_data = response.json()

    # build data for PUT request from data we received

    # leave only templates-related fields
    del existing_templates_data["id"]
    del existing_templates_data["verbal_name"]
    del existing_templates_data["payload_example"]
    del existing_templates_data["is_based_on_alertmanager"]

    # update each template
    new_templates_data = {}
    for template_name, template_value in existing_templates_data.items():
        new_templates_data[template_name] = template_update_func(template_value)

    response = client.put(url, format="json", data=new_templates_data, **make_user_auth_headers(user, token))

    # check if updated templates are applied
    updated_templates_data = response.json()
    for template_name, prev_template_value in existing_templates_data.items():
        if template_name.endswith("_is_default"):
            assert updated_templates_data[template_name] is False
        else:
            assert updated_templates_data[template_name] == template_update_func(prev_template_value)


@override_settings(FEATURE_TELEGRAM_INTEGRATION_ENABLED=False)
@override_settings(FEATURE_SLACK_INTEGRATION_ENABLED=False)
@pytest.mark.django_db
def test_update_alert_receive_channel_backend_template_hide_disabled_integration_templates(
    make_organization_and_user_with_plugin_token,
    make_user_auth_headers,
    make_alert_receive_channel,
):
    slack_integration_required_templates = [
        "slack_title_template",
        "slack_message_template",
        "slack_image_url_template",
    ]
    telegram_integration_required_templates = [
        "telegram_title_template",
        "telegram_message_template",
        "telegram_image_url_template",
    ]

    organization, user, token = make_organization_and_user_with_plugin_token()
    alert_receive_channel = make_alert_receive_channel(
        organization,
        messaging_backends_templates={"TESTONLY": {"title": "the-title", "message": "the-message", "image_url": "url"}},
    )
    client = APIClient()

    url = reverse(
        "api-internal:alert_receive_channel_template-detail", kwargs={"pk": alert_receive_channel.public_primary_key}
    )

    response = client.get(url, format="json", **make_user_auth_headers(user, token))
    assert response.status_code == status.HTTP_200_OK
    templates_data = response.json()

    for st in slack_integration_required_templates:
        assert st not in templates_data

    for tt in telegram_integration_required_templates:
        assert tt not in templates_data
