# #region TestTranslateSchedulerExecution [C:3] [TYPE Module] [SEMANTICS test, translate, scheduler, execution, trigger-type, baseline]
# @BRIEF Verify execute_scheduled_translation contracts — trigger_type dispatch, baseline expiry fallback.
# @RELATION BINDS_TO -> [execute_scheduled_translation]
# @TEST_EDGE: new_key_only_mode — trigger_type="new_key_only" when baseline not expired
# @TEST_EDGE: baseline_expired_fallback — trigger_type="scheduled" when baseline > 90 days old
# @TEST_EDGE: full_mode_default — trigger_type="scheduled" for execution_mode="full"
# @TEST_EDGE: inactive_schedule — function returns early without executing
# @TEST_EDGE: execution_error_handled — run marked FAILED, schedule tracking updated
# @TEST_INVARIANT: trigger_type_dispatch -> VERIFIED_BY: [test_new_key_only_mode, test_baseline_expired_fallback, test_full_mode_default]
from pathlib import Path
import sys

sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
from datetime import UTC, datetime, timedelta
from unittest.mock import MagicMock, patch

# -- Helpers -----------------------------------------------------------------

def _make_most_recent_run(created_at_delta_days=60):
    """Build a MagicMock TranslationRun with given age."""
    run = MagicMock()
    run.id = "prev-run-1"
    run.job_id = "job-1"
    run.status = "COMPLETED"
    run.insert_status = "succeeded"
    run.created_at = datetime.now(UTC) - timedelta(days=created_at_delta_days)
    return run


def _make_mock_run(run_id="run-1", job_id="job-1"):
    """Build a mock TranslationRun returned by start_run."""
    run = MagicMock()
    run.id = run_id
    run.job_id = job_id
    run.status = "PENDING"
    run.trigger_type = None
    run.total_records = 0
    run.successful_records = 0
    run.failed_records = 0
    run.skipped_records = 0
    run.insert_status = None
    run.superset_execution_id = None
    run.superset_execution_log = None
    run.error_message = None
    return run


# -- Tests: trigger_type dispatch and execution modes -----------------------

# #region test_new_key_only_mode [C:1] [TYPE Function]
def test_new_key_only_mode():
    """execution_mode="new_key_only" with 60-day-old prev run → trigger_type="new_key_only"."""
    from src.plugins.translate.scheduler import execute_scheduled_translation

    mock_db = MagicMock()
    mock_session_maker = MagicMock(return_value=mock_db)
    mock_config = MagicMock()

    schedule = MagicMock()
    schedule.id = "sched-1"
    schedule.job_id = "job-1"
    schedule.is_active = True
    schedule.execution_mode = "new_key_only"

    most_recent = _make_most_recent_run(created_at_delta_days=60)  # 60 days < 90

    q1 = MagicMock()
    q1.filter.return_value.first.return_value = schedule

    q2 = MagicMock()
    q2.filter.return_value.order_by.return_value.first.return_value = None  # no concurrent

    q3 = MagicMock()
    q3.filter.return_value.order_by.return_value.first.return_value = most_recent

    mock_db.query.side_effect = [q1, q2, q3]

    mock_run = _make_mock_run()

    with patch(
        "src.plugins.translate.orchestrator.TranslationOrchestrator"
    ) as mock_orch_cls:
        mock_orch = MagicMock()
        mock_orch.start_run.return_value = mock_run
        mock_orch_cls.return_value = mock_orch

        execute_scheduled_translation(
                schedule_id="sched-1",
                job_id="job-1",
                db_session_maker=mock_session_maker,
                config_manager=mock_config,
                execution_mode="new_key_only",
            )

    assert mock_run.trigger_type == "new_key_only", (
        f"Expected trigger_type='new_key_only', got '{mock_run.trigger_type}'"
    )
    mock_orch.start_run.assert_called_once_with(job_id="job-1", is_scheduled=True)
    mock_orch.execute_run.assert_called_once()
    mock_db.close.assert_called_once()
# #endregion test_new_key_only_mode


# #region test_baseline_expired_fallback [C:1] [TYPE Function]
def test_baseline_expired_fallback():
    """execution_mode="new_key_only" with >90-day-old prev run → trigger_type="scheduled" baseline fallback."""
    from src.plugins.translate.scheduler import execute_scheduled_translation

    mock_db = MagicMock()
    mock_session_maker = MagicMock(return_value=mock_db)
    mock_config = MagicMock()

    schedule = MagicMock()
    schedule.id = "sched-1"
    schedule.job_id = "job-1"
    schedule.is_active = True
    schedule.execution_mode = "new_key_only"

    most_recent = _make_most_recent_run(created_at_delta_days=95)  # 95 days > 90

    q1 = MagicMock()
    q1.filter.return_value.first.return_value = schedule

    q2 = MagicMock()
    q2.filter.return_value.order_by.return_value.first.return_value = None

    q3 = MagicMock()
    q3.filter.return_value.order_by.return_value.first.return_value = most_recent

    mock_db.query.side_effect = [q1, q2, q3]

    mock_run = _make_mock_run()

    with patch(
        "src.plugins.translate.orchestrator.TranslationOrchestrator"
    ) as mock_orch_cls:
        mock_orch = MagicMock()
        mock_orch.start_run.return_value = mock_run
        mock_orch_cls.return_value = mock_orch

        with patch(
            "src.plugins.translate.scheduler.TranslationEventLog"
        ) as mock_event_log_cls:
            mock_event_log = MagicMock()
            mock_event_log_cls.return_value = mock_event_log

            execute_scheduled_translation(
                schedule_id="sched-1",
                job_id="job-1",
                db_session_maker=mock_session_maker,
                config_manager=mock_config,
                execution_mode="new_key_only",
            )

    assert mock_run.trigger_type == "scheduled", (
        f"Expected trigger_type='scheduled' (baseline expired), got '{mock_run.trigger_type}'"
    )

    mock_event_log.log_event.assert_called_once()
    event_call = mock_event_log.log_event.call_args
    assert event_call.kwargs["job_id"] == "job-1"
    assert event_call.kwargs["event_type"] == "RUN_STARTED"
    assert event_call.kwargs["payload"]["reason"] == "baseline_expired"
    assert event_call.kwargs["payload"]["age_days"] == 95

    mock_db.close.assert_called_once()
# #endregion test_baseline_expired_fallback


# #region test_full_mode_default [C:1] [TYPE Function]
def test_full_mode_default():
    """execution_mode="full" → trigger_type="scheduled" regardless of baseline age."""
    from src.plugins.translate.scheduler import execute_scheduled_translation

    mock_db = MagicMock()
    mock_session_maker = MagicMock(return_value=mock_db)
    mock_config = MagicMock()

    schedule = MagicMock()
    schedule.id = "sched-1"
    schedule.job_id = "job-1"
    schedule.is_active = True
    schedule.execution_mode = "full"

    most_recent = _make_most_recent_run(created_at_delta_days=30)

    q1 = MagicMock()
    q1.filter.return_value.first.return_value = schedule

    q2 = MagicMock()
    q2.filter.return_value.order_by.return_value.first.return_value = None

    q3 = MagicMock()
    q3.filter.return_value.order_by.return_value.first.return_value = most_recent

    mock_db.query.side_effect = [q1, q2, q3]

    mock_run = _make_mock_run()

    with patch(
        "src.plugins.translate.orchestrator.TranslationOrchestrator"
    ) as mock_orch_cls:
        mock_orch = MagicMock()
        mock_orch.start_run.return_value = mock_run
        mock_orch_cls.return_value = mock_orch

        execute_scheduled_translation(
            schedule_id="sched-1",
            job_id="job-1",
            db_session_maker=mock_session_maker,
            config_manager=mock_config,
            execution_mode="full",
        )

    assert mock_run.trigger_type == "scheduled", (
        f"Expected trigger_type='scheduled' for full mode, got '{mock_run.trigger_type}'"
    )
    mock_orch.execute_run.assert_called_once()
    mock_db.close.assert_called_once()
# #endregion test_full_mode_default


# #region test_inactive_schedule_skips [C:1] [TYPE Function]
def test_inactive_schedule_skips():
    """Inactive schedule → function returns early without executing anything."""
    from src.plugins.translate.scheduler import execute_scheduled_translation

    mock_db = MagicMock()
    mock_session_maker = MagicMock(return_value=mock_db)
    mock_config = MagicMock()

    schedule = MagicMock()
    schedule.id = "sched-1"
    schedule.job_id = "job-1"
    schedule.is_active = False

    q1 = MagicMock()
    q1.filter.return_value.first.return_value = schedule

    mock_db.query.return_value = q1

    with patch(
        "src.plugins.translate.orchestrator.TranslationOrchestrator"
    ) as mock_orch_cls:
        execute_scheduled_translation(
            schedule_id="sched-1",
            job_id="job-1",
            db_session_maker=mock_session_maker,
            config_manager=mock_config,
            execution_mode="full",
        )

    mock_orch_cls.assert_not_called()
    mock_db.close.assert_called_once()
# #endregion test_inactive_schedule_skips


# #region test_schedule_not_found_skips [C:1] [TYPE Function]
def test_schedule_not_found_skips():
    """Schedule not found → function returns early."""
    from src.plugins.translate.scheduler import execute_scheduled_translation

    mock_db = MagicMock()
    mock_session_maker = MagicMock(return_value=mock_db)
    mock_config = MagicMock()

    q1 = MagicMock()
    q1.filter.return_value.first.return_value = None

    mock_db.query.return_value = q1

    with patch(
        "src.plugins.translate.orchestrator.TranslationOrchestrator"
    ) as mock_orch_cls:
        execute_scheduled_translation(
            schedule_id="sched-1",
            job_id="job-1",
            db_session_maker=mock_session_maker,
            config_manager=mock_config,
            execution_mode="full",
        )

    mock_orch_cls.assert_not_called()
    mock_db.close.assert_called_once()
# #endregion test_schedule_not_found_skips


# #region test_baseline_expired_full_mode [C:1] [TYPE Function]
def test_baseline_expired_full_mode():
    """execution_mode="full" with expired baseline → trigger_type stays 'scheduled'."""
    from src.plugins.translate.scheduler import execute_scheduled_translation

    mock_db = MagicMock()
    mock_session_maker = MagicMock(return_value=mock_db)
    mock_config = MagicMock()

    schedule = MagicMock()
    schedule.id = "sched-1"
    schedule.job_id = "job-1"
    schedule.is_active = True
    schedule.execution_mode = "full"

    most_recent = _make_most_recent_run(created_at_delta_days=100)  # expired

    q1 = MagicMock()
    q1.filter.return_value.first.return_value = schedule

    q2 = MagicMock()
    q2.filter.return_value.order_by.return_value.first.return_value = None

    q3 = MagicMock()
    q3.filter.return_value.order_by.return_value.first.return_value = most_recent

    mock_db.query.side_effect = [q1, q2, q3]

    mock_run = _make_mock_run()

    with patch(
        "src.plugins.translate.orchestrator.TranslationOrchestrator"
    ) as mock_orch_cls:
        mock_orch = MagicMock()
        mock_orch.start_run.return_value = mock_run
        mock_orch_cls.return_value = mock_orch

        with patch(
            "src.plugins.translate.scheduler.TranslationEventLog"
        ) as mock_event_log_cls:
            mock_event_log = MagicMock()
            mock_event_log_cls.return_value = mock_event_log

            execute_scheduled_translation(
                schedule_id="sched-1",
                job_id="job-1",
                db_session_maker=mock_session_maker,
                config_manager=mock_config,
                execution_mode="full",
            )

    assert mock_run.trigger_type == "scheduled"
    mock_event_log.log_event.assert_called_once()
    event_call = mock_event_log.log_event.call_args
    assert event_call.kwargs["payload"]["reason"] == "baseline_expired"
    mock_db.close.assert_called_once()
# #endregion test_baseline_expired_full_mode


# #region test_execution_error_handled [C:1] [TYPE Function]
def test_execution_error_handled():
    """execute_run raises → run is marked FAILED, schedule tracking updated, no crash."""
    from src.plugins.translate.scheduler import execute_scheduled_translation

    mock_db = MagicMock()
    mock_session_maker = MagicMock(return_value=mock_db)
    mock_config = MagicMock()

    schedule = MagicMock()
    schedule.id = "sched-1"
    schedule.job_id = "job-1"
    schedule.is_active = True
    schedule.execution_mode = "full"

    q1 = MagicMock()
    q1.filter.return_value.first.return_value = schedule

    q2 = MagicMock()
    q2.filter.return_value.order_by.return_value.first.return_value = None

    q3 = MagicMock()
    q3.filter.return_value.order_by.return_value.first.return_value = None

    mock_db.query.side_effect = [q1, q2, q3]

    mock_run = _make_mock_run()

    with patch(
        "src.plugins.translate.orchestrator.TranslationOrchestrator"
    ) as mock_orch_cls:
        mock_orch = MagicMock()
        mock_orch.start_run.return_value = mock_run
        mock_orch.execute_run.side_effect = RuntimeError("LLM timeout")
        mock_orch_cls.return_value = mock_orch

        execute_scheduled_translation(
            schedule_id="sched-1",
            job_id="job-1",
            db_session_maker=mock_session_maker,
            config_manager=mock_config,
            execution_mode="full",
        )

    assert mock_run.status == "FAILED"
    assert "LLM timeout" in mock_run.error_message
    assert mock_run.completed_at is not None
    assert schedule.last_run_at is not None
    mock_db.commit.assert_called()
    mock_db.close.assert_called_once()
# #endregion test_execution_error_handled

# #endregion TestTranslateSchedulerExecution
