import collections.abc
import dataclasses
import itertools

from deb_build_artifact_gather.tags import (
    Tag,
    TAG_ICE,
    TAG_COMPILER,
    TAG_GCC,
    TAG_AUTOCONF,
    TAG_CONFIG_LOG,
    TAG_CMAKE,
    TAG_MESON,
    TAG_TEST_LOG,
)
from deb_build_artifact_gather.variables import VARIABLE_BUILD_DIR, Variable


class Directory:

    def possible_matches(
        self,
        variables: collections.abc.Mapping[str, collections.abc.Iterable[str]],
        environ: collections.abc.Mapping[str, str],
    ) -> collections.abc.Iterable[str]:
        raise NotImplementedError


class EnvBasedDirectory(Directory):

    def __init__(self, *envvar: str, fallback: str | None = None) -> None:
        self.envvars = envvar
        self.fallback = fallback

    def possible_matches(
        self,
        variables: collections.abc.Mapping[str, collections.abc.Iterable[str]],
        environ: collections.abc.Mapping[str, str],
    ) -> collections.abc.Iterable[str]:
        for envvar in self.envvars:
            v = environ.get(envvar)
            if v is not None:
                yield v
        fallback = self.fallback
        if fallback is not None:
            yield fallback


class VariableBasedDirectory(Directory):

    def __init__(self, variable: Variable) -> None:
        self.variable = variable

    def possible_matches(
        self,
        variables: collections.abc.Mapping[str, collections.abc.Iterable[str]],
        environ: collections.abc.Mapping[str, str],
    ) -> collections.abc.Iterable[str]:
        v = variables.get(self.variable.variable_name)
        if v is not None:
            yield from v
        default_value = self.variable.default_value
        if default_value is not None:
            yield default_value


def inside_tmpdir() -> Directory:
    return EnvBasedDirectory("AUTOPKGTEST_TMP", "TMPDIR", fallback="/tmp")


def builddir() -> Directory:
    return VariableBasedDirectory(VARIABLE_BUILD_DIR)


@dataclasses.dataclass(slots=True, frozen=True)
class ArtifactRule:
    glob_or_basename: str
    relative_to_directory: Directory
    tags: collections.abc.Sequence[Tag]

    def possible_dir_matches(
        self,
        variables: collections.abc.Mapping[str, collections.abc.Sequence[str]],
        environ: collections.abc.Mapping[str, str],
    ) -> collections.abc.Iterable[str]:
        return self.relative_to_directory.possible_matches(variables, environ)


def artifact_rule(
    relative_to: Directory,
    glob_or_basename: str,
    *,
    tags: collections.abc.Sequence[Tag] | Tag = tuple(),
):
    all_tags = (tags,) if isinstance(tags, Tag) else tuple(tags)
    return ArtifactRule(
        glob_or_basename,
        relative_to,
        all_tags,
    )


RULES = [
    artifact_rule(
        inside_tmpdir(),
        "cc*.out",
        tags=[TAG_ICE, TAG_COMPILER, TAG_GCC],
    ),
    artifact_rule(
        builddir(),
        "config.log",
        tags=[TAG_CONFIG_LOG, TAG_AUTOCONF],
    ),
    artifact_rule(
        builddir(),
        "CMakeCache.txt",
        tags=[TAG_CONFIG_LOG, TAG_CMAKE],
    ),
    artifact_rule(
        builddir(),
        "CMakeFiles/CMakeOutput.log",
        tags=[TAG_CONFIG_LOG, TAG_CMAKE],
    ),
    artifact_rule(
        builddir(),
        "CMakeFiles/CMakeError.log",
        tags=[TAG_CONFIG_LOG, TAG_CMAKE],
    ),
    artifact_rule(
        builddir(),
        "meson-logs/meson-log.txt",
        tags=[TAG_CONFIG_LOG, TAG_MESON],
    ),
    artifact_rule(
        builddir(),
        "meson-logs/testlog.txt",
        tags=[TAG_TEST_LOG, TAG_MESON],
    ),
]

ENV_VARS = sorted(
    set(
        itertools.chain(
            itertools.chain.from_iterable(
                r.relative_to_directory.envvars
                for r in RULES
                if isinstance(r.relative_to_directory, EnvBasedDirectory)
            ),
            ("DEB_ARTIFACTS_EXTRACTION_IPC_DIR",),
        )
    )
)

VARIABLES = {
    r.relative_to_directory.variable.variable_name: r.relative_to_directory.variable
    for r in RULES
    if isinstance(r.relative_to_directory, VariableBasedDirectory)
}
