"""Unit tests for notifier domain-aware dedup (queue_job vs ws_delivery).

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

from __future__ import annotations

from typing import Any, Dict, List, Optional

import pytest

from mcp_proxy_adapter.core.job_push.notifier import (
    NotificationDomain,
    clear_notifier_listeners,
    notify_job_state_changed,
    register_notifier_listener,
)


@pytest.fixture(autouse=True)
def _clear_listeners() -> None:
    """Clear notifier state before and after each test."""
    clear_notifier_listeners()
    yield
    clear_notifier_listeners()


@pytest.mark.asyncio
async def test_domain_queue_job_dedup_same_id_once() -> None:
    """Same identifier in QUEUE_JOB terminal status is notified once per status."""
    received: List[tuple] = []

    async def listener(
        domain: NotificationDomain,
        identifier: str,
        status: str,
        result: Optional[Dict[str, Any]],
        error: Optional[str],
        progress: int,
        description: str,
    ) -> None:
        received.append((domain, identifier, status))

    register_notifier_listener(listener)
    await notify_job_state_changed(
        NotificationDomain.QUEUE_JOB, "job-1", "completed", {"x": 1}
    )
    await notify_job_state_changed(
        NotificationDomain.QUEUE_JOB, "job-1", "completed", {"x": 2}
    )
    assert len(received) == 1
    assert received[0] == (NotificationDomain.QUEUE_JOB, "job-1", "completed")


@pytest.mark.asyncio
async def test_domain_ws_delivery_dedup_same_id_once() -> None:
    """Same identifier in WS_DELIVERY terminal status is notified once."""
    received: List[tuple] = []

    async def listener(
        domain: NotificationDomain,
        identifier: str,
        status: str,
        result: Optional[Dict[str, Any]],
        error: Optional[str],
        progress: int,
        description: str,
    ) -> None:
        received.append((domain, identifier, status))

    register_notifier_listener(listener)
    await notify_job_state_changed(
        NotificationDomain.WS_DELIVERY, "ws-abc", "completed", {}
    )
    await notify_job_state_changed(
        NotificationDomain.WS_DELIVERY, "ws-abc", "completed", {}
    )
    assert len(received) == 1
    assert received[0] == (NotificationDomain.WS_DELIVERY, "ws-abc", "completed")


@pytest.mark.asyncio
async def test_domain_partitioned_dedup_cross_domain_same_id() -> None:
    """Same identifier in QUEUE_JOB and WS_DELIVERY each notified (domain-partitioned)."""
    received: List[tuple] = []

    async def listener(
        domain: NotificationDomain,
        identifier: str,
        status: str,
        result: Optional[Dict[str, Any]],
        error: Optional[str],
        progress: int,
        description: str,
    ) -> None:
        received.append((domain, identifier, status))

    register_notifier_listener(listener)
    await notify_job_state_changed(
        NotificationDomain.QUEUE_JOB, "same-id", "completed", {}
    )
    await notify_job_state_changed(
        NotificationDomain.WS_DELIVERY, "same-id", "completed", {}
    )
    assert len(received) == 2
    domains = [r[0] for r in received]
    assert NotificationDomain.QUEUE_JOB in domains
    assert NotificationDomain.WS_DELIVERY in domains


@pytest.mark.asyncio
async def test_empty_identifier_raises() -> None:
    """notify_job_state_changed with empty identifier raises ValueError."""
    with pytest.raises(ValueError, match="non-empty identifier"):
        await notify_job_state_changed(
            NotificationDomain.QUEUE_JOB, "  ", "completed", None
        )
