"""
Tests for queue_get_job_status returning progress/description updated by the job.

Verifies that (1) jobs that call set_progress/set_description in the worker and
(2) commands run via CommandExecutionJob that use context["progress_tracker"]
both have their updates visible to get_job_status (via queuemgr update_job_state).
If these tests fail, the bug is reproduced: worker updates not visible to reader.

Author: Vasiliy Zdanovskiy
email: vasilyvz@gmail.com
"""

import asyncio
from typing import AsyncGenerator, List

import pytest
import pytest_asyncio

from mcp_proxy_adapter.commands.queue.jobs import CommandExecutionJob, LongRunningJob
from mcp_proxy_adapter.integrations.queuemgr_integration import (
    QueueJobStatus,
    QueueManagerIntegration,
    init_global_queue_manager,
    shutdown_global_queue_manager,
)


@pytest_asyncio.fixture
async def queue_manager() -> AsyncGenerator[QueueManagerIntegration, None]:
    """Create and start queue manager for testing."""
    manager = await init_global_queue_manager(
        in_memory=True,
        max_concurrent_jobs=5,
        completed_job_retention_seconds=60,
    )
    yield manager
    await shutdown_global_queue_manager()


@pytest.mark.asyncio
async def test_get_job_status_returns_progress_and_description_while_job_runs(
    queue_manager: QueueManagerIntegration,
) -> None:
    """
    LongRunningJob updates progress and description in worker;
    get_job_status must return those updates.
    """
    job_id = "test_progress_visibility"
    duration_seconds = 5
    result = await queue_manager.add_job(
        LongRunningJob,
        job_id,
        {"task_type": "repro", "duration": duration_seconds},
    )
    assert result.status == QueueJobStatus.PENDING

    await queue_manager.start_job(job_id)

    progress_values: List[int] = []
    description_values: List[str] = []
    poll_interval = 0.5
    max_polls = 8
    for _ in range(max_polls):
        await asyncio.sleep(poll_interval)
        status = await queue_manager.get_job_status(job_id)
        progress_values.append(status.progress)
        description_values.append(status.description or "")
        if status.status in (
            QueueJobStatus.COMPLETED,
            QueueJobStatus.FAILED,
            QueueJobStatus.STOPPED,
        ):
            break

    assert any(p > 0 for p in progress_values), (
        f"get_job_status never returned progress > 0. "
        f"progress_values={progress_values}."
    )
    assert any("% complete" in d for d in description_values), (
        f"get_job_status never returned description with '% complete'. "
        f"description_values={description_values}."
    )


@pytest.mark.asyncio
async def test_get_job_status_returns_progress_from_command_via_progress_tracker(
    queue_manager: QueueManagerIntegration,
) -> None:
    """
    Reproduce bug: CommandExecutionJob runs a command that updates
    context['progress_tracker']; get_job_status must return those updates.

    If progress/description stay 0 and 'Executing command: progress_test',
    the bug is reproduced (worker updates not visible to status reader).
    """
    job_id = "test_command_progress_visibility"
    job_params = {
        "command": "progress_test",
        "params": {"steps": 5},
        "context": {},
        "auto_import_modules": ["tests.test_module_progress_command"],
    }
    result = await queue_manager.add_job(
        CommandExecutionJob,
        job_id,
        job_params,
    )
    assert result.status == QueueJobStatus.PENDING

    await queue_manager.start_job(job_id)

    progress_values: List[int] = []
    description_values: List[str] = []
    poll_interval = 0.35
    max_polls = 12
    for _ in range(max_polls):
        await asyncio.sleep(poll_interval)
        status = await queue_manager.get_job_status(job_id)
        progress_values.append(status.progress)
        description_values.append(status.description or "")
        if status.status in (
            QueueJobStatus.COMPLETED,
            QueueJobStatus.FAILED,
            QueueJobStatus.STOPPED,
        ):
            break

    assert any(p > 0 for p in progress_values), (
        f"Bug reproduced: get_job_status never returned progress > 0 for command "
        f"updating progress_tracker. progress_values={progress_values}."
    )
    assert any("Step" in d and "/" in d for d in description_values), (
        f"Bug reproduced: get_job_status never returned description from command "
        f"(e.g. 'Step 1/5'). description_values={description_values}."
    )
