import os
from typing import (
    Dict,
    List,
    Optional,
    Union,
)

from filesystem import FileSystem


class FakeFileSystem(FileSystem):

    def __init__(
        self,
        dir_list: Optional[Dict[str, List[Union[str, Dict[str, bytes], Dict[str, List]]]]] = None,
        file_contents: Optional[Dict[str, bytes]] = None,
    ) -> None:

        self.file_contents = file_contents or {}

        if dir_list is None:
            self.dir_list = {}
        else:
            self.dir_list = self._recursively_add_file_content(dir_list=dir_list)

    def list_entries(
        self,
        path: str,
    ) -> List[str]:

        if path in self.dir_list:
            ret = []
            for file in self.dir_list[path]:
                if isinstance(file, dict):
                    ret.append(os.path.join(path, list(file.keys())[0]))
                else:
                    ret.append(os.path.join(path, file))

            return ret

        return []

    def read_file_content(
        self,
        path: str,
    ) -> bytes:

        if path in self.file_contents:
            return self.file_contents[path]

        raise RuntimeError(f"No such file: {path}")

    def is_dir(
        self,
        path: str,
    ) -> bool:

        return path in self.dir_list

    def is_file(self, path: str) -> bool:
        for directory in self.dir_list:
            for dir_file in self.dir_list[directory]:
                full_name = os.path.join(directory, dir_file)
                if full_name == path:
                    return True

        return False

    def _recursively_add_file_content(
        self,
        dir_list: Dict[str, List[Union[str, Dict[str, bytes], Dict[str, List]]]],
        parent_dir: str = "",
    ):
        ret = {}
        for directory in dir_list.keys():
            for file in dir_list[directory]:
                full_dir = os.path.join(parent_dir, directory)
                if full_dir not in ret:
                    ret[full_dir] = []

                if not isinstance(file, dict):
                    ret[full_dir].append(file)
                    continue

                file_name = list(file.keys())[0]
                file_content = file[file_name]

                ret[full_dir].append(file_name)

                if isinstance(file_content, bytes):
                    self.file_contents[os.path.join(parent_dir, directory, file_name)] = file_content
                else:
                    inner_dir = self._recursively_add_file_content(
                        dir_list=file,
                        parent_dir=os.path.join(parent_dir, directory),
                    )
                    ret.update(inner_dir)

        return ret
