import json

import pytest
from django.utils import timezone
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.test import APIRequestFactory

from apps.auth_token.auth import PluginAuthentication

INSTANCE_CONTEXT = '{"stack_id": 42, "org_id": 24, "grafana_token": "abc"}'


@pytest.mark.django_db
def test_plugin_authentication_self_hosted_success(make_organization, make_user, make_token_for_organization):
    organization = make_organization(stack_id=42, org_id=24)
    user = make_user(organization=organization, user_id=12)
    token, token_string = make_token_for_organization(organization)

    headers = {
        "HTTP_AUTHORIZATION": token_string,
        "HTTP_X-Instance-Context": INSTANCE_CONTEXT,
        "HTTP_X-Grafana-Context": '{"UserId": 12}',
    }
    request = APIRequestFactory().get("/", **headers)

    assert PluginAuthentication().authenticate(request) == (user, token)


@pytest.mark.django_db
def test_plugin_authentication_gcom_success(make_organization, make_user, make_token_for_organization):
    # Setting gcom_token_org_last_time_synced to now, so it doesn't try to sync with gcom
    organization = make_organization(
        stack_id=42, org_id=24, gcom_token="123", api_token="abc", gcom_token_org_last_time_synced=timezone.now()
    )
    user = make_user(organization=organization, user_id=12)

    headers = {
        "HTTP_AUTHORIZATION": "gcom:123",
        "HTTP_X-Instance-Context": INSTANCE_CONTEXT,
        "HTTP_X-Grafana-Context": '{"UserId": 12}',
    }
    request = APIRequestFactory().get("/", **headers)

    ret_user, ret_token = PluginAuthentication().authenticate(request)
    assert ret_user == user
    assert ret_token.organization == organization


@pytest.mark.django_db
@pytest.mark.parametrize("grafana_context", [None, "", "non-json", '"string"', "{}", '{"UserId": 1}'])
def test_plugin_authentication_fail_grafana_context(
    make_organization, make_user, make_token_for_organization, grafana_context
):
    organization = make_organization(stack_id=42, org_id=24)
    token, token_string = make_token_for_organization(organization)

    headers = {"HTTP_AUTHORIZATION": token_string, "HTTP_X-Instance-Context": INSTANCE_CONTEXT}
    if grafana_context is not None:
        headers["HTTP_X-Grafana-Context"] = grafana_context

    request = APIRequestFactory().get("/", **headers)
    with pytest.raises(AuthenticationFailed):
        PluginAuthentication().authenticate(request)


@pytest.mark.django_db
@pytest.mark.parametrize("authorization", [None, "", "123", "gcom:123"])
@pytest.mark.parametrize(
    "instance_context", [None, "", "non-json", '"string"', "{}", '{"stack_id": 1, "org_id": 1, "grafana_token": "abc"}']
)
def test_plugin_authentication_fail(authorization, instance_context):
    headers = {}

    if authorization is not None:
        headers["HTTP_AUTHORIZATION"] = authorization

    if instance_context is not None:
        headers["HTTP_X-Instance-Context"] = instance_context

    request = APIRequestFactory().get("/", **headers)

    with pytest.raises(AuthenticationFailed):
        PluginAuthentication().authenticate(request)


@pytest.mark.django_db
def test_plugin_authentication_inactive_user(make_organization, make_user, make_token_for_organization):
    organization = make_organization(stack_id=42, org_id=24)
    token, token_string = make_token_for_organization(organization)
    user = make_user(organization=organization, user_id=12)
    # user is set to inactive if deleted via queryset (ie. during sync)
    user.is_active = False
    user.save()

    headers = {
        "HTTP_AUTHORIZATION": token_string,
        "HTTP_X-Instance-Context": INSTANCE_CONTEXT,
        "HTTP_X-Grafana-Context": '{"UserId": 12}',
    }
    request = APIRequestFactory().get("/", **headers)

    with pytest.raises(AuthenticationFailed):
        PluginAuthentication().authenticate(request)


@pytest.mark.django_db
def test_plugin_authentication_gcom_setup_new_user(make_organization):
    # Setting gcom_token_org_last_time_synced to now, so it doesn't try to sync with gcom
    organization = make_organization(
        stack_id=42, org_id=24, gcom_token="123", api_token="abc", gcom_token_org_last_time_synced=timezone.now()
    )
    assert organization.users.count() == 0
    # user = make_user(organization=organization, user_id=12)

    # logged in user data available through header
    user_data = {
        "id": 12,
        "name": "Test User",
        "login": "test_user",
        "email": "test@test.com",
        "role": "Admin",
        "avatar_url": "http://test.com/avatar.png",
        "permissions": None,
        "teams": None,
    }

    headers = {
        "HTTP_AUTHORIZATION": "gcom:123",
        "HTTP_X-Instance-Context": INSTANCE_CONTEXT,
        "HTTP_X-Grafana-Context": '{"UserId": 12}',
        "HTTP_X-Oncall-User-Context": json.dumps(user_data),
    }
    request = APIRequestFactory().get("/", **headers)

    ret_user, ret_token = PluginAuthentication().authenticate(request)

    assert ret_user.user_id == 12
    assert ret_token.organization == organization
    assert organization.users.count() == 1


@pytest.mark.django_db
def test_plugin_authentication_self_hosted_setup_new_user(make_organization, make_token_for_organization):
    # Setting gcom_token_org_last_time_synced to now, so it doesn't try to sync with gcom
    organization = make_organization(stack_id=42, org_id=24)
    token, token_string = make_token_for_organization(organization)
    assert organization.users.count() == 0

    # logged in user data available through header
    user_data = {
        "id": 12,
        "name": "Test User",
        "login": "test_user",
        "email": "test@test.com",
        "role": "Admin",
        "avatar_url": "http://test.com/avatar.png",
        "permissions": None,
        "teams": None,
    }

    headers = {
        "HTTP_AUTHORIZATION": token_string,
        "HTTP_X-Instance-Context": INSTANCE_CONTEXT,
        "HTTP_X-Grafana-Context": '{"UserId": 12}',
        "HTTP_X-Oncall-User-Context": json.dumps(user_data),
    }
    request = APIRequestFactory().get("/", **headers)

    ret_user, ret_token = PluginAuthentication().authenticate(request)

    assert ret_user.user_id == 12
    assert ret_token.organization == organization
    assert organization.users.count() == 1
