"""Port defining how the application accesses technique data.

This is a domain contract — implementations live in infrastructure/.
The domain layer NEVER imports the implementation.
"""

from __future__ import annotations

import uuid
from typing import NamedTuple, Protocol, runtime_checkable

from app.domain.entities.technique import TechniqueEntity
from app.domain.enums import TechniqueStatus


class TechniqueWithCounts(NamedTuple):
    """Pre-aggregated technique data for heatmap/scoring."""

    entity: TechniqueEntity
    test_count: int
    validated_test_count: int
    detection_rule_count: int


@runtime_checkable
class TechniqueRepository(Protocol):
    """Data access contract for techniques (one per aggregate root)."""

    # -- Single-entity access ----------------------------------------------

    def find_by_id(self, technique_id: uuid.UUID) -> TechniqueEntity | None: ...

    def find_by_mitre_id(self, mitre_id: str) -> TechniqueEntity | None: ...

    # -- List access -------------------------------------------------------

    def list_all(
        self,
        *,
        tactic: str | None = None,
        status: TechniqueStatus | None = None,
        review_required: bool | None = None,
    ) -> list[TechniqueEntity]: ...

    def list_by_ids(self, ids: list[uuid.UUID]) -> list[TechniqueEntity]: ...

    # -- Batch queries (scoring/heatmap performance) -----------------------

    def count_by_status(self) -> dict[TechniqueStatus, int]: ...

    def find_all_with_test_counts(self) -> list[TechniqueWithCounts]: ...

    # -- Mutations ---------------------------------------------------------

    def save(self, technique: TechniqueEntity) -> TechniqueEntity: ...

    def exists_by_mitre_id(self, mitre_id: str) -> bool: ...
