# #region AsyncSyncRegressionTests [C:3] [TYPE Module] [SEMANTICS test, regression, async, sync, migration]
# @BRIEF Regression tests to prevent async/sync mismatch after async migration.
# @RELATION BINDS_TO -> [TranslationOrchestrator]
# @RELATION BINDS_TO -> [TranslationExecutor]
# @RATIONALE
#   After async migration (commits ee9123b, 71000db), 20 tests failed because
#   async methods were called without await. This module prevents regression.
#
#   Root cause: Tests written as `def test_...` called `async def` methods,
#   returning coroutine objects instead of results. Tests passed locally but
#   failed at runtime with AttributeError: 'coroutine' object has no attribute 'status'.
#
# @REJECTED
#   Static analysis tool — adds complexity, not all teams use it.
#   Runtime check in production — too late, tests should catch this.

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

from src.models.translate import TranslationJob, TranslationRun
from src.plugins.translate.orchestrator import TranslationOrchestrator
from src.plugins.translate.executor import TranslationExecutor


# #region TestAsyncMethodAwaitRegression [C:3] [TYPE Class]
# @BRIEF Verify async methods are properly awaited in tests.
class TestAsyncMethodAwaitRegression:
    """Regression tests for async method await patterns."""

    # region test_execute_run_returns_result_not_coroutine [C:2] [TYPE Function]
    # @BRIEF Verify execute_run returns a result object, not a coroutine.
    async def test_execute_run_returns_result_not_coroutine(self) -> None:
        """Ensure execute_run is awaited and returns TranslationRun, not coroutine."""
        db = MagicMock()
        config_manager = MagicMock()

        job = MagicMock(spec=TranslationJob)
        job.id = "job-1"
        job.target_table = "target"
        job.target_languages = ["en"]

        run = MagicMock()
        run.id = "run-1"
        run.job_id = "job-1"
        run.status = "PENDING"

        completed_run = MagicMock()
        completed_run.id = "run-1"
        completed_run.status = "COMPLETED"
        completed_run.total_records = 0

        db.query.return_value.filter.return_value.first.side_effect = [job, completed_run]

        orch = TranslationOrchestrator(db, config_manager, "test-user")
        engine = orch._runner._executor_engine

        with patch("src.plugins.translate.orchestrator_exec.TranslationExecutor") as MockExec:
            mock_exec = MagicMock()
            mock_exec.execute_run = AsyncMock(return_value=completed_run)
            MockExec.return_value = mock_exec
            with patch.object(engine, "event_log"), \
                 patch.object(engine._aggregator, "update_language_stats"), \
                 patch.object(engine._sql_service, "generate_and_insert_sql", new_callable=AsyncMock, return_value={"status": "skipped"}):
                result = await orch.execute_run(run)

        # CRITICAL: result must be a TranslationRun object, not a coroutine
        assert not hasattr(result, '__await__'), (
            "execute_run returned a coroutine! Did you forget to await it?"
        )
        assert hasattr(result, 'status'), (
            f"Result must have 'status' attribute, got {type(result)}"
        )
        assert result.status == "COMPLETED"
    # endregion test_execute_run_returns_result_not_coroutine

    # region test_no_coroutine_never_awaited_warning [C:2] [TYPE Function]
    # @BRIEF Verify no RuntimeWarning about unawaited coroutines.
    async def test_no_coroutine_never_awaited_warning(self) -> None:
        """Ensure no RuntimeWarning about coroutines not being awaited."""
        db = MagicMock()
        config_manager = MagicMock()
        executor = TranslationExecutor(db, config_manager)

        job = MagicMock(spec=TranslationJob)
        job.id = "job-1"
        job.batch_size = 50
        job.target_languages = ["en"]

        run = MagicMock()
        run.id = "run-1"
        run.job_id = "job-1"
        run.status = "PENDING"

        db.query.return_value.filter.return_value.first.return_value = job
        executor._preview_edits_cache = {}

        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")

            with patch.object(executor, '_fetch_source_rows', new_callable=AsyncMock, return_value=[]):
                result = await executor.execute_run(run)

            # Check for RuntimeWarning about unawaited coroutines
            coroutine_warnings = [
                warning for warning in w
                if issubclass(warning.category, RuntimeWarning)
                and "coroutine" in str(warning.message).lower()
                and "never awaited" in str(warning.message).lower()
            ]

            assert len(coroutine_warnings) == 0, (
                f"Found {len(coroutine_warnings)} RuntimeWarning(s) about unawaited coroutines. "
                f"Did you forget to await an async method? Warnings: {coroutine_warnings}"
            )
    # endregion test_no_coroutine_never_awaited_warning

    # region test_async_mock_used_for_async_methods [C:2] [TYPE Function]
    # @BRIEF Verify AsyncMock is used for async methods, not MagicMock.
    def test_async_mock_used_for_async_methods(self) -> None:
        """Ensure AsyncMock is used when mocking async methods."""
        import inspect

        # Verify that execute_run is async
        assert inspect.iscoroutinefunction(TranslationOrchestrator.execute_run), (
            "TranslationOrchestrator.execute_run should be async"
        )
        assert inspect.iscoroutinefunction(TranslationExecutor.execute_run), (
            "TranslationExecutor.execute_run should be async"
        )

        # When mocking these methods, AsyncMock must be used
        # This test documents the requirement
        mock_executor = MagicMock()

        # WRONG: mock_executor.execute_run.return_value = ...
        # RIGHT: mock_executor.execute_run = AsyncMock(return_value=...)

        # Verify AsyncMock can be awaited
        async_mock = AsyncMock(return_value="result")
        assert inspect.iscoroutinefunction(async_mock), (
            "AsyncMock should be a coroutine function"
        )
    # endregion test_async_mock_used_for_async_methods

# #endregion TestAsyncMethodAwaitRegression


# #region TestAsyncHandlerClassification [C:2] [TYPE Class]
# @BRIEF Verify route handlers have correct async/sync classification.
class TestAsyncHandlerClassification:
    """Verify route handlers are correctly classified as async or sync."""

    # region test_async_handlers_call_async_methods [C:2] [TYPE Function]
    # @BRIEF Verify async handlers call async orchestrator methods.
    def test_async_handlers_call_async_methods(self) -> None:
        """Async handlers should call async methods like execute_run."""
        import inspect
        from src.api.routes.translate._run_routes import router

        # These handlers should be async because they call async orchestrator methods
        async_handlers = {"run_translation", "retry_run", "retry_insert"}

        for route in router.routes:
            if hasattr(route, "endpoint"):
                name = route.endpoint.__name__
                if name in async_handlers:
                    assert inspect.iscoroutinefunction(route.endpoint), (
                        f"Handler '{name}' should be async (calls async orchestrator methods)"
                    )
    # endregion test_async_handlers_call_async_methods

    # region test_sync_handlers_dont_call_async_methods [C:2] [TYPE Function]
    # @BRIEF Verify sync handlers don't call async orchestrator methods.
    def test_sync_handlers_dont_call_async_methods(self) -> None:
        """Sync handlers should not call async orchestrator methods."""
        import inspect
        from src.api.routes.translate._run_routes import router

        # These handlers should be sync (they don't call async orchestrator methods)
        sync_handlers = {
            "cancel_run", "get_run_history", "get_run_status",
            "get_run_records", "get_batches",
        }

        for route in router.routes:
            if hasattr(route, "endpoint"):
                name = route.endpoint.__name__
                if name in sync_handlers:
                    assert not inspect.iscoroutinefunction(route.endpoint), (
                        f"Handler '{name}' should be sync (doesn't call async orchestrator methods)"
                    )
    # endregion test_sync_handlers_dont_call_async_methods

# #endregion TestAsyncHandlerClassification

# #endregion AsyncSyncRegressionTests
