"""Import-compatibility and barrel-import narrowing regression tests.

Spec refs:
- docs/proposals/execution-architecture-refactor.md#tests-required/consumer-conformance-tests
- docs/proposals/execution-architecture-refactor.md#refactor-program/wave-4-schema-and-core-cleanup

Purpose:
These tests capture import compatibility BEFORE package splits occur.
They encode the public surface that must remain accessible through
compatibility wrappers during Wave 4 schema/core cleanup.

Test-only constraint:
- Do not implement package moves beyond minimal fixtures/helpers
"""

from __future__ import annotations

import pytest


# =============================================================================
# SECTION 1: Import Compatibility Regression Tests
# =============================================================================


class TestCoreSchemasPublicSurface:
    """Tests that core.schemas public surface remains stable.

    These imports must continue working through barrel imports or
    explicit re-exports during/after Wave 4 cleanup.
    """

    def test_import_enums_from_schemas(self) -> None:
        """Enums must be importable from core.schemas."""
        from anima.core.schemas import (
            AgentState,
            CompletionPolicy,
            ContextMode,
            LifecycleType,
            SideEffectPolicy,
            ToolPosture,
        )

        assert LifecycleType.ephemeral is not None
        assert LifecycleType.daemon is not None
        assert CompletionPolicy.auto is not None
        assert CompletionPolicy.explicit is not None

    def test_import_errors_from_schemas(self) -> None:
        """Error types must be importable from core.schemas."""
        from anima.core.schemas import (
            AgentNotFoundError,
            AnimaError,
            InternalError,
            NotFoundError,
        )

        assert AgentNotFoundError is not None
        assert NotFoundError is not None

    def test_import_models_from_schemas(self) -> None:
        """Core model types must be importable from core.schemas."""
        from anima.core.schemas import (
            AgentStateData,
            JobSpec,
            PersonaSpec,
        )

        # Models must be instantiable
        persona = PersonaSpec(
            id="test",
            description="Test persona",
            prompt="Test prompt",
            model="gpt-4o",
        )
        assert persona.id == "test"

        job = JobSpec(
            persona_ref="test@v1",
            instance_id="inst_test",
        )
        assert job.instance_id == "inst_test"

    def test_import_contract_types_from_schemas(self) -> None:
        """Contract types re-exported from schemas must be accessible."""
        from anima.core.schemas import (
            CapabilityToken,
            CompactionConfig,
            ContextBundle,
            Meta,
            RawTailEntry,
            SummaryRef,
        )

        # Contract types must be importable
        assert CompactionConfig is not None
        assert ContextBundle is not None
        assert RawTailEntry is not None
        assert SummaryRef is not None
        assert CapabilityToken is not None
        assert Meta is not None

    def test_import_state_types_from_schemas(self) -> None:
        """State types re-exported from schemas must be accessible."""
        from anima.core.schemas import (
            AgentResult,
            AgentStateData,
            LogEntry,
            TokenUsage,
            ToolCallCount,
        )

        # State types must be importable
        assert AgentResult is not None
        assert AgentStateData is not None
        assert LogEntry is not None
        assert TokenUsage is not None
        assert ToolCallCount is not None


class TestCoreContractsPublicSurface:
    """Tests that core.contracts public surface remains stable."""

    def test_import_contract_types_from_contracts(self) -> None:
        """Contract types must be importable from core.contracts."""
        from anima.core.contracts import (
            CONTRACT_SCHEMA_VERSION,
            ChildInheritancePolicy,
            ContractIdentity,
            ContractResolverProvenance,
            ContractSourceHashes,
            FilesystemScope,
            InlinePersonaBehavior,
            MaterializedPersonaStatus,
            PersonaProvenance,
            PersonaSource,
            PersonaSpawnability,
            SessionTransportState,
            SpawnabilityReason,
        )

        CONTRACT_SCHEMA_VERSION  # Must be defined
        assert ContractIdentity is not None
        assert PersonaProvenance is not None
        assert PersonaSpawnability is not None
        assert SessionTransportState is not None

    def test_import_runtime_types_from_contracts(self) -> None:
        """Runtime types must be importable from core.contracts."""
        from anima.core.contracts import (
            RuntimeModelContext,
            RuntimeOutputPolicy,
            RuntimeToolAccess,
            RuntimeToolName,
            RuntimeToolPreset,
        )

        # Runtime types must be importable
        tool_access = RuntimeToolAccess(
            effective_side_effect_policy="read_only",
        )
        assert tool_access is not None

        model_context = RuntimeModelContext(values={"instance_id": "test"})
        assert model_context.values["instance_id"] == "test"

    def test_import_job_and_capability_from_contracts(self) -> None:
        """JobSpec and CapabilityToken must be importable from contracts."""
        from anima.core.contracts import (
            CapabilityToken,
            CapabilityTokenView,
            JobSpec,
            JobSpecView,
            Meta,
            TraceMetaView,
        )

        # Must be importable
        assert JobSpec is not None
        assert CapabilityToken is not None
        assert Meta is not None


class TestCoreConversationPublicSurface:
    """Tests that core.conversation public surface remains stable."""

    def test_import_conversation_types_from_conversation(self) -> None:
        """Conversation types must be importable from core.conversation."""
        from anima.core.conversation import (
            CompactionConfig,
            CompactionSessionView,
            ContextBundle,
            ContextBundleView,
            ConversationMessageView,
            ConversationRole,
            ConversationSummaryView,
            RawTailEntry,
            SummaryRef,
        )

        # All conversation types must be accessible
        assert CompactionConfig is not None
        assert CompactionSessionView is not None
        assert ContextBundle is not None
        assert ContextBundleView is not None
        assert ConversationMessageView is not None
        assert ConversationRole is not None
        assert ConversationSummaryView is not None
        assert RawTailEntry is not None
        assert SummaryRef is not None

    def test_conversation_role_literal_values(self) -> None:
        """ConversationRole must have correct literal values."""
        from anima.core.conversation import ConversationRole

        # Role must be a literal with expected values
        # Protocol uses this type, verify literal membership
        valid_roles: tuple[ConversationRole, ...] = ("user", "assistant", "tool")
        assert all(role in ("user", "assistant", "tool") for role in valid_roles)


class TestCoreExecutionPublicSurface:
    """Tests that core.execution public surface remains stable."""

    def test_import_execution_types_from_execution(self) -> None:
        """Execution types must be importable from core.execution."""
        from anima.core.execution import (
            ExecutionIntent,
            ExecutionIntentDefaults,
            ExecutionIntentOverrides,
            ExecutionSessionState,
            ResolvedExecutionContract,
            ResolvedLifecyclePolicy,
            ResolvedRuntime,
        )

        # Execution types must be importable
        assert ExecutionIntent is not None
        assert ResolvedExecutionContract is not None
        assert ExecutionSessionState is not None

    def test_resolver_is_importable(self) -> None:
        """Resolver function must be importable."""
        from anima.core.execution import resolve_execution_contract

        assert resolve_execution_contract is not None
        assert callable(resolve_execution_contract)


# =============================================================================
# SECTION 2: Barrel Import Narrowing Regression Tests
# =============================================================================


class TestBarrelImportNarrowing:
    """Tests that barrel imports can be narrowed without breaking.

    These tests encode the invariant that public symbols accessed via
    barrel imports (from anima.core.schemas import X) remain accessible.
    The actual narrow barrel (removing __all__ entries) must preserve
    all symbols accessed by downstream code.
    """

    def test_schemas_barrel_all_exports_are_used(self) -> None:
        """All exported symbols in core.schemas __all__ are actually imported.

        This test will break if __all__ contains unused symbols that can
        be safely removed during Wave 4 narrowing.
        """
        from anima.core import schemas

        all_symbols = set(schemas.__all__)
        assert len(all_symbols) > 0

        # These are the critical symbols that MUST remain
        critical_symbols = {
            # Enums
            "AgentState",
            "CompletionPolicy",
            "ContextMode",
            "LifecycleType",
            "SideEffectPolicy",
            "ToolPosture",
            # Errors
            "AgentNotFoundError",
            "InternalError",
            "NotFoundError",
            "AnimaError",
            # Models
            "AgentStateData",
            "JobSpec",
            "PersonaSpec",
            # Contract re-exports
            "CapabilityToken",
            "CompactionConfig",
            "ContextBundle",
            "Meta",
            "RawTailEntry",
            "SummaryRef",
            # State re-exports
            "AgentResult",
            "LogEntry",
            "TokenUsage",
            "ToolCallCount",
        }

        # All critical symbols must be in __all__
        missing = critical_symbols - all_symbols
        assert not missing, f"Critical symbols missing from __all__: {missing}"

    def test_contracts_barrel_all_exports_are_used(self) -> None:
        """All exported symbols in core.contracts __all__ are needed."""
        from anima.core import contracts

        all_symbols = set(contracts.__all__)
        assert len(all_symbols) > 0

        # These are critical symbols that MUST remain
        critical_symbols = {
            # Version constants
            "CONTRACT_SCHEMA_VERSION",
            # Identity types
            "ContractIdentity",
            "ContractResolverProvenance",
            "ContractSourceHashes",
            # Persona types
            "PersonaProvenance",
            "PersonaSpawnability",
            "SpawnabilityReason",
            # Runtime types
            "RuntimeToolAccess",
            "RuntimeModelContext",
            "RuntimeOutputPolicy",
            # Session types
            "SessionTransportState",
            # Specs
            "JobSpec",
            "CapabilityToken",
            "Meta",
        }

        missing = critical_symbols - all_symbols
        assert not missing, f"Critical symbols missing from __all__: {missing}"

    def test_conversation_barrel_all_exports_are_used(self) -> None:
        """All exported symbols in core.conversation __all__ are needed."""
        from anima.core.conversation import __all__ as conversation_all

        all_symbols = set(conversation_all)
        assert len(all_symbols) > 0

        # All symbols are critical for conversation/prune/compaction
        critical_symbols = {
            "CompactionConfig",
            "CompactionSessionView",
            "ContextBundle",
            "ContextBundleView",
            "ConversationMessageView",
            "ConversationRole",
            "ConversationSummaryView",
            "RawTailEntry",
            "SummaryRef",
        }

        missing = critical_symbols - all_symbols
        assert not missing, f"Critical symbols missing from __all__: {missing}"

    def test_schemas_imports_from_contracts_and_conversation(self) -> None:
        """core.schemas re-exports from contracts and conversation modules."""
        # These imports verify the re-export chain works
        from anima.core.schemas import JobSpec, Meta, CapabilityToken
        from anima.core.schemas import CompactionConfig, ContextBundle, RawTailEntry

        # All should be accessible via schemas barrel import
        assert JobSpec is not None
        assert Meta is not None
        assert CapabilityToken is not None
        assert CompactionConfig is not None
        assert ContextBundle is not None
        assert RawTailEntry is not None


class TestCrossModuleImportPaths:
    """Tests verifying cross-module import paths are stable."""

    def test_compaction_imports_conversation_types(self) -> None:
        """core.compaction must import from core.conversation."""
        from anima.core import compaction

        # Verify the module can be imported
        assert compaction is not None

        # Verify it has the expected functions
        assert hasattr(compaction, "is_overflow")
        assert hasattr(compaction, "build_compaction_prompt")
        assert hasattr(compaction, "apply_compaction")
        assert hasattr(compaction, "find_compaction_boundary")

    def test_prune_is_importable(self) -> None:
        """core.prune must be importable for compaction flows."""
        from anima.core import prune

        assert prune is not None
        assert hasattr(prune, "prune_tool_outputs")

    def test_schemas_models_importable_directly(self) -> None:
        """core.schemas.models can be imported directly for internal use."""
        from anima.core.schemas import models

        # Internal module accessible for direct import
        assert models is not None
        assert hasattr(models, "PersonaSpec")
        assert hasattr(models, "JobSpec")


# =============================================================================
# SECTION 3: Import Compatibility During Migration
# =============================================================================


class TestImportCompatibilityShims:
    """Tests that import compatibility shims work correctly.

    These tests verify that the TypeAlias shims in core.conversation
    correctly re-export from core.schemas.models.
    """

    def test_conversation_config_is_schema_alias(self) -> None:
        """CompactionConfig in conversation is an alias to schemas.models."""
        from anima.core.conversation import CompactionConfig as ConvConfig
        from anima.core.schemas import CompactionConfig as SchemaConfig
        from anima.core.schemas.models import CompactionConfig as ModelConfig

        # All should refer to the same class
        assert ConvConfig is SchemaConfig
        assert SchemaConfig is ModelConfig

    def test_context_bundle_is_schema_alias(self) -> None:
        """ContextBundle in conversation is an alias to schemas.models."""
        from anima.core.conversation import ContextBundle as ConvBundle
        from anima.core.schemas import ContextBundle as SchemaBundle
        from anima.core.schemas.models import ContextBundle as ModelBundle

        assert ConvBundle is SchemaBundle
        assert SchemaBundle is ModelBundle

    def test_raw_tail_entry_is_schema_alias(self) -> None:
        """RawTailEntry in conversation is an alias to schemas.models."""
        from anima.core.conversation import RawTailEntry as ConvEntry
        from anima.core.schemas import RawTailEntry as SchemaEntry
        from anima.core.schemas.models import RawTailEntry as ModelEntry

        assert ConvEntry is SchemaEntry
        assert SchemaEntry is ModelEntry

    def test_summary_ref_is_schema_alias(self) -> None:
        """SummaryRef in conversation is an alias to schemas.models."""
        from anima.core.conversation import SummaryRef as ConvRef
        from anima.core.schemas import SummaryRef as SchemaRef
        from anima.core.schemas.models import SummaryRef as ModelRef

        assert ConvRef is SchemaRef
        assert SchemaRef is ModelRef
