# Copyright 2018 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

import logging
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Optional

from pants.backend.python.goals.coverage_py import (
    CoverageConfig,
    CoverageSubsystem,
    PytestCoverageData,
)
from pants.backend.python.subsystems.pytest import PyTest, PythonTestFieldSet
from pants.backend.python.util_rules.interpreter_constraints import InterpreterConstraints
from pants.backend.python.util_rules.local_dists import LocalDistsPex, LocalDistsPexRequest
from pants.backend.python.util_rules.pex import Pex, PexRequest, VenvPex, VenvPexProcess
from pants.backend.python.util_rules.pex_from_targets import PexFromTargetsRequest
from pants.backend.python.util_rules.python_sources import (
    PythonSourceFiles,
    PythonSourceFilesRequest,
)
from pants.core.goals.test import (
    BuildPackageDependenciesRequest,
    BuiltPackageDependencies,
    JunitXMLDir,
    JunitXMLDirSource,
    RuntimePackageDependenciesField,
    TestDebugRequest,
    TestExtraEnv,
    TestFieldSet,
    TestResult,
    TestSubsystem,
)
from pants.core.util_rules.config_files import ConfigFiles, ConfigFilesRequest
from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
from pants.engine.addresses import Address
from pants.engine.collection import Collection
from pants.engine.environment import CompleteEnvironment
from pants.engine.fs import (
    EMPTY_DIGEST,
    CreateDigest,
    Digest,
    DigestSubset,
    Directory,
    MergeDigests,
    PathGlobs,
    RemovePrefix,
    Snapshot,
)
from pants.engine.process import (
    FallibleProcessResult,
    InteractiveProcess,
    Process,
    ProcessCacheScope,
)
from pants.engine.rules import Get, MultiGet, collect_rules, rule
from pants.engine.target import Target, TransitiveTargets, TransitiveTargetsRequest, WrappedTarget
from pants.engine.unions import UnionMembership, UnionRule, union
from pants.option.global_options import GlobalOptions
from pants.python.python_setup import PythonSetup
from pants.util.logging import LogLevel

logger = logging.getLogger()


# -----------------------------------------------------------------------------------------
# Plugin hook
# -----------------------------------------------------------------------------------------


@dataclass(frozen=True)
class PytestPluginSetup:
    """The result of custom set up logic before Pytest runs.

    Please reach out it if you would like certain functionality, such as allowing your plugin to set
    environment variables.
    """

    digest: Digest = EMPTY_DIGEST


@union
@dataclass(frozen=True)  # type: ignore[misc]
class PytestPluginSetupRequest(ABC):
    """A request to set up the test environment before Pytest runs, e.g. to set up databases.

    To use, subclass PytestPluginSetupRequest, register the rule
    `UnionRule(PytestPluginSetupRequest, MyCustomPytestPluginSetupRequest)`, and add a rule that
    takes your subclass as a parameter and returns `PytestPluginSetup`.
    """

    target: Target

    @classmethod
    @abstractmethod
    def is_applicable(cls, target: Target) -> bool:
        """Whether the setup implementation should be used for this target or not."""


class AllPytestPluginSetups(Collection[PytestPluginSetup]):
    pass


# TODO: Why is this necessary? We should be able to use `PythonTestFieldSet` as the rule param.
@dataclass(frozen=True)
class AllPytestPluginSetupsRequest:
    address: Address


@rule
async def run_all_setup_plugins(
    request: AllPytestPluginSetupsRequest, union_membership: UnionMembership
) -> AllPytestPluginSetups: