from typing import Callable, List
from abc import ABC, abstractmethod


class File(ABC):
    """
    Represents a file in the file system.
    """

    def __init__(self, name: str, permissions: int, owner: str):
        assert 0 <= permissions <= 0o777, "Invalid permissions..."
        self.name = name
        self.permissions = permissions
        self.owner = owner

    @abstractmethod
    def map_content(self, function: Callable[[str], str]) -> "File":
        """
        Maps over the content of regular files, and just traverses the rest of the file system.
        Does not follow links. The function is applied to the content of the file.
        """
        pass

    @abstractmethod
    def map_files(self, function: Callable[["File"], None]):
        """
        Maps over all the files and directories in the file system. Does not follow links.
        Changes are done in-place.
        """
        pass


class RegularFile(File):
    """
    Represents a regular file in the file system, which is just a file with some content inside.
    """

    def __init__(self, name: str, permissions: int, owner: str, content: str):
        super().__init__(name, permissions, owner)
        self.content = content

    def map_content(self, function: Callable[[str], str]) -> "RegularFile":
        return RegularFile(self.name, self.permissions, self.owner, function(self.content))

    def map_files(self, function: Callable[["File"], None]):
        function(self)

    def __eq__(self, other):
        return self.name == other.name and self.permissions == other.permissions and self.owner == other.owner and self.content == other.content


class Directory(File):
    """
    Represents a directory in the file system, which is basically a file with a list of files.
    """

    def __init__(self, name: str, permissions: int, owner: str, files: List[File]):
        super().__init__(name, permissions, owner)
        self.files = files

    def map_content(self, function: Callable[[str], str]) -> "Directory":
        return Directory(self.name, self.permissions, self.owner, [f.map_content(function) for f in self.files])

    def map_files(self, function: Callable[["File"], None]):
        function(self)
        for f in self.files:
            f.map_files(function)

    def __eq__(self, other):
        return self.name == other.name and self.permissions == other.permissions and self.owner == other.owner and all(
            a == b for a, b in zip(self.files, other.files))