from app_data.crypto import hash_check, hash_create, string_decode, string_encode
from django.test import TestCase


class CryptoTest(TestCase):

    def test_basic_encryption_decryption(self):
        key = "my_secret_key"
        message = "Hello World"
        encrypted = string_encode(key, message)
        decrypted = string_decode(key, encrypted)
        self.assertEqual(message, decrypted)

    def test_empty_string(self):
        key = "test_key"
        message = ""
        encrypted = string_encode(key, message)
        decrypted = string_decode(key, encrypted)
        self.assertEqual(message, decrypted)

    def test_long_message(self):
        key = "long_key_12345"
        message = "A" * 10000
        encrypted = string_encode(key, message)
        decrypted = string_decode(key, encrypted)
        self.assertEqual(message, decrypted)

    def test_unicode_characters(self):
        key = "unicode_key"
        message = "Hello 世界 🌍 café élève"
        encrypted = string_encode(key, message)
        decrypted = string_decode(key, encrypted)
        self.assertEqual(message, decrypted)

    def test_special_characters(self):
        key = "special_key"
        message = "!@#$%^&*()_+-=[]{}|;':\",./<>?\n\t\r"
        encrypted = string_encode(key, message)
        decrypted = string_decode(key, encrypted)
        self.assertEqual(message, decrypted)

    def test_numeric_string(self):
        key = "123456"
        message = "1234567890"
        encrypted = string_encode(key, message)
        decrypted = string_decode(key, encrypted)
        self.assertEqual(message, decrypted)

    def test_multiline_text(self):
        key = "multiline_key"
        message = "Line 1\nLine 2\nLine 3\n"
        encrypted = string_encode(key, message)
        decrypted = string_decode(key, encrypted)
        self.assertEqual(message, decrypted)

    def test_json_like_content(self):
        key = "json_key"
        message = '{"name": "test", "value": 123, "nested": {"key": "value"}}'
        encrypted = string_encode(key, message)
        decrypted = string_decode(key, encrypted)
        self.assertEqual(message, decrypted)

    def test_different_keys_produce_different_output(self):
        key1 = "key_one"
        key2 = "key_two"
        message = "Same message"
        encrypted1 = string_encode(key1, message)
        encrypted2 = string_encode(key2, message)
        self.assertNotEqual(encrypted1, encrypted2)

    def test_randomization_same_key_different_output(self):
        key = "same_key"
        message = "Same message"
        encrypted1 = string_encode(key, message)
        encrypted2 = string_encode(key, message)
        self.assertNotEqual(encrypted1, encrypted2)

    def test_wrong_key_fails_decryption(self):
        key1 = "correct_key"
        key2 = "wrong_key"
        message = "Secret message"
        encrypted = string_encode(key1, message)
        with self.assertRaises(UnicodeDecodeError):
            string_decode(key2, encrypted)

    def test_base64_output_format(self):
        key = "test_key"
        message = "Test message"
        encrypted = string_encode(key, message)
        self.assertIsInstance(encrypted, str)
        self.assertGreater(len(encrypted), 0)
        self.assertRegex(encrypted, r'^[A-Za-z0-9_-]+=*$')

    def test_short_key(self):
        key = "k"
        message = "Message with very short key"
        encrypted = string_encode(key, message)
        decrypted = string_decode(key, encrypted)
        self.assertEqual(message, decrypted)

    def test_very_long_key(self):
        key = "x" * 1000
        message = "Message with very long key"
        encrypted = string_encode(key, message)
        decrypted = string_decode(key, encrypted)
        self.assertEqual(message, decrypted)

    def test_key_with_special_chars(self):
        key = "k€¥!@#$%^&*()"
        message = "Testing key with special characters"
        encrypted = string_encode(key, message)
        decrypted = string_decode(key, encrypted)
        self.assertEqual(message, decrypted)


class HashTest(TestCase):

    def test_hash_create_basic(self):
        password = "my_password"
        hashed = hash_create(password)
        self.assertIsNotNone(hashed)
        self.assertIn("$sha256$", hashed)

    def test_hash_check_correct_password(self):
        password = "correct_password"
        hashed = hash_create(password)
        self.assertTrue(hash_check(password, hashed))

    def test_hash_check_wrong_password(self):
        password = "correct_password"
        wrong = "wrong_password"
        hashed = hash_create(password)
        self.assertFalse(hash_check(wrong, hashed))

    def test_hash_randomization(self):
        password = "same_password"
        hash1 = hash_create(password)
        hash2 = hash_create(password)
        self.assertNotEqual(hash1, hash2)

    def test_hash_both_verify(self):
        password = "test_password"
        hash1 = hash_create(password)
        hash2 = hash_create(password)
        self.assertTrue(hash_check(password, hash1))
        self.assertTrue(hash_check(password, hash2))


    def test_hash_empty_string(self):
        hashed = hash_create("")
        self.assertIsNone(hashed)

    def test_hash_none_input(self):
        hashed = hash_create(None)
        self.assertIsNone(hashed)

    def test_hash_check_empty_hash(self):
        result = hash_check("password", "")
        self.assertFalse(result)

    def test_hash_check_none_hash(self):
        result = hash_check("password", None)
        self.assertFalse(result)

    def test_hash_check_invalid_format(self):
        result = hash_check("password", "invalid_hash_format")
        self.assertFalse(result)

    def test_hash_unicode_password(self):
        password = "pässwörd_世界_🔒"
        hashed = hash_create(password)
        self.assertTrue(hash_check(password, hashed))
        self.assertFalse(hash_check("wrong", hashed))

    def test_hash_long_password(self):
        password = "x" * 1000
        hashed = hash_create(password)
        self.assertTrue(hash_check(password, hashed))

    def test_hash_special_characters(self):
        password = "p@$$w0rd!#%&*()_+-=[]{}|;':\",./<>?"
        hashed = hash_create(password)
        self.assertTrue(hash_check(password, hashed))
