# #region TestRunRetry [C:3] [TYPE Module] [SEMANTICS test,translate,retry,integration]
# @BRIEF Tests for TranslationRunRetryManager — retry_failed_batches, cancel_run.
# @RELATION BINDS_TO -> [TranslationRunRetryManager]
import uuid
from unittest.mock import AsyncMock, MagicMock, PropertyMock, patch

import pytest

from src.models.translate import TranslationBatch, TranslationJob, TranslationRun, TranslationRecord
from src.plugins.translate.orchestrator_retry import TranslationRunRetryManager
from src.plugins.translate.executor import TranslationExecutor


# #region TestRetryFailedBatches [C:3] [TYPE Class]
class TestRetryFailedBatches:
    """Verify retry_failed_batches handles runs, batches, records correctly."""

    def _make_manager(self, db=None):
        if db is None:
            db = MagicMock()
        config = MagicMock()
        event_log = MagicMock()
        return TranslationRunRetryManager(db, config, event_log, "test_user")

    def _make_run(self, status="FAILED") -> TranslationRun:
        run = MagicMock(spec=TranslationRun)
        run.id = str(uuid.uuid4())
        run.job_id = str(uuid.uuid4())
        run.status = status
        run.successful_records = 0
        run.failed_records = 10
        run.skipped_records = 0
        return run

    def _make_job(self) -> TranslationJob:
        job = MagicMock(spec=TranslationJob)
        job.id = str(uuid.uuid4())
        return job

    def _make_batch(self, run_id: str, status="FAILED", idx=0) -> TranslationBatch:
        batch = MagicMock(spec=TranslationBatch)
        batch.id = str(uuid.uuid4())
        batch.run_id = run_id
        batch.batch_index = idx
        batch.status = status
        return batch

    async def test_retry_run_not_found(self):
        db = MagicMock()
        db.query.return_value.filter.return_value.first.return_value = None

        mgr = self._make_manager(db)
        with pytest.raises(ValueError, match="not found"):
            await mgr.retry_failed_batches("non-existent")

    async def test_retry_job_not_found(self):
        db = MagicMock()
        run = self._make_run()

        def query_side_effect(model):
            q = MagicMock()
            if model == TranslationRun:
                q.filter.return_value.first.return_value = run
            elif model == TranslationJob:
                q.filter.return_value.first.return_value = None
            else:
                q.filter.return_value.filter.return_value.all.return_value = []
            return q

        db.query.side_effect = query_side_effect
        mgr = self._make_manager(db)
        with pytest.raises(ValueError, match="not found"):
            await mgr.retry_failed_batches(run.id)

    async def test_retry_no_failed_batches(self):
        db = MagicMock()
        run = self._make_run()
        job = self._make_job()

        def query_side_effect(model):
            q = MagicMock()
            q.filter.return_value.filter.return_value.all.return_value = []
            q.filter.return_value.all.return_value = []
            if model == TranslationRun:
                q.filter.return_value.first.return_value = run
            elif model == TranslationJob:
                q.filter.return_value.first.return_value = job
            return q

        db.query.side_effect = query_side_effect
        mgr = self._make_manager(db)
        with pytest.raises(ValueError, match="No failed batches"):
            await mgr.retry_failed_batches(run.id)

    async def test_retry_job_not_found(self):
        db = MagicMock()
        run = self._make_run()

        def query_side_effect(model):
            q = MagicMock()
            if model == TranslationRun:
                q.filter.return_value.first.return_value = run
            else:
                q.filter.return_value.first.return_value = None
            return q

        db.query.side_effect = query_side_effect
        mgr = self._make_manager(db)
        with pytest.raises(ValueError, match="not found"):
            await mgr.retry_failed_batches(run.id)

    async def test_cancel_run(self):
        db = MagicMock()
        mgr = self._make_manager(db)
        run = self._make_run("RUNNING")

        with patch('src.plugins.translate.orchestrator_retry._cancel_run') as mock_cancel:
            mock_cancel.return_value = run
            result = mgr.cancel_run(run.id)
            assert result.status == "RUNNING"
            mock_cancel.assert_called_once()
    async def test_retry_insert(self):
        db = MagicMock()
        mgr = self._make_manager(db)
        run = self._make_run("COMPLETED")

        with patch('src.plugins.translate.orchestrator_retry._retry_insert', new_callable=AsyncMock) as mock_retry_insert:
            mock_retry_insert.return_value = run
            result = await mgr.retry_insert(run.id)
            assert result.status == "COMPLETED"
            mock_retry_insert.assert_called_once()
# #endregion TestRetryFailedBatches
