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

Tests for new file-transfer lifecycle messages: artifact_ready push, transfer_released on ack,
JSON-RPC lifecycle helpers.
"""

from __future__ import annotations

from pathlib import Path
from typing import Any
from unittest.mock import AsyncMock, patch

import pytest

from mcp_proxy_adapter.core.job_push.events import (
    EVENT_ARTIFACT_READY,
    EVENT_TRANSFER_RELEASED,
)


@pytest.mark.asyncio
async def test_create_download_session_with_job_id_pushes_artifact_ready(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """run_create_download_session emits artifact_ready on queue_job when job_id is set."""
    from mcp_proxy_adapter.api import handlers as handlers_mod
    from mcp_proxy_adapter.api.transfer_session_service import run_create_download_session

    root = tmp_path / "tr"
    root.mkdir()
    files_dir = root / "files"
    files_dir.mkdir(parents=True, exist_ok=True)
    src = files_dir / "out.bin"
    src.write_bytes(b"artifact-payload")

    from mcp_proxy_adapter.transfer.server_store import TransferServerStore

    store = TransferServerStore(
        storage_dir=str(root),
        session_ttl_seconds=3600,
        max_file_size_bytes=10_000_000,
        default_chunk_size_bytes=1024,
        max_chunk_size_bytes=1024,
    )
    monkeypatch.setattr(handlers_mod, "get_transfer_store", lambda: store)

    captured: list[dict[str, Any]] = []

    async def _capture(payload: dict[str, Any]) -> None:
        captured.append(dict(payload))

    monkeypatch.setattr(
        "mcp_proxy_adapter.core.job_push.queue_custom_push.push_queue_job_ws_payload",
        _capture,
    )

    await run_create_download_session(
        {
            "source_path": str(src),
            "filename": "out.bin",
            "compression": "identity",
            "job_id": "job-art",
            "correlation_id": "corr-1",
        }
    )

    assert len(captured) == 1
    msg = captured[0]
    assert msg["event"] == EVENT_ARTIFACT_READY
    assert msg["job_id"] == "job-art"
    assert msg["correlation_id"] == "corr-1"
    assert msg["filename"] == "out.bin"
    assert msg["checksum_algorithm"] == "sha256"
    assert len(str(msg["checksum_value"])) == 64
    assert msg["size_bytes"] == len(b"artifact-payload")
    assert str(msg["outbound_transfer_id"]).startswith("tr_")


@pytest.mark.asyncio
async def test_transfer_ack_emits_transfer_released_once(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """jsonrpc_transfer_ack calls notify_transfer_state_changed with transfer_released."""
    from mcp_proxy_adapter.api import handlers as handlers_mod
    from mcp_proxy_adapter.api.jsonrpc_job_lifecycle import jsonrpc_transfer_ack

    root = tmp_path / "tr2"
    root.mkdir()
    files_dir = root / "files"
    files_dir.mkdir(parents=True, exist_ok=True)
    src = files_dir / "z.bin"
    src.write_bytes(b"rel-test")
    from mcp_proxy_adapter.transfer.checksums import compute_file_checksum
    from mcp_proxy_adapter.transfer.server_store import TransferServerStore

    store = TransferServerStore(
        storage_dir=str(root),
        session_ttl_seconds=3600,
        max_file_size_bytes=10_000_000,
        default_chunk_size_bytes=1024,
        max_chunk_size_bytes=1024,
    )
    monkeypatch.setattr(handlers_mod, "get_transfer_store", lambda: store)

    receipt = store.create_download_session(
        str(src),
        "z.bin",
        "sha256",
        "identity",
        job_id="job-rel",
    )
    tid = receipt.transfer_id
    digest = compute_file_checksum(str(src))
    store.read_download_chunk(tid, 0, 10_000)

    notify_mock = AsyncMock()
    with patch(
        "mcp_proxy_adapter.core.job_push.transfer_push.notify_transfer_state_changed",
        notify_mock,
    ):
        out = await jsonrpc_transfer_ack(
            {
                "transfer_id": tid,
                "checksum_value": digest,
                "job_id": "job-rel",
            }
        )
    assert out["success"] is True
    assert out.get("idempotent") is False
    notify_mock.assert_awaited_once()
    args, _kwargs = notify_mock.call_args
    assert args[0] == tid
    assert args[1] == EVENT_TRANSFER_RELEASED
    assert args[2].get("direction") == "download"

    notify_mock.reset_mock()
    out2 = await jsonrpc_transfer_ack(
        {
            "transfer_id": tid,
            "checksum_value": digest,
            "job_id": "job-rel",
        }
    )
    assert out2.get("idempotent") is True
    notify_mock.assert_not_awaited()


@pytest.mark.asyncio
async def test_job_input_accepted_pushes_event(monkeypatch: pytest.MonkeyPatch) -> None:
    from mcp_proxy_adapter.api.jsonrpc_job_lifecycle import jsonrpc_job_input_accepted
    from mcp_proxy_adapter.core.job_push.events import EVENT_JOB_INPUT_ACCEPTED

    captured: list[dict[str, Any]] = []

    async def _capture(payload: dict[str, Any]) -> None:
        captured.append(dict(payload))

    monkeypatch.setattr(
        "mcp_proxy_adapter.api.jsonrpc_job_lifecycle.push_queue_job_ws_payload",
        _capture,
    )
    await jsonrpc_job_input_accepted(
        {
            "job_id": "j-in",
            "command": "file_chunk",
            "inbound_transfer_id": "tr_up",
            "filename": "a.md",
        }
    )
    assert len(captured) == 1
    assert captured[0]["event"] == EVENT_JOB_INPUT_ACCEPTED
    assert captured[0]["job_id"] == "j-in"
    assert captured[0]["command"] == "file_chunk"
    assert captured[0]["inbound_transfer_id"] == "tr_up"
    assert captured[0]["filename"] == "a.md"
