import logging

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

class SnippetManager:
    """
    Manages code snippets, providing functionality to store, retrieve, list, and delete snippets.
    """

    def __init__(self):
        """
        Initializes the SnippetManager with an empty dictionary to store snippets.
        """
        self.snippets: dict[str, str] = {}
        logging.info("SnippetManager initialized.")

    def store_snippet(self, name: str, code: str) -> None:
        """
        Stores a code snippet with the given name.

        Args:
            name: The name of the snippet.
            code: The code of the snippet.
        """
        try:
            if not isinstance(name, str):
                raise TypeError(f"Snippet name must be a string, got {type(name)}")
            if not isinstance(code, str):
                raise TypeError(f"Snippet code must be a string, got {type(code)}")

            self.snippets[name] = code
            logging.info(f"Snippet '{name}' stored successfully.")
        except TypeError as e:
            logging.error(f"Error storing snippet '{name}': {e}")
            raise

    def retrieve_snippet(self, name: str) -> str | None:
        """
        Retrieves a code snippet by name.

        Args:
            name: The name of the snippet to retrieve.

        Returns:
            The code snippet if found, otherwise None.
        """
        try:
            if not isinstance(name, str):
                raise TypeError(f"Snippet name must be a string, got {type(name)}")

            if name in self.snippets:
                logging.info(f"Snippet '{name}' retrieved successfully.")
                return self.snippets[name]
            else:
                logging.warning(f"Snippet '{name}' not found.")
                return None
        except TypeError as e:
            logging.error(f"Error retrieving snippet '{name}': {e}")
            return None


    def list_snippets(self) -> list[str]:
        """
        Lists all available snippet names.

        Returns:
            A list of snippet names.
        """
        logging.info("Listing all snippets.")
        return list(self.snippets.keys())

    def delete_snippet(self, name: str) -> None:
        """
        Deletes a snippet by name.

        Args:
            name: The name of the snippet to delete.
        """
        try:
            if not isinstance(name, str):
                raise TypeError(f"Snippet name must be a string, got {type(name)}")

            if name in self.snippets:
                del self.snippets[name]
                logging.info(f"Snippet '{name}' deleted successfully.")
            else:
                logging.warning(f"Snippet '{name}' not found, cannot delete.")
        except TypeError as e:
            logging.error(f"Error deleting snippet '{name}': {e}")
            raise


# Example usage (for testing)
if __name__ == '__main__':
    snippet_manager = SnippetManager()

    # Store snippets
    snippet_manager.store_snippet("my_function", "def my_function():\n  print('Hello, world!')")
    snippet_manager.store_snippet("my_class", "class MyClass:\n  def __init__(self):\n    pass")

    # List snippets
    print("Available snippets:", snippet_manager.list_snippets())

    # Retrieve snippet
    my_function_code = snippet_manager.retrieve_snippet("my_function")
    if my_function_code:
        print("\nCode for 'my_function':\n", my_function_code)

    # Delete snippet
    snippet_manager.delete_snippet("my_function")
    print("\nAvailable snippets after deletion:", snippet_manager.list_snippets())

    # Try to retrieve deleted snippet
    retrieved_snippet = snippet_manager.retrieve_snippet("my_function")
    if retrieved_snippet is None:
        print("\nSnippet 'my_function' successfully deleted and cannot be retrieved.")

    # Test invalid input
    try:
        snippet_manager.store_snippet(123, 456)
    except TypeError as e:
        print(f"\nCaught expected exception: {e}")