"""F1 unit tests — the RLock-guarded connection wrapper (100% target).

No real libvirt. Covers: double-close no-op, no-ResourceWarning after a
proper close, ResourceWarning on a leaked (unclosed) connection,
context-manager close, guarded-call serialization under threads, and
the connection-state failure translations.
"""

from __future__ import annotations

import gc
import threading
import warnings

import pytest
from mgf.vm.core.exceptions import (
    HypervisorConnectionError,
    HypervisorTransientError,
)
from tests.conftest import FakeConn

from mgf.vm.libvirt._connection import LibvirtConnection


def test_adopt_and_basic_call(fake_conn: FakeConn) -> None:
    conn = LibvirtConnection.adopt(fake_conn, uri="test:///x")
    assert conn.uri == "test:///x"
    assert conn.closed is False
    result = conn.call(fake_conn.listAllDomains)
    assert result == []
    conn.close()


def test_double_close_is_noop(fake_conn: FakeConn) -> None:
    conn = LibvirtConnection.adopt(fake_conn)
    conn.close()
    assert conn.closed is True
    assert fake_conn.close_calls == 1
    # Second close must not call the native close again and must not raise.
    conn.close()
    assert fake_conn.close_calls == 1


def test_no_resource_warning_after_close(fake_conn: FakeConn) -> None:
    conn = LibvirtConnection.adopt(fake_conn)
    conn.close()
    with warnings.catch_warnings():
        warnings.simplefilter("error")  # any ResourceWarning -> failure
        del conn
        gc.collect()  # force __del__


def test_resource_warning_on_leak() -> None:
    conn = LibvirtConnection.adopt(FakeConn())
    with pytest.warns(ResourceWarning, match="was not closed"):
        del conn
        gc.collect()


def test_context_manager_closes(fake_conn: FakeConn) -> None:
    with LibvirtConnection.adopt(fake_conn) as conn:
        assert conn.closed is False
    assert conn.closed is True
    assert fake_conn.close_calls == 1


def test_call_after_close_raises_connection_error(fake_conn: FakeConn) -> None:
    conn = LibvirtConnection.adopt(fake_conn)
    conn.close()
    with pytest.raises(HypervisorConnectionError, match="is closed"):
        conn.call(fake_conn.listAllDomains)


def test_raw_after_close_raises(fake_conn: FakeConn) -> None:
    conn = LibvirtConnection.adopt(fake_conn)
    conn.close()
    with pytest.raises(HypervisorConnectionError):
        conn.raw()


def test_raw_returns_underlying_when_open(fake_conn: FakeConn) -> None:
    conn = LibvirtConnection.adopt(fake_conn)
    assert conn.raw() is fake_conn
    conn.close()


def test_close_failure_becomes_transient_but_marks_closed() -> None:
    bad = FakeConn(close_raises=RuntimeError("daemon vanished"))
    conn = LibvirtConnection.adopt(bad)
    with pytest.raises(HypervisorTransientError, match="error closing"):
        conn.close()
    # Wrapper is durably closed despite the native close raising.
    assert conn.closed is True
    # And a second close is a clean no-op (does NOT re-raise).
    conn.close()


def test_close_failure_no_resource_warning() -> None:
    bad = FakeConn(close_raises=RuntimeError("boom"))
    conn = LibvirtConnection.adopt(bad)
    with pytest.raises(HypervisorTransientError):
        conn.close()
    with warnings.catch_warnings():
        warnings.simplefilter("error")
        del conn
        gc.collect()


def test_lock_is_reentrant(fake_conn: FakeConn) -> None:
    conn = LibvirtConnection.adopt(fake_conn)

    def nested() -> str:
        # Re-enter the guarded call from within a guarded call.
        return conn.call(lambda: "inner")

    assert conn.call(nested) == "inner"
    conn.close()


def test_concurrent_calls_serialize() -> None:
    """Smoke: N threads hammering call() never interleave the body.

    The body asserts a shared "in flight" flag is never seen set by a
    second thread — i.e. the RLock serializes access to the connection.
    """
    conn = LibvirtConnection.adopt(FakeConn())
    in_flight = False
    races: list[str] = []
    barrier = threading.Barrier(8)

    def worker() -> None:
        nonlocal in_flight
        barrier.wait()
        for _ in range(50):

            def body() -> None:
                nonlocal in_flight
                if in_flight:
                    races.append("overlap")
                in_flight = True
                # touch a little work to widen the window
                _ = sum(range(20))
                in_flight = False

            conn.call(body)

    threads = [threading.Thread(target=worker) for _ in range(8)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()

    assert races == []
    conn.close()


def test_lock_property_is_the_guard(fake_conn: FakeConn) -> None:
    conn = LibvirtConnection.adopt(fake_conn)
    lock = conn.lock
    # Holding the wrapper lock blocks a concurrent call() from another thread.
    acquired_inside = []
    with lock:

        def other() -> None:
            got = lock.acquire(timeout=0.2)
            acquired_inside.append(got)
            if got:
                lock.release()

        t = threading.Thread(target=other)
        t.start()
        t.join()
    # The RLock is re-entrant for the SAME thread, but a different thread
    # must not acquire it while we hold it.
    assert acquired_inside == [False]
    conn.close()


def test_close_when_conn_has_no_close_method() -> None:
    class NoClose:
        pass

    conn = LibvirtConnection.adopt(NoClose())
    conn.close()  # must not raise
    assert conn.closed is True


def test_open_real_libvirt_test_driver() -> None:
    """Exercise the real ``_open_libvirt`` success path via libvirt's
    built-in ``test:///`` driver (no host privileges, no daemon)."""
    libvirt = pytest.importorskip("libvirt")
    assert libvirt is not None
    conn = LibvirtConnection.open("test:///default")
    try:
        # A real guarded native call through the wrapper.
        domains = conn.call(conn.raw().listAllDomains)
        assert isinstance(domains, list)
    finally:
        conn.close()
    assert conn.closed is True


def test_open_bad_uri_raises_connection_error() -> None:
    """A real libvirtError from libvirt.open() becomes HypervisorConnectionError."""
    pytest.importorskip("libvirt")
    with pytest.raises(HypervisorConnectionError, match="could not open"):
        LibvirtConnection.open("nonexistent-driver:///bogus")


def test_close_when_conn_is_none() -> None:
    conn = LibvirtConnection.adopt(None)
    conn.close()  # the `conn is None` branch — must not raise
    assert conn.closed is True


def test_open_translates_missing_binding(monkeypatch: pytest.MonkeyPatch) -> None:
    """If the libvirt import fails, open() raises HypervisorConnectionError."""
    import builtins

    real_import = builtins.__import__

    def fake_import(name: str, *args: object, **kwargs: object) -> object:
        if name == "libvirt":
            raise ImportError("no libvirt here")
        return real_import(name, *args, **kwargs)  # type: ignore[arg-type]

    monkeypatch.setattr(builtins, "__import__", fake_import)
    with pytest.raises(HypervisorConnectionError, match="not installed"):
        LibvirtConnection.open("qemu:///system")
