"""Pytest plugin helpers."""

from __future__ import annotations

import os
import pathlib
import types
import typing as t

import pytest

from project_config.compat import TypeAlias
from project_config.tree import Tree


FileType: TypeAlias = t.Optional[t.Union[str, bool]]
FilesType: TypeAlias = t.Union[
    t.List[t.Tuple[str, FileType]],
    t.Dict[str, FileType],
]
RootdirType: TypeAlias = t.Union[str, pathlib.Path]


def create_files(  # noqa: D103
    files: FilesType,
    rootdir: RootdirType,
) -> None:
    if isinstance(rootdir, pathlib.Path):
        rootdir = str(rootdir)
    _files = files.items() if isinstance(files, dict) else files
    for fpath, content in _files:
        if content is False:
            continue
        full_path = os.path.join(rootdir, fpath)

        if content is None:
            os.mkdir(full_path)
        else:
            # same name as an existent directory, means that `files` has been
            # passed as a list of tuples
            content = t.cast(str, content)
            # ensure parent path directory exists
            parent_fpath, _ = os.path.splitext(full_path)
            if parent_fpath:
                os.makedirs(parent_fpath, exist_ok=True)

            if os.path.isdir(full_path):
                continue

            try:
                with open(full_path, "w", encoding="utf-8") as f:
                    f.write(content)
            except OSError:
                # globs raising here in Windows
                continue


def create_tree(  # noqa: D103
    files: FilesType,
    rootdir: RootdirType,
    cache_files: bool = False,
) -> Tree:
    create_files(files, rootdir)
    tree = Tree(str(rootdir))
    if cache_files:
        _files = (
            list(files) if isinstance(files, dict) else [f[0] for f in files]
        )
        tree.cache_files(_files)
    return tree


def assert_expected_files(  # noqa: D103
    expected_files: FilesType,
    rootdir: RootdirType,
) -> None:
    if isinstance(rootdir, pathlib.Path):
        rootdir = str(rootdir)
    _expected_files = (
        expected_files.items()
        if isinstance(expected_files, dict)
        else expected_files
    )
    for fpath, content in _expected_files:
        full_path = os.path.join(rootdir, fpath)
        if content is False:
            assert not os.path.exists(full_path)
        else:
            assert os.path.exists(full_path)
            if content is not None:
                try:
                    with open(full_path, encoding="utf-8") as f:
                        assert f.read() == content
                except IsADirectoryError:
                    continue
                except PermissionError:
                    pytest.skip()
                    continue
                except OSError:
                    continue


def get_reporter_class_from_module(  # noqa: D103
    reporter_module: types.ModuleType,
    color: bool,
) -> type:
    for object_name in dir(reporter_module):
        if object_name.startswith(("_", "Base")):
            continue
        if (color and "ColorReporter" in object_name) or (
            not color
            and "Reporter" in object_name
            and "ColorReporter" not in object_name
        ):
            return getattr(reporter_module, object_name)  # type: ignore

    raise ValueError(
        f"No{' color' if color else ''} reporter class found in"
        f" module '{reporter_module.__name__}'",
    )


__all__ = (
    "assert_expected_files",
    "create_files",
    "create_tree",
    "get_reporter_class_from_module",
)
