"""Domain repository protocol for lifecycle plans.

Defines the ``LifecyclePlanRepositoryProtocol`` — the port that the
application layer uses to persist and retrieve v3 lifecycle plans.
Infrastructure adapters (e.g. the SQLAlchemy-backed
``LifecyclePlanRepository``) must satisfy this protocol.

Based on the clean architecture principle described in the specification:
adapters live at the edge; the domain layer defines the contracts.
"""

from __future__ import annotations

from typing import Protocol, runtime_checkable

from cleveragents.domain.models.core.plan import Plan


@runtime_checkable
class LifecyclePlanRepositoryProtocol(Protocol):
    """Port for v3 lifecycle plan persistence.

    All methods that mutate state flush but do **not** commit; the caller
    or a Unit-of-Work wrapper is responsible for committing the transaction.
    """

    def create(self, plan: Plan) -> Plan:
        """Persist a new plan and return it.

        Args:
            plan: A v3 ``Plan`` domain model instance.

        Returns:
            The same ``Plan`` after persistence (identity pass-through).
        """
        ...

    def get(self, plan_id: str) -> Plan | None:
        """Retrieve a plan by its ULID primary key.

        Args:
            plan_id: ULID string of the plan.

        Returns:
            The ``Plan`` domain object, or ``None`` if not found.
        """
        ...

    def get_by_name(self, namespaced_name: str) -> Plan | None:
        """Retrieve a plan by its full namespaced name string.

        Args:
            namespaced_name: Full ``namespace/name`` string.

        Returns:
            The ``Plan`` domain object, or ``None`` if not found.
        """
        ...

    def update(self, plan: Plan) -> Plan:
        """Update all mutable fields of an existing plan.

        Args:
            plan: The ``Plan`` domain model with updated fields.

        Returns:
            The same ``Plan`` after persistence.
        """
        ...

    def list_all(self) -> list[Plan]:
        """List all persisted plans, ordered by ``created_at`` DESC.

        Returns:
            List of ``Plan`` domain objects.
        """
        ...

    def list_plans(
        self,
        phase: str | None = None,
        processing_state: str | None = None,
        action_name: str | None = None,
        project_name: str | None = None,
        namespace: str | None = None,
        limit: int = 100,
        offset: int = 0,
    ) -> list[Plan]:
        """List plans with optional filters, ordered by ``created_at`` DESC.

        Args:
            phase: Optional phase filter (e.g. ``"action"``).
            processing_state: Optional state filter (e.g. ``"queued"``).
            action_name: Optional action name filter.
            project_name: Optional project name filter.
            namespace: Optional namespace filter.
            limit: Maximum number of results (default 100).
            offset: Number of results to skip (default 0).

        Returns:
            List of ``Plan`` domain objects.
        """
        ...

    def count(
        self,
        phase: str | None = None,
        processing_state: str | None = None,
    ) -> int:
        """Count matching plans with optional filters.

        Args:
            phase: Optional phase filter.
            processing_state: Optional state filter.

        Returns:
            Number of matching plans.
        """
        ...

    def delete(self, plan_id: str) -> bool:
        """Delete a plan by its ULID primary key.

        Args:
            plan_id: ULID string of the plan to delete.

        Returns:
            ``True`` if the plan was deleted, ``False`` if not found.
        """
        ...
