# #region Test.DatasetReview.OrchestratorEdge [C:2] [TYPE Module] [SEMANTICS test,dataset,orchestrator,edge,recovery,preview]
# @BRIEF Edge-case coverage for orchestrator.py uncovered paths.
# @RELATION BINDS_TO -> [DatasetReviewOrchestrator]

from pathlib import Path
import sys

sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))

from unittest.mock import MagicMock, AsyncMock, patch
import pytest


@pytest.fixture
def mock_repository():
    repo = MagicMock()
    repo.db = MagicMock()
    repo.event_logger = MagicMock()
    repo.bump_session_version = MagicMock(return_value=2)
    return repo


@pytest.fixture
def mock_config_manager():
    cm = MagicMock()
    env = MagicMock()
    env.id = "env-1"
    env.name = "Test Environment"
    cm.get_environment.return_value = env
    return cm


@pytest.fixture
def mock_user():
    user = MagicMock()
    user.id = "user-1"
    return user


@pytest.fixture
def orchestrator(mock_repository, mock_config_manager):
    from src.services.dataset_review.orchestrator import DatasetReviewOrchestrator
    return DatasetReviewOrchestrator(
        repository=mock_repository,
        config_manager=mock_config_manager,
    )


class TestStartSessionEdge:
    """Edge cases for start_session."""

    @pytest.mark.asyncio
    async def test_start_session_superset_link_no_partial_recovery(
        self, orchestrator, mock_repository, mock_config_manager, mock_user
    ):
        """Superset link without partial_recovery (line 170)."""
        from src.services.dataset_review.orchestrator_pkg._commands import StartSessionCommand

        parsed_context = MagicMock()
        parsed_context.partial_recovery = False
        parsed_context.dataset_ref = "schema.table"
        parsed_context.dataset_id = 42
        parsed_context.dashboard_id = 99
        parsed_context.dataset_payload = {"id": 42}

        command = StartSessionCommand(
            source_kind="superset_link",
            source_input="http://superset.example.com/dashboard/99",
            environment_id="env-1",
            user=mock_user,
        )

        persisted_session = MagicMock()
        persisted_session.session_id = "sess-3"
        persisted_session.current_phase = MagicMock(value="review")
        persisted_session.readiness_state = MagicMock(value="review_ready")
        persisted_session.source_kind = "superset_link"
        persisted_session.dataset_ref = "schema.table"
        persisted_session.dataset_id = 42
        persisted_session.dashboard_id = 99
        persisted_session.active_task_id = None
        persisted_session.user_id = "user-1"
        persisted_session.environment_id = "env-1"
        persisted_session.source_input = "test"
        mock_repository.create_session.return_value = persisted_session
        mock_repository.save_profile_and_findings.return_value = persisted_session

        with patch("src.services.dataset_review.orchestrator.SupersetContextExtractor") as mock_extractor_cls, \
             patch("src.services.dataset_review.orchestrator.build_initial_profile") as mock_build_profile, \
             patch("src.services.dataset_review.orchestrator.build_partial_recovery_findings"):

            mock_extractor = MagicMock()
            mock_extractor.parse_superset_link = AsyncMock(return_value=parsed_context)
            mock_extractor.recover_imported_filters.return_value = []
            mock_extractor.discover_template_variables.return_value = []
            mock_extractor_cls.return_value = mock_extractor

            mock_profile = MagicMock()
            mock_build_profile.return_value = mock_profile
            mock_repository.save_recovery_state.return_value = persisted_session

            result = await orchestrator.start_session(command)
            assert result is not None
            assert result.session.session_id == "sess-3"

    @pytest.mark.asyncio
    async def test_start_session_with_recovery_state(
        self, orchestrator, mock_repository, mock_user
    ):
        """start_session with recovered filters saves recovery state (line 233)."""
        from src.services.dataset_review.orchestrator_pkg._commands import StartSessionCommand

        command = StartSessionCommand(
            source_kind="dataset_selection",
            source_input="42",
            environment_id="env-1",
            user=mock_user,
        )

        persisted_session = MagicMock()
        persisted_session.session_id = "sess-4"
        persisted_session.current_phase = MagicMock(value="review")
        persisted_session.readiness_state = MagicMock(value="review_ready")
        persisted_session.source_kind = "dataset_selection"
        persisted_session.dataset_ref = "dataset:42"
        persisted_session.dataset_id = 42
        persisted_session.dashboard_id = None
        persisted_session.active_task_id = None
        persisted_session.user_id = "user-1"
        persisted_session.environment_id = "env-1"
        persisted_session.source_input = "42"
        mock_repository.create_session.return_value = persisted_session
        mock_repository.save_profile_and_findings.return_value = persisted_session
        mock_repository.save_recovery_state.return_value = persisted_session

        with patch("src.services.dataset_review.orchestrator.build_initial_profile") as mock_build_profile, \
             patch("src.services.dataset_review.orchestrator.parse_dataset_selection",
                   return_value=("dataset:42", 42)):
            mock_profile = MagicMock()
            mock_build_profile.return_value = mock_profile

            result = await orchestrator.start_session(command)
            assert result is not None


class TestPrepareLaunchPreviewEdge:
    """Edge cases for prepare_launch_preview."""

    def test_prepare_preview_blocked_by_execution(
        self, orchestrator, mock_repository
    ):
        """Preview blocked by execution snapshot blockers (lines 300-301)."""
        from src.services.dataset_review.orchestrator_pkg._commands import PreparePreviewCommand

        session = MagicMock()
        session.session_id = "sess-1"
        session.user_id = "user-1"
        session.dataset_id = 42
        session.environment_id = "env-1"
        session.current_phase = MagicMock(value="review")
        session.last_activity_at = None
        session.version = 1
        mock_repository.load_session_detail.return_value = session

        with patch("src.services.dataset_review.orchestrator.build_execution_snapshot") as mock_build:
            mock_build.return_value = {
                "preview_blockers": ["missing_filter"],
                "preview_fingerprint": "",
                "template_params": {},
                "effective_filters": [],
            }

            command = PreparePreviewCommand(
                session_id="sess-1",
                expected_version=1,
                user=MagicMock(id="user-1"),
            )

            with pytest.raises(ValueError, match="Preview blocked"):
                orchestrator.prepare_launch_preview(command)

    def test_prepare_preview_partially_ready(
        self, orchestrator, mock_repository, mock_config_manager
    ):
        """Preview with non-READY status sets PARTIALLY_READY (lines 331-332)."""
        from src.services.dataset_review.orchestrator_pkg._commands import PreparePreviewCommand
        from src.services.dataset_review.orchestrator_pkg._helpers import PreviewStatus

        session = MagicMock()
        session.session_id = "sess-2"
        session.user_id = "user-1"
        session.dataset_id = 42
        session.environment_id = "env-1"
        session.current_phase = MagicMock(value="review")
        session.last_activity_at = None
        session.version = 1
        mock_repository.load_session_detail.return_value = session

        with patch("src.services.dataset_review.orchestrator.build_execution_snapshot") as mock_build_snapshot, \
             patch("src.services.dataset_review.orchestrator.SupersetCompilationAdapter") as mock_adapter_cls, \
             patch("src.services.dataset_review.orchestrator.build_launch_blockers") as mock_build_blockers:

            mock_build_snapshot.return_value = {
                "preview_blockers": [],
                "preview_fingerprint": "fp-1",
                "template_params": {"region": "US"},
                "effective_filters": [],
            }

            mock_adapter = MagicMock()
            mock_adapter_cls.return_value = mock_adapter
            preview = MagicMock()
            preview.preview_id = "pv-1"
            preview.preview_status = PreviewStatus.PENDING
            preview.preview_fingerprint = "fp-1"
            mock_adapter.compile_preview.return_value = preview

            persisted_preview = MagicMock()
            persisted_preview.preview_id = "pv-1"
            persisted_preview.preview_status = PreviewStatus.PENDING
            mock_repository.save_preview.return_value = persisted_preview

            mock_build_blockers.return_value = []

            command = PreparePreviewCommand(
                session_id="sess-2",
                expected_version=1,
                user=MagicMock(id="user-1"),
            )

            result = orchestrator.prepare_launch_preview(command)
            assert result is not None
            # Check session readiness state was updated to PARTIALLY_READY
            assert session.readiness_state.value == "partially_ready"

    def test_prepare_preview_no_dataset_id(
        self, orchestrator, mock_repository
    ):
        """Missing dataset_id raises ValueError (line 371)."""
        from src.services.dataset_review.orchestrator_pkg._commands import PreparePreviewCommand

        session = MagicMock()
        session.session_id = "sess-1"
        session.user_id = "user-1"
        session.dataset_id = None
        session.environment_id = "env-1"
        mock_repository.load_session_detail.return_value = session

        command = PreparePreviewCommand(
            session_id="sess-1",
            expected_version=1,
            user=MagicMock(id="user-1"),
        )

        with pytest.raises(ValueError, match="requires a resolved dataset_id"):
            orchestrator.prepare_launch_preview(command)

    def test_prepare_preview_env_not_found(
        self, orchestrator, mock_repository, mock_config_manager
    ):
        """Missing environment raises ValueError (line 375)."""
        from src.services.dataset_review.orchestrator_pkg._commands import PreparePreviewCommand

        mock_config_manager.get_environment.return_value = None

        session = MagicMock()
        session.session_id = "sess-1"
        session.user_id = "user-1"
        session.dataset_id = 42
        session.environment_id = "nonexistent"
        session.current_phase = MagicMock(value="review")
        mock_repository.load_session_detail.return_value = session

        command = PreparePreviewCommand(
            session_id="sess-1",
            expected_version=1,
            user=MagicMock(id="user-1"),
        )

        with pytest.raises(ValueError, match="Environment not found"):
            orchestrator.prepare_launch_preview(command)

    def test_prepare_preview_compiled_ready_with_blockers(
        self, orchestrator, mock_repository, mock_config_manager
    ):
        """Preview READY with launch blockers sets COMPILED_PREVIEW_READY (lines 325-326)."""
        from src.services.dataset_review.orchestrator_pkg._commands import PreparePreviewCommand
        from src.services.dataset_review.orchestrator_pkg._helpers import PreviewStatus

        session = MagicMock()
        session.session_id = "sess-3"
        session.user_id = "user-1"
        session.dataset_id = 42
        session.environment_id = "env-1"
        session.current_phase = MagicMock(value="review")
        session.last_activity_at = None
        session.version = 1
        mock_repository.load_session_detail.return_value = session

        with patch("src.services.dataset_review.orchestrator.build_execution_snapshot") as mock_build_snapshot, \
             patch("src.services.dataset_review.orchestrator.SupersetCompilationAdapter") as mock_adapter_cls, \
             patch("src.services.dataset_review.orchestrator.build_launch_blockers") as mock_build_blockers:

            mock_build_snapshot.return_value = {
                "preview_blockers": [],
                "preview_fingerprint": "fp-1",
                "template_params": {"region": "US"},
                "effective_filters": [],
            }

            mock_adapter = MagicMock()
            mock_adapter_cls.return_value = mock_adapter
            preview = MagicMock()
            preview.preview_status = PreviewStatus.READY
            mock_adapter.compile_preview.return_value = preview

            persisted_preview = MagicMock()
            persisted_preview.preview_status = PreviewStatus.READY
            mock_repository.save_preview.return_value = persisted_preview

            mock_build_blockers.return_value = ["finding blocking"]

            command = PreparePreviewCommand(
                session_id="sess-3",
                expected_version=1,
                user=MagicMock(id="user-1"),
            )

            result = orchestrator.prepare_launch_preview(command)
            assert result is not None


class TestBuildRecoveryBootstrapEdge:
    """Edge cases for _build_recovery_bootstrap."""

    @pytest.mark.asyncio
    async def test_recovery_without_imported_filters_payload(
        self, orchestrator, mock_config_manager
    ):
        """recover_imported_filters returns None (line 467)."""
        session = MagicMock()
        session.session_id = "sess-1"
        session.dataset_id = 42

        parsed_context = MagicMock()
        parsed_context.dataset_payload = {"id": 42}
        parsed_context.unresolved_references = []

        with patch("src.services.dataset_review.orchestrator.SupersetContextExtractor") as mock_extractor_cls:
            mock_extractor = MagicMock()
            mock_extractor.recover_imported_filters.return_value = None
            mock_extractor.discover_template_variables.return_value = []
            mock_extractor_cls.return_value = mock_extractor

            filters, vars_, mappings, findings = (
                await orchestrator._build_recovery_bootstrap(
                    MagicMock(), session, parsed_context, []
                )
            )
            assert filters == []
            assert vars_ == []

    @pytest.mark.asyncio
    async def test_recovery_skip_unmatched_variable(
        self, orchestrator, mock_config_manager
    ):
        """Template variable with no matching filter is skipped (line 527)."""
        session = MagicMock()
        session.session_id = "sess-1"
        session.dataset_id = 42

        parsed_context = MagicMock()
        parsed_context.dataset_payload = {"id": 42}
        parsed_context.unresolved_references = []

        with patch("src.services.dataset_review.orchestrator.SupersetContextExtractor") as mock_extractor_cls:
            mock_extractor = MagicMock()
            mock_extractor.recover_imported_filters.return_value = [
                {"filter_name": "region"}
            ]
            mock_extractor.discover_template_variables.return_value = [
                {"variable_name": "unmatched_var"}  # no matching filter
            ]
            mock_extractor_cls.return_value = mock_extractor

            filters, vars_, mappings, findings = (
                await orchestrator._build_recovery_bootstrap(
                    MagicMock(), session, parsed_context, []
                )
            )
            assert len(mappings) == 0  # No mapping created for unmatched variable


class TestLaunchDatasetEdge:
    """Edge cases for launch_dataset."""

    def test_launch_no_dataset_id(
        self, orchestrator, mock_repository
    ):
        """Missing dataset_id in launch (some uncovered branch)."""
        from src.services.dataset_review.orchestrator_pkg._commands import LaunchDatasetCommand

        session = MagicMock()
        session.session_id = "sess-1"
        session.user_id = "user-1"
        session.dataset_id = None
        session.environment_id = "env-1"
        session.current_phase = MagicMock(value="preview")
        mock_repository.load_session_detail.return_value = session

        command = LaunchDatasetCommand(
            session_id="sess-1",
            expected_version=1,
            user=MagicMock(id="user-1"),
        )

        with pytest.raises(ValueError, match="dataset_id"):
            orchestrator.launch_dataset(command)

    def test_launch_env_not_found(
        self, orchestrator, mock_repository, mock_config_manager
    ):
        """Missing environment in launch."""
        from src.services.dataset_review.orchestrator_pkg._commands import LaunchDatasetCommand

        mock_config_manager.get_environment.return_value = None

        session = MagicMock()
        session.session_id = "sess-1"
        session.user_id = "user-1"
        session.dataset_id = 42
        session.environment_id = "nonexistent"
        session.current_phase = MagicMock(value="preview")
        mock_repository.load_session_detail.return_value = session

        command = LaunchDatasetCommand(
            session_id="sess-1",
            expected_version=1,
            user=MagicMock(id="user-1"),
        )

        with pytest.raises(ValueError, match="Environment not found"):
            orchestrator.launch_dataset(command)
# #endregion Test.DatasetReview.OrchestratorEdge
