# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
# For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt

"""Management of core choices."""

from __future__ import annotations

import os
import sys
from typing import Any

from coverage import env
from coverage.disposition import FileDisposition
from coverage.exceptions import ConfigError
from coverage.misc import isolate_module
from coverage.pytracer import PyTracer
from coverage.sysmon import SysMonitor
from coverage.types import (
    TFileDisposition,
    Tracer,
    TWarnFn,
)


os = isolate_module(os)

try:
    # Use the C extension code when we can, for speed.
    from coverage.tracer import CTracer, CFileDisposition
    HAS_CTRACER = True
except ImportError:
    # Couldn't import the C extension, maybe it isn't built.
    if os.getenv("COVERAGE_CORE") == "ctrace":      # pragma: part covered
        # During testing, we use the COVERAGE_CORE environment variable
        # to indicate that we've fiddled with the environment to test this
        # fallback code.  If we thought we had a C tracer, but couldn't import
        # it, then exit quickly and clearly instead of dribbling confusing
        # errors. I'm using sys.exit here instead of an exception because an
        # exception here causes all sorts of other noise in unittest.
        sys.stderr.write("*** COVERAGE_CORE is 'ctrace' but can't import CTracer!\n")
        sys.exit(1)
    HAS_CTRACER = False


class Core:
    """Information about the central technology enabling execution measurement."""

    tracer_class: type[Tracer]
    tracer_kwargs: dict[str, Any]
    file_disposition_class: type[TFileDisposition]
    supports_plugins: bool
    packed_arcs: bool
    systrace: bool

    def __init__(self,
        warn: TWarnFn,
        timid: bool,
        metacov: bool,
    ) -> None:
        # Defaults
        self.tracer_kwargs = {}

        core_name: str | None
        if timid:
            core_name = "pytrace"
        else:
            core_name = os.getenv("COVERAGE_CORE")

            if core_name == "sysmon" and not env.PYBEHAVIOR.pep669:
                warn("sys.monitoring isn't available, using default core", slug="no-sysmon")
                core_name = None

            if not core_name:
                # Once we're comfortable with sysmon as a default:
                # if env.PYBEHAVIOR.pep669 and self.should_start_context is None:
                #     core_name = "sysmon"
                if HAS_CTRACER:
                    core_name = "ctrace"
                else:
                    core_name = "pytrace"

        if core_name == "sysmon":
            self.tracer_class = SysMonitor
            self.tracer_kwargs = {"tool_id": 3 if metacov else 1}
            self.file_disposition_class = FileDisposition
            self.supports_plugins = False
            self.packed_arcs = False
            self.systrace = False
        elif core_name == "ctrace":
            self.tracer_class = CTracer
            self.file_disposition_class = CFileDisposition
            self.supports_plugins = True
            self.packed_arcs = True
            self.systrace = True
        elif core_name == "pytrace":
            self.tracer_class = PyTracer
            self.file_disposition_class = FileDisposition
            self.supports_plugins = False
            self.packed_arcs = False
            self.systrace = True
        else:
            raise ConfigError(f"Unknown core value: {core_name!r}")
