import hashlib
import os
import secrets

class ContextHardenedObject:
    """
    A class to represent a context-hardened object. This object aims to prevent
    context leakage by salting and hashing sensitive data and providing
    mechanisms to verify data integrity.
    """

    def __init__(self, data, salt=None):
        """
        Initializes a ContextHardenedObject.

        Args:
            data: The sensitive data to be protected.  Can be any type, but will be converted to a string for hashing.
            salt: An optional salt to use. If None, a random salt will be generated.
        """
        self.salt = salt or secrets.token_hex(16)  # Generate a random salt if none is provided
        self.data = str(data) # Ensure data is a string for consistent hashing.
        self.hashed_data = self._hash_data()

    def _hash_data(self):
        """
        Hashes the data with the salt using SHA-256.

        Returns:
            The hexadecimal representation of the hash.
        """
        salted_data = self.salt + self.data
        hashed_data = hashlib.sha256(salted_data.encode('utf-8')).hexdigest()
        return hashed_data

    def verify(self, data):
        """
        Verifies if the given data matches the original data.

        Args:
            data: The data to verify.

        Returns:
            True if the data matches, False otherwise.
        """
        salted_data = self.salt + str(data)
        hashed_data = hashlib.sha256(salted_data.encode('utf-8')).hexdigest()
        return hashed_data == self.hashed_data

    def get_hashed_representation(self):
        """
        Returns the hashed representation of the data (for storage or transmission).

        Returns:
            A tuple containing the salt and the hashed data.
        """
        return self.salt, self.hashed_data

    @staticmethod
    def from_hashed_representation(salt, hashed_data):
        """
        Creates a ContextHardenedObject from a stored salt and hashed data.

        Args:
            salt: The salt used to hash the original data.
            hashed_data: The hashed representation of the original data.

        Returns:
            A ContextHardenedObject instance.  Note: The 'data' attribute will be None.
        """
        obj = ContextHardenedObject(data="") # Dummy data, as the original data is not recoverable.
        obj.salt = salt
        obj.hashed_data = hashed_data
        obj.data = None # Indicate that the original data is not available.
        return obj

    def __repr__(self):
        return f"ContextHardenedObject(salt='{self.salt[:8]}...', hashed_data='{self.hashed_data[:8]}...')"


def generate_key():
    """
    Generates a cryptographically secure random key.

    Returns:
        A hexadecimal string representing the key.
    """
    return secrets.token_hex(32)


class ContextHardenedKey:
    """
    Represents a cryptographically secure key hardened against context leakage.
    """
    def __init__(self, key=None):
        """
        Initializes a ContextHardenedKey.  If no key is provided, a new one is generated.

        Args:
            key:  An optional key to use. If None, a new key is generated.
        """
        self.key = key or generate_key()
        self.hardened_key = ContextHardenedObject(self.key)

    def get_hardened_representation(self):
        """
        Returns a hardened representation of the key.
        """
        return self.hardened_key.get_hashed_representation()

    def verify(self, key):
        """
        Verifies a potential key against the hardened representation.

        Args:
            key: The key to verify.

        Returns:
            True if the key matches, False otherwise.
        """
        return self.hardened_key.verify(key)

    @staticmethod
    def from_hardened_representation(salt, hashed_key):
        """
        Reconstructs a ContextHardenedKey from a hardened representation.

        Args:
            salt: The salt.
            hashed_key: The hashed key.

        Returns:
            A ContextHardenedKey instance.  The original key is NOT recoverable.
        """
        hardened_object = ContextHardenedObject.from_hashed_representation(salt, hashed_key)
        key_object = ContextHardenedKey(key=None)  # Initialize with None, since the original key is lost.
        key_object.hardened_key = hardened_object
        key_object.key = None # Indicate that the original key is not available.
        return key_object

    def __repr__(self):
        return f"ContextHardenedKey(hardened_key={self.hardened_key})"