from django.conf import settings
from django.contrib.auth import get_user_model
from django.test import TestCase, override_settings
from django.urls import reverse
from django_otp.oath import totp
from django_otp.plugins.otp_totp.models import TOTPDevice

from clients.models import Client, ClientPortalAccess

from .models import RecoveryCode

User = get_user_model()


class UnifiedLoginTests(TestCase):
    def setUp(self):
        self.staff = User.objects.create_user("adminv8", "admin@example.com", "StrongPass!2026", is_staff=True, is_superuser=True)
        self.client_obj = Client.objects.create(name="Cliente V8", email="cliente@example.com", portal_access_enabled=True)
        self.client_user = User.objects.create_user("clientev8", "cliente@example.com", "StrongPass!2026")
        ClientPortalAccess.objects.create(client=self.client_obj, user=self.client_user)

    def test_single_login_routes_staff_and_client(self):
        response = self.client.post(reverse("security:login"), {"username": "adminv8", "password": "StrongPass!2026"})
        self.assertRedirects(response, reverse("admin:index"), fetch_redirect_response=False)
        self.client.post(reverse("security:logout"))
        response = self.client.post(reverse("security:login"), {"username": "clientev8", "password": "StrongPass!2026"})
        self.assertRedirects(response, reverse("client_portal:dashboard"), fetch_redirect_response=False)

    def test_admin_login_redirects_to_unified_window(self):
        response = self.client.get("/admin/login/?next=/admin/")
        self.assertEqual(response.status_code, 302)
        self.assertIn(reverse("security:login"), response.url)

    def test_totp_challenge(self):
        device = TOTPDevice.objects.create(user=self.client_user, name="test", confirmed=True)
        response = self.client.post(reverse("security:login"), {"username": "clientev8", "password": "StrongPass!2026"})
        self.assertRedirects(response, reverse("security:verify"), fetch_redirect_response=False)
        token = str(totp(device.bin_key, step=device.step, t0=device.t0, digits=device.digits)).zfill(device.digits)
        response = self.client.post(reverse("security:verify"), {"token": token})
        self.assertRedirects(response, reverse("client_portal:dashboard"), fetch_redirect_response=False)

    def test_recovery_code_is_one_time(self):
        TOTPDevice.objects.create(user=self.client_user, name="test", confirmed=True)
        RecoveryCode.create_for_user(self.client_user, "ABCD-EFGH-IJKL")
        self.client.post(reverse("security:login"), {"username": "clientev8", "password": "StrongPass!2026"})
        response = self.client.post(reverse("security:verify"), {"token": "ABCD-EFGH-IJKL"})
        self.assertEqual(response.status_code, 302)
        self.assertIsNotNone(RecoveryCode.objects.get().used_at)

    @override_settings(K4W_REQUIRE_2FA_FOR_STAFF=True)
    def test_staff_can_be_forced_to_enroll(self):
        response = self.client.post(reverse("security:login"), {"username": "adminv8", "password": "StrongPass!2026"})
        self.assertRedirects(response, reverse("security:setup"), fetch_redirect_response=False)


class V8InterfaceTests(TestCase):
    def setUp(self):
        self.admin = User.objects.create_superuser("admin.ui", "admin.ui@example.com", "StrongPass!2026")
        self.staff = User.objects.create_user("staff.ui", "staff.ui@example.com", "StrongPass!2026", is_staff=True)
        self.client_obj = Client.objects.create(name="Cliente Interface", email="interface@example.com", portal_access_enabled=True)
        self.portal_user = User.objects.create_user("client.ui", "client.ui@example.com", "StrongPass!2026")
        ClientPortalAccess.objects.create(client=self.client_obj, user=self.portal_user)

    @override_settings(DEMO_CREDENTIALS_VISIBLE=True)
    def test_demo_credentials_are_superuser_only(self):
        self.client.force_login(self.admin)
        response = self.client.get(reverse("demo_credentials_center"))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "Credenciais dos clientes fictícios")
        self.client.force_login(self.staff)
        self.assertEqual(self.client.get(reverse("demo_credentials_center")).status_code, 403)

    def test_setup_page_contains_qr_code(self):
        self.client.force_login(self.portal_user)
        response = self.client.get(reverse("security:setup"))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "<svg", html=False)

    def test_admin_and_client_show_versioned_footer(self):
        self.client.force_login(self.admin)
        response = self.client.get(reverse("admin:index"))
        self.assertContains(response, f"Portal v{settings.K4W_VERSION}")
        self.client.force_login(self.portal_user)
        response = self.client.get(reverse("client_portal:dashboard"))
        self.assertContains(response, f"Portal v{settings.K4W_VERSION}")
        self.assertContains(response, "Editar a minha conta")

    def test_client_notification_center_loads(self):
        self.client.force_login(self.portal_user)
        response = self.client.get(reverse("client_portal:notifications"))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "Centro de notificações")


from django.core import mail
from django.test import override_settings


@override_settings(EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend")
class PasswordRecoveryTests(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(
            "recover.user", "recover@example.com", "Old-Password-2026!", is_active=True
        )

    def test_password_reset_sends_link_and_changes_password(self):
        response = self.client.post(
            reverse("security:password_reset"), {"email": "recover@example.com"}
        )
        self.assertRedirects(response, reverse("security:password_reset_done"))
        self.assertEqual(len(mail.outbox), 1)
        body = mail.outbox[0].body
        reset_url = next(line for line in body.splitlines() if "/recuperar-palavra-passe/" in line)
        response = self.client.get(reset_url)
        self.assertEqual(response.status_code, 302)
        confirm_url = response.url
        response = self.client.post(
            confirm_url,
            {"new_password1": "New-Password-2026!", "new_password2": "New-Password-2026!"},
        )
        self.assertRedirects(response, reverse("security:password_reset_complete"))
        self.user.refresh_from_db()
        self.assertTrue(self.user.check_password("New-Password-2026!"))

    def test_unknown_email_does_not_reveal_account(self):
        response = self.client.post(
            reverse("security:password_reset"), {"email": "unknown@example.com"}
        )
        self.assertRedirects(response, reverse("security:password_reset_done"))
        self.assertEqual(len(mail.outbox), 0)
