import base64
from typing import Any, cast

from django.test import TestCase
from django.urls import reverse

from engine.vibes_auth.models import User


class GraphQLAuthTests(TestCase):
    def graphql(self, query: str, variables: dict[str, Any] | None = None) -> Any:
        url = reverse("graphql-platform")
        payload: dict[str, Any] = {"query": query}
        if variables:
            payload["variables"] = variables
        response = self.client.post(url, data=payload, content_type="application/json")
        self.assertEqual(response.status_code, 200, response.json())
        return response.json()

    def test_obtain_refresh_verify_jwt_via_graphql(self):
        user = cast(
            User,
            cast(Any, User.objects).create_user(
                email="user@example.com", password="Str0ngPass!word", is_active=True
            ),
        )

        data = self.graphql(
            """
            mutation {
              obtainJwtToken(email: "user@example.com", password: "Str0ngPass!word") {
                accessToken
                refreshToken
                user { email uuid }
              }
            }
            """,
        )
        self.assertNotIn("errors", data)
        payload = data["data"]["obtainJwtToken"]
        self.assertEqual(payload["user"]["email"], cast(Any, user).email)
        refresh = payload["refreshToken"]

        data2 = self.graphql(
            f"""
            mutation {{
              refreshJwtToken(refreshToken: "{refresh}") {{
                accessToken
                refreshToken
                user {{ email }}
              }}
            }}
            """,
        )
        self.assertNotIn("errors", data2)
        access2 = data2["data"]["refreshJwtToken"]["accessToken"]

        data3 = self.graphql(
            f"""
            mutation {{
              verifyJwtToken(token: "{access2}") {{
                tokenIsValid
                user {{ email }}
              }}
            }}
            """,
        )
        self.assertTrue(data3["data"]["verifyJwtToken"]["tokenIsValid"])
        self.assertEqual(
            data3["data"]["verifyJwtToken"]["user"]["email"], cast(Any, user).email
        )

    def test_create_user_and_activate_graphql(self):
        data = self.graphql(
            """
            mutation {
              createUser(email:"new@example.com", password:"Str0ngPass!word", confirmPassword:"Str0ngPass!word") {
                success
              }
            }
            """,
        )
        self.assertTrue(data["data"]["createUser"]["success"])
        user = User.objects.get(email="new@example.com")
        self.assertFalse(cast(Any, user).is_active)

        uid = base64.b64encode(str(cast(Any, user).uuid).encode()).decode()
        token = base64.b64encode(
            str(cast(Any, user).activation_token).encode()
        ).decode()
        data2 = self.graphql(
            f"""
            mutation {{
              activateUser(uid:"{uid}", token:"{token}") {{
                success
              }}
            }}
            """,
        )
        self.assertTrue(data2["data"]["activateUser"]["success"], data2)
        user.refresh_from_db()
        self.assertTrue(cast(Any, user).is_active and cast(Any, user).is_verified, user)

    def test_verify_json_web_token_invalid_graphql(self):
        data = self.graphql(
            """
            mutation {
              verifyJwtToken(token: "invalid") {
                tokenIsValid
                user { email }
              }
            }
            """,
        )
        self.assertFalse(data["data"]["verifyJwtToken"]["tokenIsValid"], data)
