"""Tests for storage.mirror — the INV-STG1 dual-write seal.

mirror() must: be a no-op under the filesystem backend (the disk write is
canonical), persist to the S3 sub-backend under s3/dual, and never raise (an S3
failure is logged, the request proceeds on the disk copy). Offline.
"""

from __future__ import annotations

import asyncio

import pytest

from legal_mcp.services import storage


class _FakeS3:
    def __init__(self, fail: bool = False) -> None:
        self.fail = fail
        self.puts: list[tuple] = []

    async def put_bytes(self, key, data, *, bucket, content_type=None, metadata=None):
        if self.fail:
            raise RuntimeError("s3 down")
        self.puts.append((key, bytes(data), bucket))
        return f"s3://{bucket}/{key}"


class _FakeFilesystem:
    name = "filesystem"


class _FakeDual:
    name = "dual"

    def __init__(self, s3: _FakeS3) -> None:
        self.s3 = s3


def _run(coro):
    loop = asyncio.new_event_loop()
    try:
        return loop.run_until_complete(coro)
    finally:
        loop.close()


def test_mirror_noop_under_filesystem(monkeypatch):
    monkeypatch.setattr(storage, "get_storage", lambda: _FakeFilesystem())
    # must not raise and must not attempt any S3 work
    _run(storage.mirror("cases/x/y.pdf", b"data", bucket=storage.Bucket.DOCUMENTS))


def test_mirror_persists_under_dual(monkeypatch):
    s3 = _FakeS3()
    monkeypatch.setattr(storage, "get_storage", lambda: _FakeDual(s3))
    _run(storage.mirror("cases/x/y.pdf", b"data", bucket=storage.Bucket.DOCUMENTS))
    assert s3.puts == [("cases/x/y.pdf", b"data", storage.Bucket.DOCUMENTS)]


def test_mirror_best_effort_never_raises(monkeypatch):
    s3 = _FakeS3(fail=True)
    monkeypatch.setattr(storage, "get_storage", lambda: _FakeDual(s3))
    # S3 failure must be swallowed-with-log, never propagate (disk copy holds)
    _run(storage.mirror("cases/x/y.pdf", b"data", bucket=storage.Bucket.DOCUMENTS))


def test_mirror_uses_backend_itself_when_no_s3_attr(monkeypatch):
    # a pure s3 backend has no .s3 sub-attr → getattr falls back to the backend
    s3 = _FakeS3()
    s3.name = "s3"
    monkeypatch.setattr(storage, "get_storage", lambda: s3)
    _run(storage.mirror("k", b"d", bucket=storage.Bucket.DERIVED))
    assert s3.puts == [("k", b"d", storage.Bucket.DERIVED)]
