"""Tests for logging state transition utilities."""

from __future__ import annotations

from enum import Enum
from typing import TYPE_CHECKING
from unittest.mock import MagicMock, patch

import pytest

from noteflow.infrastructure.logging.transitions import log_state_transition

if TYPE_CHECKING:
    from collections.abc import Generator


# Sample enum for state transitions (prefixed to avoid pytest collection)
class SampleState(Enum):
    """Sample state enum for testing."""

    PENDING = "pending"
    RUNNING = "running"
    COMPLETED = "completed"
    FAILED = "failed"


# Constants
ENTITY_TYPE = "test_entity"
ENTITY_ID = "test-123"
EXPECTED_EVENT = f"{ENTITY_TYPE}_state_transition"


@pytest.fixture
def mock_transition_logger() -> Generator[MagicMock, None, None]:
    """Provide mock logger for transition module tests."""
    with patch("noteflow.infrastructure.logging.transitions.get_logger") as mock_get:
        mock_log = MagicMock()
        mock_get.return_value = mock_log
        yield mock_log


class TestLogStateTransition:
    """Test log_state_transition function."""

    def test_logs_enum_state_transition(self, mock_transition_logger: MagicMock) -> None:
        """Verify enum states logged with their values."""
        log_state_transition(
            ENTITY_TYPE,
            ENTITY_ID,
            old_state=SampleState.PENDING,
            new_state=SampleState.RUNNING,
        )

        mock_transition_logger.info.assert_called_once()
        call_args = mock_transition_logger.info.call_args
        assert call_args[0][0] == EXPECTED_EVENT
        assert call_args[1]["entity_type"] == ENTITY_TYPE
        assert call_args[1]["entity_id"] == ENTITY_ID
        assert call_args[1]["old_state"] == "pending"
        assert call_args[1]["new_state"] == "running"

    def test_logs_string_state_transition(self, mock_transition_logger: MagicMock) -> None:
        """Verify string states logged directly."""
        log_state_transition(
            ENTITY_TYPE,
            ENTITY_ID,
            old_state="active",
            new_state="inactive",
        )

        call_args = mock_transition_logger.info.call_args
        assert call_args[1]["old_state"] == "active"
        assert call_args[1]["new_state"] == "inactive"

    def test_logs_none_old_state_for_creation(self, mock_transition_logger: MagicMock) -> None:
        """Verify None old_state logged for initial creation."""
        log_state_transition(
            ENTITY_TYPE,
            ENTITY_ID,
            old_state=None,
            new_state=SampleState.PENDING,
        )

        call_args = mock_transition_logger.info.call_args
        assert call_args[1]["old_state"] is None
        assert call_args[1]["new_state"] == "pending"

    def test_includes_context_kwargs(self, mock_transition_logger: MagicMock) -> None:
        """Verify additional context passed to log."""
        log_state_transition(
            ENTITY_TYPE,
            ENTITY_ID,
            old_state=SampleState.RUNNING,
            new_state=SampleState.COMPLETED,
            workspace_id="ws-456",
            user_id="user-789",
        )

        call_args = mock_transition_logger.info.call_args
        assert call_args[1]["workspace_id"] == "ws-456"
        assert call_args[1]["user_id"] == "user-789"

    def test_filters_none_context_values(self, mock_transition_logger: MagicMock) -> None:
        """Verify None context values are filtered out."""
        log_state_transition(
            ENTITY_TYPE,
            ENTITY_ID,
            old_state=SampleState.PENDING,
            new_state=SampleState.RUNNING,
            workspace_id="ws-123",
            optional=None,
        )

        call_args = mock_transition_logger.info.call_args
        assert call_args[1]["workspace_id"] == "ws-123"
        assert "optional" not in call_args[1]

    def test_handles_mixed_enum_and_string(self, mock_transition_logger: MagicMock) -> None:
        """Verify mixed enum/string states handled correctly."""
        log_state_transition(
            ENTITY_TYPE,
            ENTITY_ID,
            old_state="legacy_state",
            new_state=SampleState.COMPLETED,
        )

        call_args = mock_transition_logger.info.call_args
        assert call_args[1]["old_state"] == "legacy_state"
        assert call_args[1]["new_state"] == "completed"

    @pytest.mark.parametrize(
        ("old_state", "new_state", "expected_old", "expected_new"),
        [
            pytest.param(
                SampleState.PENDING,
                SampleState.RUNNING,
                "pending",
                "running",
                id="enum-to-enum",
            ),
            pytest.param(
                None,
                SampleState.PENDING,
                None,
                "pending",
                id="none-to-enum",
            ),
            pytest.param(
                "active",
                "inactive",
                "active",
                "inactive",
                id="string-to-string",
            ),
            pytest.param(
                SampleState.RUNNING,
                "custom_state",
                "running",
                "custom_state",
                id="enum-to-string",
            ),
        ],
    )
    def test_state_value_extraction(
        self,
        mock_transition_logger: MagicMock,
        old_state: SampleState | str | None,
        new_state: SampleState | str,
        expected_old: str | None,
        expected_new: str,
    ) -> None:
        """Verify state values extracted correctly for various types."""
        log_state_transition(
            ENTITY_TYPE,
            ENTITY_ID,
            old_state=old_state,
            new_state=new_state,
        )

        call_args = mock_transition_logger.info.call_args
        assert call_args[1]["old_state"] == expected_old
        assert call_args[1]["new_state"] == expected_new
