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

Unit tests for mcp_proxy_adapter.transfer.server_store.TransferServerStore.
"""

from __future__ import annotations

import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

import pytest

from mcp_proxy_adapter.transfer.compression import compress_file
from mcp_proxy_adapter.transfer.errors import TransferResumeConflictError
from mcp_proxy_adapter.transfer.models import TransferStatus
from mcp_proxy_adapter.transfer.server_store import TransferServerStore

PAYLOAD = b"hello world!"
EXPECTED_SHA256 = "7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9"


def _make_store(tmp_path: Path, **kwargs: Any) -> TransferServerStore:
    defaults: dict[str, Any] = dict(
        storage_dir=str(tmp_path / "store"),
        session_ttl_seconds=3600,
        max_file_size_bytes=10737418240,
        default_chunk_size_bytes=4,
        max_chunk_size_bytes=8,
    )
    defaults.update(kwargs)
    return TransferServerStore(**defaults)


def test_create_upload_session_persists_metadata_file(tmp_path: Path) -> None:
    store = _make_store(tmp_path)
    r = store.create_upload_session(
        filename="report.jsonl",
        size_bytes=12,
        checksum_algorithm="sha256",
        checksum_value=EXPECTED_SHA256,
        compression="identity",
        chunk_size=4,
    )
    assert r.offset == 0
    assert r.completed is False
    assert r.chunk_size == 4
    assert r.status == TransferStatus.CREATED.value
    assert r.transfer_id.startswith("tr_")
    meta = tmp_path / "store" / "sessions" / f"{r.transfer_id}.json"
    assert meta.exists()
    data = json.loads(meta.read_text(encoding="utf-8"))
    assert data["size_bytes"] == 12
    assert data["filename"] == "report.jsonl"


def test_write_upload_chunk_advances_offset(tmp_path: Path) -> None:
    store = _make_store(tmp_path)
    r = store.create_upload_session(
        filename="report.jsonl",
        size_bytes=12,
        checksum_algorithm="sha256",
        checksum_value=EXPECTED_SHA256,
        compression="identity",
        chunk_size=4,
    )
    tid = r.transfer_id
    c1 = PAYLOAD[0:4]
    r2 = store.write_upload_chunk(tid, 0, c1)
    assert r2.offset == 4
    assert r2.status == TransferStatus.UPLOADING.value
    r3 = store.write_upload_chunk(tid, 4, PAYLOAD[4:8])
    assert r3.offset == 8
    r4 = store.write_upload_chunk(tid, 8, PAYLOAD[8:12])
    assert r4.offset == 12


def test_write_upload_chunk_with_wrong_offset_raises_resume_conflict(
    tmp_path: Path,
) -> None:
    store = _make_store(tmp_path / "rc")
    r = store.create_upload_session(
        "x.bin",
        12,
        "sha256",
        EXPECTED_SHA256,
        "identity",
        chunk_size=4,
    )
    tid = r.transfer_id
    store.write_upload_chunk(tid, 0, PAYLOAD[0:4])
    with pytest.raises(TransferResumeConflictError) as ctx:
        store.write_upload_chunk(tid, 0, PAYLOAD[0:4])
    assert ctx.value.expected_offset == 4
    assert ctx.value.actual_offset == 0


def test_complete_upload_session_verifies_checksum_and_marks_uploaded(
    tmp_path: Path,
) -> None:
    store = _make_store(tmp_path / "id")
    r = store.create_upload_session(
        "report.jsonl",
        12,
        "sha256",
        EXPECTED_SHA256,
        "identity",
        chunk_size=4,
    )
    tid = r.transfer_id
    store.write_upload_chunk(tid, 0, PAYLOAD[0:4])
    store.write_upload_chunk(tid, 4, PAYLOAD[4:8])
    store.write_upload_chunk(tid, 8, PAYLOAD[8:12])
    r_done = store.complete_upload_session(tid)
    assert r_done.completed is True
    assert r_done.status == TransferStatus.UPLOADED.value
    assert r_done.offset == 12
    committed = store.get_committed_upload_path(tid)
    assert Path(committed).read_bytes() == PAYLOAD

    plain = tmp_path / "plain.bin"
    plain.write_bytes(PAYLOAD)
    gzpath = tmp_path / "plain.gz"
    compress_file(str(plain), str(gzpath), "gzip")
    compressed = gzpath.read_bytes()
    clen = len(compressed)
    gz_store = _make_store(
        tmp_path / "gz",
        max_chunk_size_bytes=max(clen, 65536),
        default_chunk_size_bytes=min(clen, 1048576),
    )
    r2 = gz_store.create_upload_session(
        "f.gz",
        size_bytes=clen,
        checksum_algorithm="sha256",
        checksum_value=EXPECTED_SHA256,
        compression="gzip",
        chunk_size=clen,
    )
    gz_store.write_upload_chunk(r2.transfer_id, 0, compressed)
    r2_done = gz_store.complete_upload_session(r2.transfer_id)
    assert r2_done.completed is True
    assert r2_done.status == TransferStatus.UPLOADED.value


def test_create_download_session_and_read_download_chunk(tmp_path: Path) -> None:
    src = tmp_path / "src.bin"
    src.write_bytes(PAYLOAD)
    store = _make_store(tmp_path / "d")
    r = store.create_download_session(
        str(src), "src.bin", "sha256", "identity", chunk_size=4
    )
    tid = r.transfer_id
    data1, rec1 = store.read_download_chunk(tid, 0, 4)
    assert data1 == PAYLOAD[0:4]
    assert rec1.offset == 4
    assert rec1.completed is False
    data2, rec2 = store.read_download_chunk(tid, 4, 4)
    assert data2 == PAYLOAD[4:8]
    assert rec2.offset == 8
    data3, rec3 = store.read_download_chunk(tid, 8, 4)
    assert data3 == PAYLOAD[8:12]
    assert rec3.offset == 12
    assert rec3.completed is True
    assert rec3.checksum_verified is False


def test_expire_stale_sessions_removes_files_and_marks_expired(
    tmp_path: Path,
) -> None:
    store = _make_store(
        tmp_path / "e", session_ttl_seconds=3600, max_chunk_size_bytes=12
    )
    r = store.create_upload_session(
        "x.bin",
        size_bytes=12,
        checksum_algorithm="sha256",
        checksum_value=EXPECTED_SHA256,
        compression="identity",
        chunk_size=12,
    )
    tid = r.transfer_id
    meta_path = tmp_path / "e" / "store" / "sessions" / f"{tid}.json"
    data = json.loads(meta_path.read_text(encoding="utf-8"))
    data["expires_at"] = "2000-01-01T00:00:00+00:00"
    meta_path.write_text(json.dumps(data, indent=2), encoding="utf-8")
    result = store.expire_stale_sessions(
        now_iso=datetime.now(timezone.utc).isoformat()
    )
    assert result.count >= 1
    assert (tid, "upload") in result.expired
    data2 = json.loads(meta_path.read_text(encoding="utf-8"))
    assert data2["status"] == TransferStatus.EXPIRED.value
    staged = tmp_path / "e" / "store" / "files" / f"{tid}.upload"
    assert not staged.exists()
