try:
    from trezorio import fatfs, sdcard

    HAVE_SDCARD = True
    is_present = sdcard.is_present  # type: ignore [obscured-by-same-name]
    capacity = sdcard.capacity  # type: ignore [obscured-by-same-name]

except Exception:
    HAVE_SDCARD = False

    def is_present() -> bool:
        return False

    def capacity() -> int:
        return 0


from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from typing import Any, Callable, TypeVar
    from typing_extensions import ParamSpec

    P = ParamSpec("P")
    R = TypeVar("R")


class FilesystemWrapper:
    _INSTANCE: "FilesystemWrapper" | None = None

    def __init__(self, mounted: bool) -> None:
        if not HAVE_SDCARD:
            raise RuntimeError

        self.mounted = mounted
        self.counter = 0

    @classmethod
    def get_instance(cls, mounted: bool = True) -> "FilesystemWrapper":
        if cls._INSTANCE is None:
            cls._INSTANCE = cls(mounted=mounted)
        if cls._INSTANCE.mounted is not mounted:
            raise RuntimeError  # cannot request mounted and non-mounted instance at the same time
        return cls._INSTANCE

    def _deinit_instance(self) -> None:
        fatfs.unmount()
        sdcard.power_off()
        FilesystemWrapper._INSTANCE = None

    def __enter__(self) -> None:
        try:
            if self.counter <= 0:
                self.counter = 0
                sdcard.power_on()
                if self.mounted:
                    fatfs.mount()
            self.counter += 1
        except Exception:
            self._deinit_instance()
            raise

    def __exit__(self, exc_type: Any, exc_val: Any, tb: Any) -> None:
        self.counter -= 1
        if self.counter <= 0:
            self.counter = 0
            self._deinit_instance()


def filesystem(mounted: bool = True) -> FilesystemWrapper:
    return FilesystemWrapper.get_instance(mounted=mounted)


def with_filesystem(func: Callable[P, R]) -> Callable[P, R]:
    def wrapped_func(*args: P.args, **kwargs: P.kwargs) -> R:
        with filesystem():
            return func(*args, **kwargs)

    return wrapped_func
