"""Tests for execute-async acceptance contract models and validation (T01 + T05).

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

from __future__ import annotations

import pytest
from pydantic import ValidationError

from mcp_proxy_adapter.api.execute_async_contract import parse_acceptance_payload

# T05: parametrized matrix (payload, expected exception type or None for success)
MATRIX_ROWS: list[tuple[dict[str, object], type[BaseException] | None]] = [
    (
        {
            "accepted": True,
            "job_id": "j",
            "ws_id": None,
            "delivery": {"mode": "poll"},
        },
        None,
    ),
    (
        {
            "accepted": True,
            "job_id": "j",
            "ws_id": None,
            "delivery": {"mode": "ws", "terminal_only": True},
        },
        ValueError,
    ),
    (
        {
            "accepted": True,
            "job_id": "job-1",
            "ws_id": "ws-1",
            "delivery": {"mode": "poll"},
        },
        ValueError,
    ),
    (
        {
            "accepted": True,
            "job_id": None,
            "ws_id": "w",
            "delivery": {"mode": "ws", "terminal_only": False},
        },
        ValueError,
    ),
    (
        {
            "accepted": True,
            "job_id": None,
            "ws_id": None,
            "delivery": {"mode": "poll"},
        },
        ValueError,
    ),
    (
        {
            "accepted": True,
            "job_id": None,
            "ws_id": None,
            "delivery": {"mode": "ws", "terminal_only": True},
        },
        ValueError,
    ),
]
EXPECTED_T05_MATRIX_LEN = len(MATRIX_ROWS)


def test_legal_queued_no_ws() -> None:
    """Legal: accepted, job_id set, ws_id null, delivery.mode poll."""
    payload = {
        "accepted": True,
        "job_id": "job-123",
        "ws_id": None,
        "delivery": {"mode": "poll"},
    }
    model = parse_acceptance_payload(payload)
    assert model.accepted is True
    assert model.job_id == "job-123"
    assert model.ws_id is None
    assert model.delivery.mode == "poll"


def test_legal_in_process_ws() -> None:
    """Legal: accepted, job_id null, ws_id set, delivery mode ws, terminal_only True."""
    payload = {
        "accepted": True,
        "job_id": None,
        "ws_id": "ws-456",
        "delivery": {"mode": "ws", "terminal_only": True},
    }
    model = parse_acceptance_payload(payload)
    assert model.job_id is None
    assert model.ws_id == "ws-456"
    assert model.delivery.terminal_only is True


def test_legal_queued_with_ws() -> None:
    """Legal: accepted, both job_id and ws_id set, mode ws, terminal_only True."""
    payload = {
        "accepted": True,
        "job_id": "job-123",
        "ws_id": "ws-789",
        "delivery": {"mode": "ws", "terminal_only": True},
    }
    model = parse_acceptance_payload(payload)
    assert model.job_id == "job-123"
    assert model.ws_id == "ws-789"
    assert model.delivery.mode == "ws"
    assert model.delivery.terminal_only is True


def test_forbidden_ws_with_poll_mode() -> None:
    """Forbidden: ws_id set with delivery.mode poll raises ValueError."""
    payload = {
        "accepted": True,
        "job_id": "job-1",
        "ws_id": "ws-1",
        "delivery": {"mode": "poll"},
    }
    with pytest.raises(
        ValueError, match="ws_id must be null when delivery.mode is poll"
    ):
        parse_acceptance_payload(payload)


def test_forbidden_ws_mode_without_ws_id() -> None:
    """Forbidden: delivery.mode ws with ws_id null raises ValueError."""
    payload = {
        "accepted": True,
        "job_id": "job-1",
        "ws_id": None,
        "delivery": {"mode": "ws", "terminal_only": True},
    }
    with pytest.raises(ValueError, match="ws_id required when delivery.mode is ws"):
        parse_acceptance_payload(payload)


def test_parse_legacy_deliver_id_optional() -> None:
    """Legacy delivery-id key maps to ws_id when allow_legacy_deliver_id=True; rejected when False."""
    legacy_key = "deliver" + "_id"
    payload = {
        "accepted": True,
        "job_id": None,
        "delivery": {"mode": "ws", "terminal_only": True},
        legacy_key: "legacy-ws",
    }
    model = parse_acceptance_payload(payload, allow_legacy_deliver_id=True)
    assert model.ws_id == "legacy-ws"

    with pytest.raises((ValueError, ValidationError)):
        parse_acceptance_payload(
            {
                "accepted": True,
                "job_id": None,
                "delivery": {"mode": "ws", "terminal_only": True},
                legacy_key: "legacy-ws",
            },
            allow_legacy_deliver_id=False,
        )


def test_t05_param_matrix_row_count() -> None:
    """T05: checklist test; expected number of matrix rows is fixed."""
    assert len(MATRIX_ROWS) == EXPECTED_T05_MATRIX_LEN


@pytest.mark.parametrize(
    "payload,expect_exc", MATRIX_ROWS, ids=[f"row_{i}" for i in range(len(MATRIX_ROWS))]
)
def test_t05_matrix_row(
    payload: dict[str, object], expect_exc: type[BaseException] | None
) -> None:
    """T05: each matrix row passes parse_acceptance_payload with expected outcome."""
    payload_dict = {k: v for k, v in payload.items()}
    if expect_exc is None:
        model = parse_acceptance_payload(payload_dict)
        assert model.accepted is True
    else:
        with pytest.raises(expect_exc):
            parse_acceptance_payload(payload_dict)
