"""
Tests - App Users
===================
Tests para: modelo CustomUser, registro, login, permisos por rol, email confirmacion.
"""

from django.contrib.auth import get_user_model
from django.core import mail
from django.test import TestCase, override_settings
from django.urls import reverse
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode

from .tokens import email_confirmation_token

User = get_user_model()


# ============================================================
# TESTS DEL MODELO
# ============================================================
class CustomUserModelTest(TestCase):
    """Tests del modelo CustomUser."""

    def test_create_user_with_email(self):
        user = User.objects.create_user(
            email="paciente@test.com",
            password="TestPass!123",
            nombre="Ana",
            apellido="Garcia",
        )
        self.assertEqual(user.email, "paciente@test.com")
        self.assertEqual(user.nombre, "Ana")
        self.assertEqual(user.apellido, "Garcia")
        self.assertTrue(user.is_active)
        self.assertFalse(user.is_staff)
        self.assertFalse(user.is_superuser)
        self.assertEqual(user.rol, "paciente")

    def test_create_user_without_email_raises(self):
        with self.assertRaises(ValueError):
            User.objects.create_user(email="", password="TestPass!123", nombre="X", apellido="Y")

    def test_create_superuser(self):
        admin = User.objects.create_superuser(
            email="admin@test.com",
            password="AdminPass!123",
            nombre="Admin",
            apellido="Super",
        )
        self.assertTrue(admin.is_staff)
        self.assertTrue(admin.is_superuser)
        self.assertTrue(admin.is_verified)
        self.assertEqual(admin.rol, "admin")

    def test_email_is_normalized(self):
        user = User.objects.create_user(
            email="Test@UPPER.COM",
            password="TestPass!123",
            nombre="T",
            apellido="U",
        )
        self.assertEqual(user.email, "Test@upper.com")

    def test_username_field_is_email(self):
        self.assertEqual(User.USERNAME_FIELD, "email")

    def test_roles(self):
        paciente = User.objects.create_user(email="p@t.com", password="x", nombre="P", apellido="A", rol="paciente")
        doctora = User.objects.create_user(email="d@t.com", password="x", nombre="D", apellido="A", rol="doctora")
        admin = User.objects.create_user(email="a@t.com", password="x", nombre="A", apellido="A", rol="admin")
        self.assertTrue(paciente.es_paciente)
        self.assertTrue(doctora.es_doctora)
        self.assertTrue(admin.es_admin)

    def test_get_full_name(self):
        user = User.objects.create_user(email="x@t.com", password="x", nombre="Maria", apellido="Lopez")
        self.assertEqual(user.get_full_name(), "Maria Lopez")

    def test_str_representation(self):
        user = User.objects.create_user(email="x2@t.com", password="x", nombre="Ana", apellido="Ruiz", rol="paciente")
        self.assertIn("Ana Ruiz", str(user))
        self.assertIn("Paciente", str(user))


# ============================================================
# TESTS DE REGISTRO
# ============================================================
@override_settings(
    EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend",
    RATELIMIT_ENABLE=False,
)
class RegistroViewTest(TestCase):
    """Tests del flujo de registro."""

    def test_registro_page_loads(self):
        resp = self.client.get(reverse("users:registro"))
        self.assertEqual(resp.status_code, 200)
        self.assertContains(resp, "Crear Cuenta")

    def test_registro_exitoso(self):
        resp = self.client.post(reverse("users:registro"), {
            "email": "nueva@test.com",
            "nombre": "Nueva",
            "apellido": "Paciente",
            "password1": "SecurePass!123",
            "password2": "SecurePass!123",
        })
        self.assertEqual(resp.status_code, 302)
        self.assertTrue(User.objects.filter(email="nueva@test.com").exists())
        user = User.objects.get(email="nueva@test.com")
        self.assertFalse(user.is_verified)
        self.assertEqual(user.rol, "paciente")

    def test_registro_envia_email_confirmacion(self):
        self.client.post(reverse("users:registro"), {
            "email": "confirm@test.com",
            "nombre": "Confirm",
            "apellido": "Test",
            "password1": "SecurePass!123",
            "password2": "SecurePass!123",
        })
        self.assertEqual(len(mail.outbox), 1)
        self.assertIn("Confirma tu cuenta", mail.outbox[0].subject)

    def test_registro_passwords_no_coinciden(self):
        resp = self.client.post(reverse("users:registro"), {
            "email": "fail@test.com",
            "nombre": "Fail",
            "apellido": "Test",
            "password1": "SecurePass!123",
            "password2": "DifferentPass!456",
        })
        self.assertEqual(resp.status_code, 200)
        self.assertFalse(User.objects.filter(email="fail@test.com").exists())

    def test_registro_email_duplicado(self):
        User.objects.create_user(email="dup@test.com", password="x", nombre="D", apellido="U")
        resp = self.client.post(reverse("users:registro"), {
            "email": "dup@test.com",
            "nombre": "Dup",
            "apellido": "Test",
            "password1": "SecurePass!123",
            "password2": "SecurePass!123",
        })
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(User.objects.filter(email="dup@test.com").count(), 1)


# ============================================================
# TESTS DE CONFIRMACION EMAIL
# ============================================================
class EmailConfirmacionTest(TestCase):
    """Tests para confirmacion de email."""

    def setUp(self):
        self.user = User.objects.create_user(
            email="verify@test.com",
            password="TestPass!123",
            nombre="Verify",
            apellido="Test",
            is_verified=False,
        )

    def test_confirmar_email_valido(self):
        uid = urlsafe_base64_encode(force_bytes(self.user.pk))
        token = email_confirmation_token.make_token(self.user)
        resp = self.client.get(reverse("users:confirmar_email", kwargs={"uidb64": uid, "token": token}))
        self.assertEqual(resp.status_code, 302)
        self.user.refresh_from_db()
        self.assertTrue(self.user.is_verified)

    def test_confirmar_email_token_invalido(self):
        uid = urlsafe_base64_encode(force_bytes(self.user.pk))
        resp = self.client.get(reverse("users:confirmar_email", kwargs={"uidb64": uid, "token": "invalid-token"}))
        self.assertEqual(resp.status_code, 302)
        self.user.refresh_from_db()
        self.assertFalse(self.user.is_verified)


# ============================================================
# TESTS DE LOGIN
# ============================================================
@override_settings(RATELIMIT_ENABLE=False)
class LoginViewTest(TestCase):
    """Tests del flujo de login."""

    def setUp(self):
        self.user = User.objects.create_user(
            email="login@test.com",
            password="TestPass!123",
            nombre="Login",
            apellido="Test",
            is_verified=True,
        )

    def test_login_page_loads(self):
        resp = self.client.get(reverse("users:login"))
        self.assertEqual(resp.status_code, 200)

    def test_login_exitoso_paciente(self):
        resp = self.client.post(reverse("users:login"), {
            "email": "login@test.com",
            "password": "TestPass!123",
        })
        self.assertEqual(resp.status_code, 302)
        self.assertIn("paciente", resp.url)

    def test_login_exitoso_doctora(self):
        self.user.rol = "doctora"
        self.user.save()
        resp = self.client.post(reverse("users:login"), {
            "email": "login@test.com",
            "password": "TestPass!123",
        })
        self.assertEqual(resp.status_code, 302)
        self.assertIn("doctora", resp.url)

    def test_login_exitoso_admin(self):
        self.user.rol = "admin"
        self.user.save()
        resp = self.client.post(reverse("users:login"), {
            "email": "login@test.com",
            "password": "TestPass!123",
        })
        self.assertEqual(resp.status_code, 302)
        self.assertIn("admin", resp.url)

    def test_login_password_incorrecto(self):
        resp = self.client.post(reverse("users:login"), {
            "email": "login@test.com",
            "password": "WrongPassword",
        })
        self.assertEqual(resp.status_code, 200)

    def test_login_email_no_verificado(self):
        self.user.is_verified = False
        self.user.save()
        resp = self.client.post(reverse("users:login"), {
            "email": "login@test.com",
            "password": "TestPass!123",
        })
        self.assertEqual(resp.status_code, 200)


# ============================================================
# TESTS DE LOGOUT
# ============================================================
class LogoutViewTest(TestCase):
    """Tests del flujo de logout."""

    def setUp(self):
        self.user = User.objects.create_user(
            email="logout@test.com",
            password="TestPass!123",
            nombre="Logout",
            apellido="Test",
            is_verified=True,
        )
        self.client.login(username="logout@test.com", password="TestPass!123")

    def test_logout_redirige_a_home(self):
        resp = self.client.get(reverse("users:logout"))
        self.assertEqual(resp.status_code, 302)
        self.assertEqual(resp.url, "/")


# ============================================================
# TESTS DE PERMISOS POR ROL
# ============================================================
class RolePermissionsTest(TestCase):
    """Tests de acceso por rol - 403 para rutas no autorizadas."""

    def setUp(self):
        self.paciente = User.objects.create_user(
            email="pac@test.com", password="Pass!123", nombre="P", apellido="A",
            rol="paciente", is_verified=True,
        )
        self.doctora = User.objects.create_user(
            email="doc@test.com", password="Pass!123", nombre="D", apellido="A",
            rol="doctora", is_verified=True,
        )
        self.admin = User.objects.create_user(
            email="adm@test.com", password="Pass!123", nombre="A", apellido="A",
            rol="admin", is_verified=True,
        )

    def test_paciente_accede_a_su_panel(self):
        self.client.login(username="pac@test.com", password="Pass!123")
        resp = self.client.get(reverse("users:panel_paciente"))
        self.assertEqual(resp.status_code, 200)

    def test_paciente_no_accede_a_panel_doctora(self):
        self.client.login(username="pac@test.com", password="Pass!123")
        resp = self.client.get(reverse("users:panel_doctora"))
        self.assertEqual(resp.status_code, 403)

    def test_paciente_no_accede_a_panel_admin(self):
        self.client.login(username="pac@test.com", password="Pass!123")
        resp = self.client.get(reverse("users:panel_admin"))
        self.assertEqual(resp.status_code, 403)

    def test_doctora_accede_a_su_panel(self):
        self.client.login(username="doc@test.com", password="Pass!123")
        resp = self.client.get(reverse("users:panel_doctora"))
        self.assertEqual(resp.status_code, 200)

    def test_doctora_no_accede_a_panel_admin(self):
        self.client.login(username="doc@test.com", password="Pass!123")
        resp = self.client.get(reverse("users:panel_admin"))
        self.assertEqual(resp.status_code, 403)

    def test_admin_accede_a_su_panel(self):
        self.client.login(username="adm@test.com", password="Pass!123")
        resp = self.client.get(reverse("users:panel_admin"))
        self.assertEqual(resp.status_code, 200)

    def test_usuario_anonimo_redirige_a_login(self):
        resp = self.client.get(reverse("users:panel_paciente"))
        self.assertEqual(resp.status_code, 302)
        self.assertIn("login", resp.url)


# ============================================================
# TESTS DE PASSWORD RESET
# ============================================================
@override_settings(EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend")
class PasswordResetTest(TestCase):
    """Tests del flujo de reset de password."""

    def setUp(self):
        self.user = User.objects.create_user(
            email="reset@test.com",
            password="OldPass!123",
            nombre="Reset",
            apellido="Test",
            is_verified=True,
        )

    def test_password_reset_page_loads(self):
        resp = self.client.get(reverse("users:password_reset"))
        self.assertEqual(resp.status_code, 200)

    def test_password_reset_envia_email(self):
        resp = self.client.post(reverse("users:password_reset"), {"email": "reset@test.com"})
        self.assertEqual(resp.status_code, 302)
        self.assertEqual(len(mail.outbox), 1)
