"""View tests for the per-user API token self-service dashboard."""

from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse

from mcp_server.models import UserToken

User = get_user_model()


class TokenListViewTest(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(
            username="alice", email="alice@example.com", password="pw"
        )
        self.url = reverse("mcp_server:token-list")

    def test_login_required(self):
        resp = self.client.get(self.url)
        self.assertEqual(resp.status_code, 302)
        self.assertIn("/login/", resp.url)

    def test_list_shows_only_own_tokens(self):
        other = User.objects.create_user(username="bob", password="pw")
        UserToken.objects.create_token(user=self.user, name="mine")
        UserToken.objects.create_token(user=other, name="theirs")
        self.client.force_login(self.user)
        resp = self.client.get(self.url)
        self.assertContains(resp, "mine")
        self.assertNotContains(resp, "theirs")

    def test_empty_state(self):
        self.client.force_login(self.user)
        resp = self.client.get(self.url)
        self.assertContains(resp, "No API tokens yet.")


class TokenCreateViewTest(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(username="alice", password="pw")
        self.client.force_login(self.user)
        self.url = reverse("mcp_server:token-create")

    def test_get_renders_form(self):
        resp = self.client.get(self.url)
        self.assertEqual(resp.status_code, 200)
        self.assertContains(resp, "Generate API Token")

    def test_post_creates_token_and_shows_plaintext_once(self):
        resp = self.client.post(self.url, {"name": "Claude Desktop"})
        self.assertEqual(resp.status_code, 200)
        self.assertContains(resp, "Save this token now")
        # Pull the created row, verify the response contained a plaintext that
        # is NOT what we stored.
        token = UserToken.objects.get(user=self.user, name="Claude Desktop")
        self.assertNotContains(resp, token.token_hash)  # hash is not what we display
        # And the detail page never renders the plaintext.
        body = resp.content.decode()
        # Find the plaintext from the response: the only long alphanumeric
        # block inside the #mcp-plaintext div.
        import re

        m = re.search(r'id="mcp-plaintext">([A-Za-z0-9_\-]+)<', body)
        self.assertIsNotNone(m, "plaintext block not found in response")
        plaintext = m.group(1)
        # Sanity: round-tripping the plaintext through hash_token reproduces
        # what's stored.
        from mcp_server.models import hash_token

        self.assertEqual(hash_token(plaintext), token.token_hash)
        # Detail page must NOT contain the plaintext.
        detail_resp = self.client.get(
            reverse("mcp_server:token-detail", args=[token.pk])
        )
        self.assertNotContains(detail_resp, plaintext)

    def test_post_invalid_renders_form_again(self):
        resp = self.client.post(self.url, {"name": ""})
        self.assertEqual(resp.status_code, 200)
        self.assertContains(resp, "Generate API Token")
        self.assertEqual(UserToken.objects.count(), 0)


class TokenDetailViewTest(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(username="alice", password="pw")
        self.client.force_login(self.user)
        self.token, _ = UserToken.objects.create_token(user=self.user, name="t")

    def test_renders_token(self):
        resp = self.client.get(
            reverse("mcp_server:token-detail", args=[self.token.pk])
        )
        self.assertContains(resp, self.token.name)
        self.assertContains(resp, self.token.get_masked_token())

    def test_cannot_view_other_users_token(self):
        other = User.objects.create_user(username="bob", password="pw")
        other_token, _ = UserToken.objects.create_token(user=other, name="theirs")
        resp = self.client.get(
            reverse("mcp_server:token-detail", args=[other_token.pk])
        )
        self.assertEqual(resp.status_code, 404)


class TokenEditViewTest(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(username="alice", password="pw")
        self.client.force_login(self.user)
        self.token, _ = UserToken.objects.create_token(user=self.user, name="t")

    def test_post_updates_metadata(self):
        resp = self.client.post(
            reverse("mcp_server:token-edit", args=[self.token.pk]),
            {
                "name": "Renamed",
                "is_active": "on",
                "expires_at": "",
                "allowed_tools": ["search"],
            },
        )
        self.assertEqual(resp.status_code, 302)
        self.token.refresh_from_db()
        self.assertEqual(self.token.name, "Renamed")
        self.assertEqual(self.token.allowed_tools, ["search"])

    def test_cannot_edit_other_users_token(self):
        other = User.objects.create_user(username="bob", password="pw")
        other_token, _ = UserToken.objects.create_token(user=other, name="theirs")
        resp = self.client.post(
            reverse("mcp_server:token-edit", args=[other_token.pk]),
            {"name": "hacked"},
        )
        self.assertEqual(resp.status_code, 404)


class TokenRevokeViewTest(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(username="alice", password="pw")
        self.client.force_login(self.user)
        self.token, _ = UserToken.objects.create_token(user=self.user, name="t")

    def test_revoke_sets_inactive_keeps_row(self):
        url = reverse("mcp_server:token-revoke", args=[self.token.pk])
        resp = self.client.post(url)
        self.assertEqual(resp.status_code, 302)
        self.token.refresh_from_db()
        self.assertFalse(self.token.is_active)
        # Row still exists for audit trail.
        self.assertTrue(UserToken.objects.filter(pk=self.token.pk).exists())

    def test_get_not_allowed(self):
        url = reverse("mcp_server:token-revoke", args=[self.token.pk])
        resp = self.client.get(url)
        self.assertEqual(resp.status_code, 405)


class TokenDeleteViewTest(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(username="alice", password="pw")
        self.client.force_login(self.user)
        self.token, _ = UserToken.objects.create_token(user=self.user, name="t")

    def test_delete_removes_row(self):
        url = reverse("mcp_server:token-delete", args=[self.token.pk])
        resp = self.client.post(url)
        self.assertEqual(resp.status_code, 302)
        self.assertFalse(UserToken.objects.filter(pk=self.token.pk).exists())

    def test_cannot_delete_other_users_token(self):
        other = User.objects.create_user(username="bob", password="pw")
        other_token, _ = UserToken.objects.create_token(user=other, name="theirs")
        url = reverse("mcp_server:token-delete", args=[other_token.pk])
        resp = self.client.post(url)
        self.assertEqual(resp.status_code, 404)
        self.assertTrue(UserToken.objects.filter(pk=other_token.pk).exists())
