import time
import unittest

from auth_core import AuthError, AuthService


class AuthFlowTests(unittest.TestCase):
    def setUp(self):
        self.auth = AuthService(jwt_key=b"test-key-" * 4, session_ttl=2)

    def test_register_hashes_and_returns_normalized_user(self):
        result = self.auth.register("Alice_Test", "correct horse battery")
        self.assertEqual(result, {"user": "alice_test"})
        self.assertNotIn("correct horse", repr(self.auth.__dict__))

    def test_duplicate_registration_rejected(self):
        self.auth.register("alice", "correct horse battery")
        with self.assertRaises(AuthError): self.auth.register("ALICE", "correct horse battery")

    def test_short_password_rejected(self):
        with self.assertRaises(AuthError): self.auth.register("alice", "short")

    def test_login_issues_opaque_session_and_optional_jwt(self):
        self.auth.register("alice", "correct horse battery")
        result = self.auth.login("alice", "correct horse battery", issue_jwt=True)
        self.assertTrue(result["session_token"] and result["csrf_token"] and result["jwt"])
        self.assertEqual(self.auth.verify_jwt(result["jwt"]), "alice")

    def test_bad_password_does_not_login(self):
        self.auth.register("alice", "correct horse battery")
        with self.assertRaises(AuthError): self.auth.login("alice", "wrong password")

    def test_csrf_required_for_mutation(self):
        self.auth.register("alice", "correct horse battery")
        result = self.auth.login("alice", "correct horse battery")
        with self.assertRaises(AuthError): self.auth.logout(result["session_token"], "wrong")
        self.auth.logout(result["session_token"], result["csrf_token"])

    def test_logout_revokes_session(self):
        self.auth.register("alice", "correct horse battery")
        result = self.auth.login("alice", "correct horse battery")
        self.auth.logout(result["session_token"], result["csrf_token"])
        with self.assertRaises(AuthError): self.auth.authenticate(result["session_token"])

    def test_expired_session_is_rejected(self):
        auth = AuthService(session_ttl=1)
        auth.register("alice", "correct horse battery")
        result = auth.login("alice", "correct horse battery")
        time.sleep(1.05)
        with self.assertRaises(AuthError): auth.authenticate(result["session_token"])

    def test_tampered_jwt_is_rejected(self):
        self.auth.register("alice", "correct horse battery")
        token = self.auth.login("alice", "correct horse battery", issue_jwt=True)["jwt"]
        parts = token.split("."); parts[1] = parts[1][:-1] + ("A" if parts[1][-1] != "A" else "B")
        with self.assertRaises(AuthError): self.auth.verify_jwt(".".join(parts))


if __name__ == "__main__":
    unittest.main()
