"""Tests for TransferDownloader resumable download and checksum verification.

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

from __future__ import annotations

from unittest.mock import AsyncMock

import pytest

from mcp_proxy_adapter.client.jsonrpc_client.exceptions import (
    TransferChecksumMismatchClientError,
    TransferRangeNotSatisfiableClientError,
)
from mcp_proxy_adapter.transfer.checksums import compute_file_checksum
from mcp_proxy_adapter.transfer.client_downloader import (
    PARTIAL_SUFFIX,
    TransferDownloader,
)


@pytest.mark.asyncio
async def test_download_file_reads_chunks_and_writes_destination(
    tmp_path,
) -> None:
    src = tmp_path / "server.bin"
    payload = b"hello world"
    src.write_bytes(payload)
    checksum = compute_file_checksum(str(src))

    tid = "tr_dl1"
    transport = AsyncMock()
    transport.get_download_session = AsyncMock(
        return_value={
            "transfer_id": tid,
            "size_bytes": len(payload),
            "compression": "identity",
            "checksum_value": checksum,
            "checksum_algorithm": "sha256",
            "status": "created",
        }
    )

    async def _dl_chunk(t_id: str, offset: int, limit: int) -> tuple[bytes, dict]:
        assert t_id == tid
        chunk = payload[offset:offset + limit]
        new_off = offset + len(chunk)
        done = new_off >= len(payload)
        return chunk, {
            "transfer_id": tid,
            "offset": new_off,
            "completed": done,
            "status": "completed" if done else "downloading",
        }

    transport.download_chunk = AsyncMock(side_effect=_dl_chunk)

    dest = tmp_path / "out.bin"
    dl = TransferDownloader(transport, default_chunk_size=4)
    receipt = await dl.download_file(tid, str(dest))

    assert dest.read_bytes() == payload
    assert receipt.checksum_verified is True
    assert receipt.completed is True
    assert not (tmp_path / (dest.name + PARTIAL_SUFFIX)).exists()


@pytest.mark.asyncio
async def test_resume_download_uses_existing_local_size(tmp_path) -> None:
    src = tmp_path / "srv2.bin"
    payload = b"abcdefghij"
    src.write_bytes(payload)
    checksum = compute_file_checksum(str(src))
    tid = "tr_dl2"

    partial = tmp_path / f"out2.bin{PARTIAL_SUFFIX}"
    partial.write_bytes(payload[:4])

    transport = AsyncMock()
    transport.get_download_session = AsyncMock(
        return_value={
            "transfer_id": tid,
            "size_bytes": len(payload),
            "compression": "identity",
            "checksum_value": checksum,
            "checksum_algorithm": "sha256",
            "status": "downloading",
        }
    )

    async def _dl_chunk(t_id: str, offset: int, limit: int) -> tuple[bytes, dict]:
        chunk = payload[offset:offset + limit]
        new_off = offset + len(chunk)
        done = new_off >= len(payload)
        return chunk, {
            "transfer_id": tid,
            "offset": new_off,
            "completed": done,
            "status": "completed" if done else "downloading",
        }

    transport.download_chunk = AsyncMock(side_effect=_dl_chunk)

    dest = tmp_path / "out2.bin"
    dl = TransferDownloader(transport, default_chunk_size=3)
    await dl.resume_download(tid, str(dest))

    assert dest.read_bytes() == payload
    first = transport.download_chunk.await_args_list[0]
    assert first[0][1] == 4


@pytest.mark.asyncio
async def test_download_file_verifies_checksum_after_completion(
    tmp_path,
) -> None:
    src = tmp_path / "srv3.bin"
    src.write_bytes(b"x")
    wrong_checksum = "0" * 64
    tid = "tr_dl3"

    transport = AsyncMock()
    transport.get_download_session = AsyncMock(
        return_value={
            "transfer_id": tid,
            "size_bytes": 1,
            "compression": "identity",
            "checksum_value": wrong_checksum,
            "checksum_algorithm": "sha256",
            "status": "created",
        }
    )
    transport.download_chunk = AsyncMock(
        return_value=(
            b"x",
            {
                "transfer_id": tid,
                "offset": 1,
                "completed": True,
                "status": "completed",
            },
        )
    )

    dl = TransferDownloader(transport, default_chunk_size=1024)
    with pytest.raises(TransferChecksumMismatchClientError):
        await dl.download_file(tid, str(tmp_path / "badout.bin"))


@pytest.mark.asyncio
async def test_resume_download_propagates_range_error(tmp_path) -> None:
    tid = "tr_dl4"
    transport = AsyncMock()
    transport.get_download_session = AsyncMock(
        return_value={
            "transfer_id": tid,
            "size_bytes": 5,
            "compression": "identity",
            "checksum_value": "a" * 64,
            "checksum_algorithm": "sha256",
            "status": "created",
        }
    )

    partial = tmp_path / "big.partial"
    partial.write_bytes(b"123456")

    dl = TransferDownloader(transport, default_chunk_size=2)
    with pytest.raises(TransferRangeNotSatisfiableClientError):
        await dl.resume_download(tid, str(tmp_path / "big"))
