# #region Test.Orchestrator [C:3] [TYPE Module] [SEMANTICS test, translate, orchestrator, lifecycle]
# @BRIEF Tests for orchestrator.py — TranslationOrchestrator delegates to sub-components.
# @RELATION BINDS_TO -> [TranslationOrchestrator]
# @TEST_CONTRACT: start_run -> TranslationRun | delegates to TranslationPlanner
# @TEST_CONTRACT: execute_run -> TranslationRun | delegates to TranslationStageRunner
# @TEST_CONTRACT: retry_failed_batches -> TranslationRun | delegates to TranslationStageRunner
# @TEST_CONTRACT: retry_insert -> TranslationRun | delegates to TranslationStageRunner
# @TEST_CONTRACT: cancel_run -> TranslationRun | delegates to TranslationStageRunner
# @TEST_CONTRACT: get_run_status -> dict | delegates to TranslationResultAggregator
# @TEST_CONTRACT: get_run_records -> dict | delegates to TranslationResultAggregator
# @TEST_CONTRACT: get_run_history -> tuple | delegates to TranslationResultAggregator
# @TEST_EDGE: sql_insert_service_backward_compat
# @TEST_EDGE: language_stats_updates

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

import os
os.environ.setdefault("SECRET_KEY", "test-secret-key-for-testing")
os.environ.setdefault("DATABASE_URL", "sqlite:///:memory:")
os.environ.setdefault("AUTH_DATABASE_URL", "sqlite:///:memory:")

from unittest.mock import MagicMock, AsyncMock, patch

import pytest

from src.plugins.translate.orchestrator import TranslationOrchestrator


class TestTranslationOrchestrator:
    """TranslationOrchestrator — delegates all operations to sub-components."""

    def _make_orchestrator(self):
        """Create orchestrator with mocked sub-components."""
        db = MagicMock()
        config = MagicMock()

        with patch(
            "src.plugins.translate.orchestrator.TranslationPlanner"
        ) as MockPlanner, patch(
            "src.plugins.translate.orchestrator.TranslationStageRunner"
        ) as MockRunner, patch(
            "src.plugins.translate.orchestrator.TranslationResultAggregator"
        ) as MockAggregator:
            orch = TranslationOrchestrator(db, config, "user")
            return orch, db, config, MockPlanner, MockRunner, MockAggregator

    def test_start_run_delegates(self):
        """start_run delegates to TranslationPlanner."""
        orch, _, _, MockPlanner, _, _ = self._make_orchestrator()
        mock_run = MagicMock()
        orch._planner = MockPlanner.return_value
        orch._planner.plan_run.return_value = mock_run

        result = orch.start_run("job-1", is_scheduled=True, trigger_type="cron", full_translation=True)

        orch._planner.plan_run.assert_called_once_with(
            job_id="job-1", is_scheduled=True, trigger_type="cron", full_translation=True,
        )
        assert result == mock_run

    def test_start_run_minimal(self):
        """start_run with only job_id uses defaults."""
        orch, _, _, MockPlanner, _, _ = self._make_orchestrator()
        mock_run = MagicMock()
        orch._planner = MockPlanner.return_value
        orch._planner.plan_run.return_value = mock_run

        result = orch.start_run("job-1")

        orch._planner.plan_run.assert_called_once_with(
            job_id="job-1", is_scheduled=False, trigger_type=None, full_translation=False,
        )

    @pytest.mark.asyncio
    async def test_execute_run_delegates(self):
        """execute_run delegates to TranslationStageRunner."""
        orch, _, _, _, MockRunner, _ = self._make_orchestrator()
        mock_run = MagicMock()
        orch._runner = MockRunner.return_value
        orch._runner.execute_run = AsyncMock(return_value=mock_run)

        progress = MagicMock()
        result = await orch.execute_run(mock_run, on_batch_progress=progress, skip_insert=True)

        orch._runner.execute_run.assert_called_once_with(
            run=mock_run, on_batch_progress=progress, skip_insert=True,
        )
        assert result == mock_run

    @pytest.mark.asyncio
    async def test_retry_failed_batches_delegates(self):
        """retry_failed_batches delegates to TranslationStageRunner."""
        orch, _, _, _, MockRunner, _ = self._make_orchestrator()
        mock_run = MagicMock()
        orch._runner = MockRunner.return_value
        orch._runner.retry_failed_batches = AsyncMock(return_value=mock_run)

        result = await orch.retry_failed_batches("run-1")

        orch._runner.retry_failed_batches.assert_called_once_with("run-1")

    @pytest.mark.asyncio
    async def test_retry_insert_delegates(self):
        """retry_insert delegates to TranslationStageRunner."""
        orch, _, _, _, MockRunner, _ = self._make_orchestrator()
        mock_run = MagicMock()
        orch._runner = MockRunner.return_value
        orch._runner.retry_insert = AsyncMock(return_value=mock_run)

        result = await orch.retry_insert("run-1")

        orch._runner.retry_insert.assert_called_once_with("run-1")

    def test_cancel_run_delegates(self):
        """cancel_run delegates to TranslationStageRunner."""
        orch, _, _, _, MockRunner, _ = self._make_orchestrator()
        mock_run = MagicMock()
        orch._runner = MockRunner.return_value
        orch._runner.cancel_run.return_value = mock_run

        result = orch.cancel_run("run-1")

        orch._runner.cancel_run.assert_called_once_with("run-1")

    def test_get_run_status_delegates(self):
        """get_run_status delegates to TranslationResultAggregator."""
        orch, _, _, _, _, MockAggregator = self._make_orchestrator()
        orch._aggregator = MockAggregator.return_value
        orch._aggregator.get_run_status.return_value = {"status": "COMPLETED"}

        result = orch.get_run_status("run-1")

        orch._aggregator.get_run_status.assert_called_once_with("run-1")

    def test_get_run_records_delegates(self):
        """get_run_records delegates to TranslationResultAggregator."""
        orch, _, _, _, _, MockAggregator = self._make_orchestrator()
        orch._aggregator = MockAggregator.return_value
        orch._aggregator.get_run_records.return_value = {"items": [], "total": 0}

        result = orch.get_run_records("run-1", page=2, page_size=10, status_filter="failed",
                                      deduplicate=True)

        orch._aggregator.get_run_records.assert_called_once_with(
            "run-1", 2, 10, "failed", deduplicate=True,
        )

    def test_get_run_history_delegates(self):
        """get_run_history delegates to TranslationResultAggregator."""
        orch, _, _, _, _, MockAggregator = self._make_orchestrator()
        orch._aggregator = MockAggregator.return_value
        orch._aggregator.get_run_history.return_value = (5, [{"id": "r1"}])

        total, items = orch.get_run_history("job-1", page=1, page_size=20)

        orch._aggregator.get_run_history.assert_called_once_with("job-1", 1, 20)
        assert total == 5
        assert items == [{"id": "r1"}]

    def test_generate_and_insert_sql_backward_compat(self):
        """_generate_and_insert_sql creates SQLInsertService and delegates."""
        orch, db, config, _, _, _ = self._make_orchestrator()
        mock_job = MagicMock()
        mock_run = MagicMock()

        # SQLInsertService is imported INSIDE the method from .orchestrator_sql
        with patch(
            "src.plugins.translate.orchestrator_sql.SQLInsertService"
        ) as MockSQL:
            mock_svc = MockSQL.return_value
            mock_svc.generate_and_insert_sql = AsyncMock(return_value={"status": "success"})

            import asyncio
            result = asyncio.run(orch._generate_and_insert_sql(mock_job, mock_run))

        MockSQL.assert_called_once_with(db, config, orch.event_log)
        mock_svc.generate_and_insert_sql.assert_called_once_with(mock_job, mock_run)

    def test_update_language_stats_delegates(self):
        """_update_language_stats delegates to TranslationResultAggregator."""
        orch, _, _, _, _, MockAggregator = self._make_orchestrator()
        orch._aggregator = MockAggregator.return_value
        stats_map = {"ru": MagicMock()}

        orch._update_language_stats("run-1", stats_map)

        orch._aggregator.update_language_stats.assert_called_once_with("run-1", stats_map)

    def test_initializes_event_log_and_components(self):
        """Constructor initializes all sub-components."""
        db = MagicMock()
        config = MagicMock()

        with patch(
            "src.plugins.translate.orchestrator.TranslationPlanner"
        ) as MockPlanner, patch(
            "src.plugins.translate.orchestrator.TranslationStageRunner"
        ) as MockRunner, patch(
            "src.plugins.translate.orchestrator.TranslationResultAggregator"
        ) as MockAggregator:
            orch = TranslationOrchestrator(db, config, "user")

        assert orch.db == db
        assert orch.config_manager == config
        assert orch.current_user == "user"
        assert orch.event_log is not None
        MockPlanner.assert_called_once_with(db, orch.event_log, "user")
        MockRunner.assert_called_once_with(db, config, orch.event_log, "user")
        MockAggregator.assert_called_once_with(db, orch.event_log)
# #endregion Test.Orchestrator
