"""Component domain policies — Phase 11.2."""
from __future__ import annotations

from typing import Any

from app.models.types import (
    ComponentStatus,
    ComponentVersionStatus,
)
from app.validators.components import (
    ensure_component_status_transition,
    ensure_version_mutable,
    ensure_version_status_transition,
)
from shared.exceptions import AppError


class ComponentLifecyclePolicy:
    """Enforces component catalog status transitions."""

    def assert_status_change(
        self, *, current: ComponentStatus, target: ComponentStatus
    ) -> ComponentStatus:
        ensure_component_status_transition(current, target)
        return target

    def assert_can_soft_delete(self, *, status: ComponentStatus) -> None:
        if status == ComponentStatus.ARCHIVED:
            return
        # Soft-delete allowed from any status; archive is preferred path.
        return


class ComponentVersionPolicy:
    """Enforces immutability and publish/retire rules."""

    def assert_can_update(self, version: Any) -> None:
        ensure_version_mutable(version)

    def assert_publish(
        self, *, current: ComponentVersionStatus
    ) -> ComponentVersionStatus:
        ensure_version_status_transition(current, ComponentVersionStatus.PUBLISHED)
        return ComponentVersionStatus.PUBLISHED

    def assert_retire(
        self, *, current: ComponentVersionStatus
    ) -> ComponentVersionStatus:
        ensure_version_status_transition(current, ComponentVersionStatus.RETIRED)
        return ComponentVersionStatus.RETIRED


class PagePlacementPolicy:
    """Composition shells may only pin published (non-retired) versions."""

    def assert_version_placeable(self, version: Any) -> None:
        status = getattr(version, "status", None)
        if status != ComponentVersionStatus.PUBLISHED:
            raise AppError(
                "فقط نسخه منتشرشده قابل جایگذاری است",
                error_code="component_version_not_published",
                status_code=409,
                details={"status": getattr(status, "value", status)},
            )
        if getattr(version, "is_deleted", False):
            raise AppError(
                "نسخه حذف‌شده قابل جایگذاری نیست",
                error_code="component_version_deleted",
                status_code=409,
            )

    def assert_component_placeable(self, component: Any) -> None:
        status = getattr(component, "status", None)
        if status in (ComponentStatus.ARCHIVED,):
            raise AppError(
                "کامپوننت بایگانی‌شده قابل جایگذاری نیست",
                error_code="component_archived",
                status_code=409,
            )
        if getattr(component, "is_deleted", False):
            raise AppError(
                "کامپوننت حذف‌شده قابل جایگذاری نیست",
                error_code="component_deleted",
                status_code=409,
            )
