"""Language plugin protocol for multi-language optimization.

Architecture
------------

The optimization pipeline is **composable, not prescriptive**.

``codeflash-core`` provides reusable building blocks — candidate
evaluation, deduplication, ranking, repair loops, AI request
construction.  Each language package (``codeflash-python``,
``codeflash-java``, ``codeflash-javascript``) owns its own
orchestration loop and assembles it from those blocks, passing
language-specific callables where needed::

    # In codeflash_python/_optimizer.py
    from codeflash_core import pipeline

    class PythonOptimizer:
        def optimize_function(self, func):
            ctx = get_code_optimization_context(func, ...)
            baseline = pipeline.establish_baseline(
                run_fn=run_behavioral_tests,
                parse_fn=parse_test_results,
                ...
            )
            best = pipeline.evaluate_candidates(
                candidates,
                run_fn=run_behavioral_tests,
                compare_fn=compare_test_results,
                normalize_fn=normalize_python_code,
                ...
            )

Core's pipeline steps declare exactly what they need as
parameters — they never call methods on a protocol object.
This lets each language wire things differently (skip steps,
reorder them, add language-specific stages) without fighting
an inherited interface.

:class:`LanguagePlugin` carries **metadata** that shared
pipeline steps need, a **configuration** for session settings,
a **state** for cached analysis artifacts, and a
**capabilities** dict that declares the language-specific
callables the plugin provides.

The capabilities dict serves as a **checklist for new language
implementers** — if you're adding JavaScript support, the
required capabilities tell you exactly what functions you need
to write.  See :mod:`._capabilities` for the typed protocols.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Protocol, runtime_checkable

if TYPE_CHECKING:
    from ._configuration import LanguageConfiguration


@runtime_checkable
class LanguagePlugin(Protocol):
    """Language contract consumed by shared pipeline steps.

    Each language package creates a concrete implementation
    (typically a frozen attrs class) and passes it to core
    pipeline functions that need language-level information.

    The protocol has three layers:

    **Metadata** — simple attributes that pipeline steps read
    directly (``language_id``, ``file_extensions``, etc.).

    **Configuration** — a :class:`LanguageConfiguration`
    subclass carrying session settings (project root, test
    framework config, AI parameters).  Centralizes what was
    previously scattered across multiple constructor arguments.

    **Capabilities** — a dict mapping well-known names to
    callables that implement language-specific behavior.
    Pipeline steps use ``plugin.capabilities["normalize_code"]``
    instead of requiring a separate ``normalize_fn`` parameter.
    See :data:`._capabilities.REQUIRED_CAPABILITIES` for the
    full list.

    The composable pipeline philosophy is preserved: languages
    still own their orchestration loop and can skip, reorder,
    or add steps.  The capabilities dict just makes the
    contract explicit rather than implicit.
    """

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

    language_id: str
    """Short identifier: ``"python"``, ``"java"``, ``"javascript"``.

    Included in AI optimization requests so the model knows
    which language it is generating code for.
    """

    file_extensions: tuple[str, ...]
    """Source file extensions including the dot.

    Example: ``(".py",)`` or ``(".java",)``.
    """

    test_framework: str
    """Default test framework name.

    Examples: ``"pytest"``, ``"junit"``, ``"jest"``.
    """

    comment_prefix: str
    """Line comment prefix: ``"#"`` or ``"//"``.

    Used by shared pipeline steps that inject runtime comments
    into generated tests.
    """

    dir_excludes: frozenset[str]
    """Directory names to skip during source discovery.

    Typical entries: ``"__pycache__"``, ``"node_modules"``,
    ``".git"``, ``"build"``, ``"target"``.
    """

    serialization_format: str
    """Test-result serialization: ``"pickle"`` or ``"json"``.

    Determines how the test harness serializes return values
    and behavioral data.  Python uses ``"pickle"``; Java and
    JavaScript use ``"json"``.
    """

    # -- Configuration ---------------------------------------------

    configuration: LanguageConfiguration
    """Session configuration for this language.

    Carries project paths, test settings, and AI parameters.
    Language packages subclass :class:`LanguageConfiguration`
    to add language-specific fields.
    """

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

    capabilities: dict[str, object]
    """Language-specific callables keyed by well-known names.

    Required capabilities (must be present)::

        normalize_code    — NormalizeFn
        discover_functions — DiscoverFn
        extract_context   — ExtractContextFn
        replace_code      — ReplaceCodeFn
        run_tests         — RunTestsFn
        parse_results     — ParseResultsFn
        compare_results   — CompareResultsFn

    Optional capabilities::

        run_benchmarks    — language-specific benchmark runner
        generate_tests    — AI-powered test generation
        detect_numerical  — numerical code detection

    Use :func:`._capabilities.validate_capabilities` at
    construction time to verify all required capabilities
    are declared.

    Pipeline steps access capabilities by name::

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