import json
import typing
from unittest.mock import patch

import pytest

from apps.webhooks.models import Webhook
from apps.webhooks.models.webhook import WEBHOOK_FIELD_PLACEHOLDER
from apps.webhooks.presets.advanced import AdvancedWebhookPreset
from apps.webhooks.presets.preset import WebhookPreset, WebhookPresetMetadata
from apps.webhooks.tasks.trigger_webhook import make_request
from apps.webhooks.tests.test_trigger_webhook import MockResponse

TEST_WEBHOOK_PRESET_URL = "https://test123.com"
TEST_WEBHOOK_PRESET_NAME = "Test Webhook"
TEST_WEBHOOK_PRESET_ID = "test_webhook"
TEST_WEBHOOK_LOGO = "test_logo"
TEST_WEBHOOK_PRESET_DESCRIPTION = "Description of test webhook preset"
TEST_WEBHOOK_PRESET_CONTROLLED_FIELDS = ["url", "http_method", "data", "authorization_header"]
TEST_WEBHOOK_AUTHORIZATION_HEADER = "Test Auth header 12345"
TEST_WEBHOOK_MASK_HEADER = "X-Secret-Header"
TEST_WEBHOOK_MASK_HEADER_VALUE = "abc123"
INVALID_PRESET_ID = "invalid_preset_id"
ADVANCED_WEBHOOK_PRESET_ID = "advanced_webhook"


class TestWebhookPreset(WebhookPreset):
    def _metadata(self) -> WebhookPresetMetadata:
        return WebhookPresetMetadata(
            id=TEST_WEBHOOK_PRESET_ID,
            name=TEST_WEBHOOK_PRESET_NAME,
            logo=TEST_WEBHOOK_LOGO,
            description=TEST_WEBHOOK_PRESET_DESCRIPTION,
            controlled_fields=TEST_WEBHOOK_PRESET_CONTROLLED_FIELDS,
        )

    def override_parameters_before_save(self, webhook: Webhook):
        webhook.data = webhook.organization.org_title
        webhook.url = TEST_WEBHOOK_PRESET_URL
        webhook.http_method = "GET"

    def override_parameters_at_runtime(self, webhook: Webhook):
        webhook.authorization_header = TEST_WEBHOOK_AUTHORIZATION_HEADER
        webhook.headers = json.dumps(
            {"Content-Type": "application/json", TEST_WEBHOOK_MASK_HEADER: TEST_WEBHOOK_MASK_HEADER_VALUE}
        )

    def get_masked_headers(self) -> typing.List[str]:
        return [TEST_WEBHOOK_MASK_HEADER]


class TestAdvancedWebhookPreset(AdvancedWebhookPreset):
    pass


@pytest.mark.django_db
def test_create_webhook_from_preset(make_organization, webhook_preset_api_setup, make_custom_webhook):
    organization = make_organization()
    webhook = make_custom_webhook(
        name="the_webhook",
        organization=organization,
        trigger_type=Webhook.TRIGGER_ALERT_GROUP_CREATED,
        preset=TEST_WEBHOOK_PRESET_ID,
    )

    webhook.refresh_from_db()
    assert webhook.url == TEST_WEBHOOK_PRESET_URL
    assert webhook.http_method == "GET"
    assert webhook.data == organization.org_title
    assert webhook.authorization_header is None


@pytest.mark.django_db
def test_create_webhook_from_invalid_preset(make_organization, webhook_preset_api_setup, make_custom_webhook):
    organization = make_organization()
    expected = None
    try:
        make_custom_webhook(
            name="the_webhook",
            organization=organization,
            trigger_type=Webhook.TRIGGER_ALERT_GROUP_CREATED,
            preset=INVALID_PRESET_ID,
        )
    except NotImplementedError as e:
        expected = e

    assert expected.args[0] == f"Webhook references unknown preset implementation {INVALID_PRESET_ID}"


@pytest.mark.django_db
def test_update_webhook_from_preset(make_organization, webhook_preset_api_setup, make_custom_webhook):
    organization = make_organization()
    webhook = make_custom_webhook(
        name="the_webhook",
        organization=organization,
        trigger_type=Webhook.TRIGGER_ALERT_GROUP_CREATED,
        preset=TEST_WEBHOOK_PRESET_ID,
    )

    webhook.refresh_from_db()
    webhook.http_method = "POST"
    webhook.save()

    webhook.refresh_from_db()
    assert webhook.http_method == "GET"


@pytest.mark.django_db
def test_update_webhook_from_invalid_preset(make_organization, webhook_preset_api_setup, make_custom_webhook):
    organization = make_organization()
    webhook = make_custom_webhook(
        name="the_webhook",
        organization=organization,
        trigger_type=Webhook.TRIGGER_ALERT_GROUP_CREATED,
        preset=TEST_WEBHOOK_PRESET_ID,
    )
    webhook.refresh_from_db()
    webhook.preset = INVALID_PRESET_ID

    try:
        webhook.save()
    except NotImplementedError as e:
        expected = e

    assert expected.args[0] == f"Webhook references unknown preset implementation {INVALID_PRESET_ID}"

    webhook.refresh_from_db()
    assert webhook.preset == TEST_WEBHOOK_PRESET_ID


@pytest.mark.django_db
def test_webhook_preset_runtime_override(make_organization, webhook_preset_api_setup, make_custom_webhook):
    organization = make_organization()
    webhook = make_custom_webhook(
        name="the_webhook",
        organization=organization,
        trigger_type=Webhook.TRIGGER_ALERT_GROUP_CREATED,
        preset=TEST_WEBHOOK_PRESET_ID,
    )

    with patch.object(webhook, "build_url"):
        response = MockResponse()
        with patch.object(webhook, "make_request", return_value=response) as mock_make_request:
            triggered, webhook_status, error, exception = make_request(webhook, None, {})
            assert mock_make_request.call_args.args[1]["headers"]["Content-Type"] == "application/json"
            assert mock_make_request.call_args.args[1]["headers"]["Authorization"] == TEST_WEBHOOK_AUTHORIZATION_HEADER
            assert (
                mock_make_request.call_args.args[1]["headers"][TEST_WEBHOOK_MASK_HEADER]
                == TEST_WEBHOOK_MASK_HEADER_VALUE
            )
            assert triggered
            assert error is None
            assert exception is None
            webhook_status_headers = json.loads(webhook_status["request_headers"])
            assert webhook_status_headers["Content-Type"] == "application/json"
            assert webhook_status_headers["Authorization"] == WEBHOOK_FIELD_PLACEHOLDER
            assert webhook_status_headers[TEST_WEBHOOK_MASK_HEADER] == WEBHOOK_FIELD_PLACEHOLDER

    webhook.refresh_from_db()
    assert webhook.authorization_header is None


@pytest.mark.django_db
def test_webhook_invalid_preset_runtime_override(make_organization, webhook_preset_api_setup, make_custom_webhook):
    organization = make_organization()
    webhook = make_custom_webhook(
        name="the_webhook",
        organization=organization,
        trigger_type=Webhook.TRIGGER_ALERT_GROUP_CREATED,
    )
    webhook.refresh_from_db()

    expected_error = f"Invalid preset {INVALID_PRESET_ID}"
    Webhook.objects.filter(id=webhook.id).update(preset=INVALID_PRESET_ID)
    webhook.refresh_from_db()
    with patch.object(webhook, "build_url"):
        with patch.object(webhook, "make_request") as mock_make_request:
            triggered, webhook_status, error, exception = make_request(webhook, None, None)
            mock_make_request.assert_not_called()
            assert triggered
            assert webhook_status["content"] == expected_error
            assert error == expected_error
            assert exception.args[0] == expected_error

    webhook.refresh_from_db()
    assert webhook.authorization_header is None
