from django.test import TestCase
from django.contrib.auth import get_user_model
from django.utils import timezone
from datetime import timedelta

User = get_user_model()


class PasswordExpiryTests(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(
            email="testuser@example.com",
            username="testuser",
            password="TestPassword123!",
            role="CANDIDATE"
        )

    def test_password_expiry_not_reached(self):
        # Expiry set to 90 days in the future
        self.user.password_expiry_date = timezone.now() + timedelta(days=90)
        self.user.password_expired = False
        self.user.save()

        expired = self.user.check_password_expiry()
        self.assertFalse(expired)
        self.assertFalse(self.user.password_expired)

    def test_password_expiry_reached(self):
        # Expiry set to 1 day in the past
        self.user.password_expiry_date = timezone.now() - timedelta(days=1)
        self.user.password_expired = False
        self.user.save()

        expired = self.user.check_password_expiry()
        self.assertTrue(expired)
        self.assertTrue(self.user.password_expired)

        # Verify it persisted in database
        db_user = User.objects.get(pk=self.user.pk)
        self.assertTrue(db_user.password_expired)

    def test_password_change_resets_expiry(self):
        # Set to expired initially
        self.user.password_expiry_date = timezone.now() - timedelta(days=1)
        self.user.password_expired = True
        self.user.save()

        # Update password
        self.user.set_password("NewPassword123!")
        self.user.save()

        # Should no longer be expired, and expiry date should be in the future
        self.assertFalse(self.user.password_expired)
        self.assertGreater(self.user.password_expiry_date, timezone.now())
        self.assertFalse(self.user.check_password_expiry())

    def test_admin_is_exempt_from_password_expiry(self):
        admin = User.objects.create_user(
            email="admin@example.com",
            username="adminuser",
            password="AdminPassword123!",
            role=User.Role.ADMIN,
        )
        admin.password_expiry_date = timezone.now() - timedelta(days=1)
        admin.password_expired = True
        admin.save()

        expired = admin.check_password_expiry()
        self.assertFalse(expired)
        self.assertFalse(admin.password_expired)
        self.assertIsNone(admin.password_expiry_date)

    def test_admin_password_change_does_not_set_expiry(self):
        admin = User.objects.create_user(
            email="admin2@example.com",
            username="adminuser2",
            password="AdminPassword123!",
            role=User.Role.ADMIN,
        )

        admin.set_password("AnotherAdmin123!")
        admin.save()

        self.assertFalse(admin.password_expired)
        self.assertIsNone(admin.password_expiry_date)


from unittest.mock import patch
from django.core import mail
from apps.authentication.models import OTPRecord


class ForgotPasswordTests(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(
            email="forgot_test@example.com",
            username="forgotuser",
            password="OldPassword123!",
            role="CANDIDATE",
        )

    def test_forgot_password_sends_email_otp(self):
        response = self.client.post(
            "/api/v1/auth/password/forgot/",
            {"email": "forgot_test@example.com"},
            format="json",
        )
        self.assertEqual(response.status_code, 200)
        self.assertIn("registered email address", response.json()["message"])

        # Verify email sent via mail.outbox
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].to, ["forgot_test@example.com"])
        self.assertIn("Password Reset Verification Code", mail.outbox[0].subject)

        # Verify OTP record in DB
        otp_rec = OTPRecord.objects.get(user=self.user)
        self.assertTrue(otp_rec.otp_code.isdigit())
        self.assertEqual(len(otp_rec.otp_code), 6)

    def test_verify_and_reset_password_flow(self):
        # Step 1: Request OTP
        self.client.post("/api/v1/auth/password/forgot/", {"email": "forgot_test@example.com"})
        otp_code = OTPRecord.objects.get(user=self.user).otp_code

        # Step 2: Verify OTP
        verify_res = self.client.post(
            "/api/v1/auth/password/verify-otp/",
            {"email": "forgot_test@example.com", "otp": otp_code},
            format="json",
        )
        self.assertEqual(verify_res.status_code, 200)

        # Step 3: Reset password
        reset_res = self.client.post(
            "/api/v1/auth/password/reset/",
            {
                "email": "forgot_test@example.com",
                "otp": otp_code,
                "new_password": "NewSecretPassword123!",
                "confirm_password": "NewSecretPassword123!",
            },
            format="json",
        )
        self.assertEqual(reset_res.status_code, 200)

        # Confirm user can authenticate with new password
        self.user.refresh_from_db()
        self.assertTrue(self.user.check_password("NewSecretPassword123!"))


