"""Python language plugin for the codeflash optimization pipeline."""

from __future__ import annotations

from typing import TYPE_CHECKING

import attrs

from codeflash_core import LanguagePlugin, validate_capabilities

from ..analysis._discovery import _ALL_DIR_EXCLUDES

if TYPE_CHECKING:
    from .._configuration import PythonConfiguration
    from .._state import PythonState


def _build_capabilities() -> dict[str, object]:
    """Build the Python capability map.

    Imports are deferred to avoid circular dependencies and
    to keep import time low — these modules are heavy.
    """
    from ..analysis._discovery import (  # noqa: PLC0415
        discover_functions,
    )
    from ..analysis._normalizer import (  # noqa: PLC0415
        normalize_python_code,
    )
    from ..codegen._replacement import (  # noqa: PLC0415
        replace_functions_in_file,
    )
    from ..context.pipeline import (  # noqa: PLC0415
        get_code_optimization_context,
    )
    from ..testing._parse_results import (  # noqa: PLC0415
        parse_test_results,
    )
    from ..testing._test_runner import (  # noqa: PLC0415
        async_run_behavioral_tests,
    )
    from ..verification._verification import (  # noqa: PLC0415
        compare_test_results,
    )

    return {
        # Required capabilities (see REQUIRED_CAPABILITIES).
        "normalize_code": normalize_python_code,
        "discover_functions": discover_functions,
        "extract_context": get_code_optimization_context,
        "replace_code": replace_functions_in_file,
        "run_tests": async_run_behavioral_tests,
        "parse_results": parse_test_results,
        "compare_results": compare_test_results,
        # Optional capabilities.
        "detect_numerical": _lazy_detect_numerical,
        "generate_tests": _lazy_generate_tests,
        "run_benchmarks": _lazy_run_benchmarks,
    }


def _lazy_detect_numerical() -> object:
    """Placeholder — actual binding happens at call site."""
    from .._function_optimizer import (  # type: ignore[import-not-found]  # noqa: PLC0415
        is_numerical_code,
    )

    return is_numerical_code


def _lazy_generate_tests() -> object:
    """Placeholder — actual binding happens at call site."""
    from ..testing._testgen import generate_tests  # noqa: PLC0415

    return generate_tests


def _lazy_run_benchmarks() -> object:
    """Placeholder — actual binding happens at call site."""
    from ..testing._test_runner import (  # noqa: PLC0415
        async_run_benchmarking_tests,
    )

    return async_run_benchmarking_tests


@attrs.frozen
class PythonPlugin:
    """Python-specific metadata and capabilities for the optimization pipeline.

    Satisfies the :class:`codeflash_core.LanguagePlugin` protocol.
    Pass an instance to core pipeline functions that need
    language-level information.

    Usage::

        from codeflash_python.pipeline import PythonPlugin
        from codeflash_python import PythonConfiguration, PythonState

        cfg = PythonConfiguration(
            project_root=Path("/code/myproject"),
            tests_root=Path("/code/myproject/tests"),
            test_framework="pytest",
        )
        state = PythonState(cfg=cfg)
        plugin = PythonPlugin(configuration=cfg, state=state)

        # Access metadata directly.
        assert plugin.language_id == "python"

        # Access capabilities by name.
        normalize = plugin.capabilities["normalize_code"]
        unique = dedup_candidates(
            candidates,
            normalize_fn=normalize,
            ...
        )
    """

    # -- Metadata --------------------------------------------------

    language_id: str = "python"
    file_extensions: tuple[str, ...] = (".py",)
    test_framework: str = "pytest"
    comment_prefix: str = "#"
    dir_excludes: frozenset[str] = _ALL_DIR_EXCLUDES
    serialization_format: str = "pickle"

    # -- Configuration & State -------------------------------------

    configuration: PythonConfiguration = attrs.field(
        kw_only=True,
        repr=False,
    )
    """Python session configuration."""

    state: PythonState = attrs.field(kw_only=True, repr=False)
    """Cached analysis state for this session."""

    # -- Capabilities ----------------------------------------------

    capabilities: dict[str, object] = attrs.field(
        factory=_build_capabilities,
        repr=False,
    )
    """Python capability map.

    Populated automatically with all required and optional
    capabilities.  See :func:`_build_capabilities`.
    """

    def __attrs_post_init__(self) -> None:
        """Validate that all required capabilities are declared."""
        missing = validate_capabilities(self.capabilities)
        if missing:
            msg = (
                f"PythonPlugin is missing required capabilities: "
                f"{', '.join(missing)}"
            )
            raise ValueError(msg)

    # -- Convenience typed accessors -------------------------------

    @property
    def normalize_code(self) -> object:
        """Shortcut for ``capabilities["normalize_code"]``."""
        return self.capabilities["normalize_code"]

    @property
    def discover_functions(self) -> object:
        """Shortcut for ``capabilities["discover_functions"]``."""
        return self.capabilities["discover_functions"]


# -- Static type assertion -----------------------------------------


def _assert_protocol_compliance() -> None:
    """Compile-time check that PythonPlugin satisfies LanguagePlugin."""
    _: LanguagePlugin = PythonPlugin(  # type: ignore[assignment]
        configuration=None,  # type: ignore[arg-type]
        state=None,  # type: ignore[arg-type]
    )
