"""SQLAlchemy implementation of TestRepository."""

# Enable future language features for compatibility
from __future__ import annotations

# Import uuid
import uuid

# Import func from sqlalchemy
from sqlalchemy import func

# Import Session from sqlalchemy.orm
from sqlalchemy.orm import Session

# Import TestState from app.domain.enums
from app.domain.enums import TestState

# Import Test from app.models.test
from app.models.test import Test


# Define class SATestRepository
class SATestRepository:
    """Concrete test repository backed by SQLAlchemy."""

    # Define function __init__
    def __init__(self, session: Session) -> None:
        """Initialise the repository with a caller-provided session.

        Args:
            session (Session): The SQLAlchemy session to use for all queries.
        """
        # Assign self._session = session
        self._session = session

    # Define function find_by_id
    def find_by_id(self, test_id: uuid.UUID) -> Test | None:
        """Return a single test by its primary key.

        Args:
            test_id (uuid.UUID): The UUID primary key of the test.

        Returns:
            Test | None: The ORM model instance, or ``None`` if not found.
        """
        # Return (
        return (
            self._session.query(Test)
            # Chain .filter() call
            .filter(Test.id == test_id)
            # Chain .first() call
            .first()
        )

    # Define function list_by_technique
    def list_by_technique(self, technique_id: uuid.UUID) -> list[Test]:
        """Return all tests for a given technique, ordered by creation date.

        Args:
            technique_id (uuid.UUID): The UUID of the parent technique.

        Returns:
            list[Test]: ORM model instances ordered by ``created_at`` ascending.
        """
        # Return (
        return (
            self._session.query(Test)
            # Chain .filter() call
            .filter(Test.technique_id == technique_id)
            # Chain .order_by() call
            .order_by(Test.created_at)
            # Chain .all() call
            .all()
        )

    # Define function list_by_state
    def list_by_state(self, state: TestState) -> list[Test]:
        """Return all tests that are currently in the given workflow state.

        Args:
            state (TestState): The workflow state to filter on.

        Returns:
            list[Test]: All ORM model instances with the specified state.
        """
        # Return (
        return (
            self._session.query(Test)
            # Chain .filter() call
            .filter(Test.state == state)
            # Chain .all() call
            .all()
        )

    # Define function count_by_technique_and_state
    def count_by_technique_and_state(
        self,
        # Entry: technique_id
        technique_id: uuid.UUID,
    ) -> dict[TestState, int]:
        """Return per-state test counts for a specific technique.

        Args:
            technique_id (uuid.UUID): The UUID of the technique to aggregate for.

        Returns:
            dict[TestState, int]: Mapping of each state to the number of tests in that state.
        """
        # Assign rows = (
        rows = (
            self._session.query(Test.state, func.count(Test.id))
            # Chain .filter() call
            .filter(Test.technique_id == technique_id)
            # Chain .group_by() call
            .group_by(Test.state)
            # Chain .all() call
            .all()
        )
        # Assign result = {}
        result: dict[TestState, int] = {}
        # Iterate over rows
        for state_val, count in rows:
            # Assign key = (
            key = (
                state_val
                if isinstance(state_val, TestState)
                else TestState(state_val)
            )
            # Assign result[key] = count
            result[key] = count
        # Return result
        return result

    # Define function get_states_and_results_for_technique
    def get_states_and_results_for_technique(
        self,
        # Entry: technique_id
        technique_id: uuid.UUID,
    ) -> list[tuple[str, str | None]]:
        """Return lightweight ``(state, detection_result)`` pairs for a technique.

        Used by ``TechniqueEntity.recalculate_status()`` to avoid loading full
        ``Test`` models.

        Args:
            technique_id (uuid.UUID): The UUID of the technique to query.

        Returns:
            list[tuple[str, str | None]]: Each tuple contains the state string
                and the detection result string (or ``None``).
        """
        # Assign rows = (
        rows = (
            self._session.query(Test.state, Test.detection_result)
            # Chain .filter() call
            .filter(Test.technique_id == technique_id)
            # Chain .all() call
            .all()
        )
        # Return [
        return [
            (
                r.state.value if hasattr(r.state, "value") else str(r.state),
                (
                    r.detection_result.value
                    if hasattr(r.detection_result, "value")
                    else r.detection_result
                ),
            )
            for r in rows
        ]
