import json
from unittest.mock import patch

import pytest
from django.conf import settings
from django.test import override_settings
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient

GRAFANA_TOKEN = "TEST_TOKEN"
STACK_ID = 1
ORG_ID = 5
GRAFANA_API_URL = "hello.com"
LICENSE = "OpenSource"
STACK_SLUG = "asdfasdf"
ORG_SLUG = "hellooo"
ORG_TITLE = "nmvcnmvnmvc"
REGION_SLUG = "nmcvnmcvnmcvnmcv"
CLUSTER_SLUG = "nmcvnmcvnmcvnmcvnmcv"
SELF_HOSTED_SETTINGS = {
    "GRAFANA_API_URL": GRAFANA_API_URL,
    "STACK_ID": STACK_ID,
    "ORG_ID": ORG_ID,
    "LICENSE": LICENSE,
    "STACK_SLUG": STACK_SLUG,
    "ORG_SLUG": ORG_SLUG,
    "ORG_TITLE": ORG_TITLE,
    "REGION_SLUG": REGION_SLUG,
    "CLUSTER_SLUG": CLUSTER_SLUG,
}

UNABLE_TO_FIND_GRAFANA_ERROR_MSG = f"Unable to connect to the specified Grafana API - {GRAFANA_API_URL}"
UNAUTHED_GRAFANA_API_ERROR_MSG = (
    f"You are not authorized to communicate with the specified Grafana API - {GRAFANA_API_URL}"
)


@pytest.fixture
def make_self_hosted_install_header():
    def _make_instance_context_header(token):
        return {
            "HTTP_X-Instance-Context": json.dumps({"grafana_token": token}),
        }

    return _make_instance_context_header


@override_settings(LICENSE=settings.CLOUD_LICENSE_NAME)
def test_a_cloud_license_gets_an_unauthorized_error(make_self_hosted_install_header):
    client = APIClient()
    url = reverse("grafana-plugin:self-hosted-install")
    response = client.post(url, format="json", **make_self_hosted_install_header(GRAFANA_TOKEN))

    assert response.status_code == status.HTTP_403_FORBIDDEN


@pytest.mark.parametrize(
    "grafana_api_status_code,expected_error_msg",
    [
        (status.HTTP_404_NOT_FOUND, UNABLE_TO_FIND_GRAFANA_ERROR_MSG),
        (status.HTTP_401_UNAUTHORIZED, UNAUTHED_GRAFANA_API_ERROR_MSG),
        (status.HTTP_401_UNAUTHORIZED, UNAUTHED_GRAFANA_API_ERROR_MSG),
    ],
)
@override_settings(SELF_HOSTED_SETTINGS=SELF_HOSTED_SETTINGS)
@patch("apps.grafana_plugin.views.self_hosted_install.GrafanaAPIClient")
def test_it_properly_handles_errors_from_the_grafana_api(
    mocked_grafana_api_client, make_self_hosted_install_header, grafana_api_status_code, expected_error_msg
):
    mocked_grafana_api_client.return_value.check_token.return_value = (None, {"status_code": grafana_api_status_code})

    client = APIClient()
    url = reverse("grafana-plugin:self-hosted-install")
    response = client.post(url, format="json", **make_self_hosted_install_header(GRAFANA_TOKEN))

    mocked_grafana_api_client.assert_called_once_with(api_url=GRAFANA_API_URL, api_token=GRAFANA_TOKEN)
    mocked_grafana_api_client.return_value.check_token.assert_called_once_with()

    assert response.status_code == status.HTTP_400_BAD_REQUEST
    assert response.data["error"] == expected_error_msg


@override_settings(SELF_HOSTED_SETTINGS=SELF_HOSTED_SETTINGS)
@pytest.mark.django_db
@patch("apps.grafana_plugin.views.self_hosted_install.GrafanaAPIClient")
@patch("apps.grafana_plugin.views.self_hosted_install.sync_organization")
@patch("apps.grafana_plugin.views.self_hosted_install.Organization.provision_plugin")
@patch("apps.grafana_plugin.views.self_hosted_install.Organization.revoke_plugin")
def test_if_organization_exists_it_is_updated(
    mocked_revoke_plugin,
    mocked_provision_plugin,
    mocked_sync_organization,
    mocked_grafana_api_client,
    make_self_hosted_install_header,
    make_organization,
):
    organization = make_organization(stack_id=STACK_ID, org_id=ORG_ID)
    provision_plugin_response = {"stackId": STACK_ID, "orgId": ORG_ID, "onCallToken": "HELLOOO", "license": LICENSE}

    mocked_provision_plugin.return_value = provision_plugin_response
    mocked_grafana_api_client.return_value.check_token.return_value = (None, {"status_code": status.HTTP_200_OK})
    mocked_grafana_api_client.return_value.is_rbac_enabled_for_organization.return_value = True, False

    client = APIClient()
    url = reverse("grafana-plugin:self-hosted-install")
    response = client.post(url, format="json", **make_self_hosted_install_header(GRAFANA_TOKEN))

    mocked_grafana_api_client.assert_called_once_with(api_url=GRAFANA_API_URL, api_token=GRAFANA_TOKEN)
    mocked_grafana_api_client.return_value.check_token.assert_called_once_with()
    mocked_grafana_api_client.return_value.is_rbac_enabled_for_organization.assert_called_once_with()

    mocked_sync_organization.assert_called_once_with(organization)
    mocked_provision_plugin.assert_called_once_with()
    mocked_revoke_plugin.assert_called_once_with()

    assert response.status_code == status.HTTP_201_CREATED
    assert response.data == {"error": None, **provision_plugin_response}

    organization.refresh_from_db()

    assert organization.grafana_url == GRAFANA_API_URL
    assert organization.api_token == GRAFANA_TOKEN
    assert organization.is_rbac_permissions_enabled is True


@override_settings(SELF_HOSTED_SETTINGS=SELF_HOSTED_SETTINGS)
@pytest.mark.django_db
@patch("apps.grafana_plugin.views.self_hosted_install.GrafanaAPIClient")
@patch("apps.grafana_plugin.views.self_hosted_install.sync_organization")
@patch("apps.grafana_plugin.views.self_hosted_install.Organization.provision_plugin")
@patch("apps.grafana_plugin.views.self_hosted_install.Organization.revoke_plugin")
def test_if_organization_does_not_exist_it_is_created(
    mocked_revoke_plugin,
    mocked_provision_plugin,
    mocked_sync_organization,
    mocked_grafana_api_client,
    make_self_hosted_install_header,
):
    provision_plugin_response = {"stackId": STACK_ID, "orgId": ORG_ID, "onCallToken": "HELLOOO", "license": LICENSE}

    mocked_provision_plugin.return_value = provision_plugin_response
    mocked_grafana_api_client.return_value.check_token.return_value = (None, {"status_code": status.HTTP_200_OK})
    mocked_grafana_api_client.return_value.is_rbac_enabled_for_organization.return_value = True, False

    client = APIClient()
    url = reverse("grafana-plugin:self-hosted-install")
    response = client.post(url, format="json", **make_self_hosted_install_header(GRAFANA_TOKEN))

    from apps.user_management.models import Organization

    organization = Organization.objects.filter(stack_id=STACK_ID, org_id=ORG_ID).first()

    mocked_grafana_api_client.assert_called_once_with(api_url=GRAFANA_API_URL, api_token=GRAFANA_TOKEN)
    mocked_grafana_api_client.return_value.check_token.assert_called_once_with()
    mocked_grafana_api_client.return_value.is_rbac_enabled_for_organization.assert_called_once_with()

    mocked_sync_organization.assert_called_once_with(organization)
    mocked_provision_plugin.assert_called_once_with()
    assert not mocked_revoke_plugin.called

    assert response.status_code == status.HTTP_201_CREATED
    assert response.data == {"error": None, **provision_plugin_response}

    assert organization.stack_id == STACK_ID
    assert organization.stack_slug == STACK_SLUG
    assert organization.org_slug == ORG_SLUG
    assert organization.org_title == ORG_TITLE
    assert organization.region_slug == REGION_SLUG
    assert organization.grafana_url == GRAFANA_API_URL
    assert organization.api_token == GRAFANA_TOKEN
    assert organization.is_rbac_permissions_enabled is True
