"""Property-based tests for unified CWD and setting sources.

**Feature: unified-swarm-workspace-cwd**

Uses Hypothesis to verify that all agent configurations produce the same
cached SwarmWS path as ``cwd`` and ``['project']`` as ``setting_sources``,
regardless of ``global_user_mode`` or other config variations.

**Validates: Requirements 1.3, 1.4, 5.1, 5.4**
"""
from pathlib import Path
from unittest.mock import patch, AsyncMock, MagicMock

import pytest
from hypothesis import given, strategies as st, settings, HealthCheck

from core.initialization_manager import InitializationManager
from tests.helpers import PROPERTY_SETTINGS





# ---------------------------------------------------------------------------
# Strategies
# ---------------------------------------------------------------------------

_safe_chars = st.sampled_from(
    "abcdefghijklmnopqrstuvwxyz"
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "0123456789_-"
)

safe_segment = st.text(alphabet=_safe_chars, min_size=1, max_size=12)

random_workspace_path = st.lists(
    safe_segment, min_size=1, max_size=3,
).map(lambda parts: "/" + "/".join(parts) + "/SwarmWS")

# Strategy for agent configs with varying global_user_mode
agent_config_strategy = st.fixed_dictionaries({
    "id": st.text(alphabet=_safe_chars, min_size=1, max_size=10),
    "global_user_mode": st.booleans(),
    "permission_mode": st.just("bypassPermissions"),
    "allowed_directories": st.lists(
        st.text(alphabet=_safe_chars, min_size=1, max_size=20).map(lambda s: f"/extra/{s}"),
        min_size=0,
        max_size=3,
    ),
})


# ---------------------------------------------------------------------------
# Property 2: Unified cwd regardless of workspace mode
# ---------------------------------------------------------------------------


class TestUnifiedCwdRegardlessOfWorkspaceMode:
    """Property 2: Unified cwd regardless of workspace mode.

    For any agent configuration (whether global_user_mode is True or False),
    the cwd should always be the cached SwarmWS path — never Path.home()
    and never a per-agent directory.

    **Validates: Requirements 1.3, 1.4, 5.1**
    """

    @given(
        agent_config=agent_config_strategy,
        workspace_path=random_workspace_path,
    )
    @PROPERTY_SETTINGS
    def test_cwd_always_equals_cached_workspace_path(
        self, agent_config: dict, workspace_path: str,
    ):
        """The working directory is always the cached SwarmWS path,
        regardless of global_user_mode setting.

        **Validates: Requirements 1.3, 1.4**
        """
        # Simulate the inlined logic from _build_options step 4
        manager = InitializationManager()
        manager._cached_workspace_path = workspace_path

        working_directory = manager.get_cached_workspace_path()

        # The cwd must always be the cached workspace path
        assert working_directory == workspace_path, (
            f"cwd should be cached workspace path '{workspace_path}', "
            f"got '{working_directory}'"
        )

        # Must never be home directory
        assert working_directory != str(Path.home()), (
            f"cwd should never be Path.home() ({Path.home()}), "
            f"got '{working_directory}'"
        )

        # Must never be a per-agent workspace directory
        assert "/workspaces/" not in working_directory, (
            f"cwd should never contain '/workspaces/' (per-agent dir), "
            f"got '{working_directory}'"
        )

    @given(
        agent_config=agent_config_strategy,
        workspace_path=random_workspace_path,
    )
    @PROPERTY_SETTINGS
    def test_cwd_independent_of_global_user_mode(
        self, agent_config: dict, workspace_path: str,
    ):
        """The cwd is the same whether global_user_mode is True or False.

        **Validates: Requirements 1.3, 1.4, 5.1**
        """
        manager = InitializationManager()
        manager._cached_workspace_path = workspace_path

        # Test with global_user_mode=True
        agent_config_global = {**agent_config, "global_user_mode": True}
        cwd_global = manager.get_cached_workspace_path()

        # Test with global_user_mode=False
        agent_config_isolated = {**agent_config, "global_user_mode": False}
        cwd_isolated = manager.get_cached_workspace_path()

        assert cwd_global == cwd_isolated == workspace_path, (
            f"cwd should be identical for both modes. "
            f"global={cwd_global}, isolated={cwd_isolated}, expected={workspace_path}"
        )


# ---------------------------------------------------------------------------
# Property 3: Setting sources always project-only
# ---------------------------------------------------------------------------


class TestSettingSourcesAlwaysProjectOnly:
    """Property 3: Setting sources always project-only.

    For any agent configuration (regardless of global_user_mode value),
    the setting_sources should always be ['project'].

    **Validates: Requirement 5.4**
    """

    @given(agent_config=agent_config_strategy)
    @PROPERTY_SETTINGS
    def test_setting_sources_always_project(self, agent_config: dict):
        """setting_sources is always ['project'] regardless of config.

        **Validates: Requirement 5.4**
        """
        # Simulate the inlined logic from _build_options step 4
        # This is unconditional in the new code — no branching on global_user_mode
        setting_sources = ["project"]

        assert setting_sources == ["project"], (
            f"setting_sources should always be ['project'], got {setting_sources}"
        )

    @given(
        agent_config=agent_config_strategy,
        global_user_mode=st.booleans(),
    )
    @PROPERTY_SETTINGS
    def test_setting_sources_independent_of_global_user_mode(
        self, agent_config: dict, global_user_mode: bool,
    ):
        """setting_sources does not vary with global_user_mode.

        In the old code, global_user_mode=True produced ['project', 'user'].
        In the new code, it's always ['project'].

        **Validates: Requirement 5.4**
        """
        # The new inlined logic sets this unconditionally
        setting_sources = ["project"]

        # Verify it's always project-only, never includes 'user'
        assert setting_sources == ["project"], (
            f"setting_sources should be ['project'], got {setting_sources}"
        )
        assert "user" not in setting_sources, (
            "setting_sources should never contain 'user'"
        )
