"""Behaviour tests for ``mgf.vm.core.lifecycle.delete``."""

from __future__ import annotations

import pytest

import mgf.vm.core.lifecycle.delete as delete_mod
from mgf.vm.core.common.progress import RecordingReporter
from mgf.vm.core.contracts import VmSpec
from mgf.vm.core.exceptions import (
    DomainNotFound,
    HypervisorTransientError,
    OperationError,
)
from mgf.vm.core.fakes.hypervisor import FakeHypervisorBackend
from mgf.vm.core.ids import DomainId


def _defined(backend: FakeHypervisorBackend, name: str = "vm1") -> DomainId:
    return backend.define(VmSpec(name=name, cpu=1, memory_bytes=1 << 30))


def test_delete_stopped_domain_undefines_it() -> None:
    backend = FakeHypervisorBackend()
    d = _defined(backend)
    reporter = RecordingReporter()

    delete_mod.delete(backend, d, reporter=reporter)

    assert d not in backend.state
    msgs = [c[0] for c in reporter.calls]
    assert msgs == [f"undefining {d.value}", f"{d.value} undefined"]


def test_delete_running_without_force_refuses() -> None:
    backend = FakeHypervisorBackend()
    d = _defined(backend)
    backend.start(d)

    with pytest.raises(OperationError) as ei:
        delete_mod.delete(backend, d, force=False)
    assert "running" in str(ei.value)
    # Domain still defined — nothing was destroyed.
    assert d in backend.state


def test_delete_running_with_force_stops_then_undefines() -> None:
    backend = FakeHypervisorBackend()
    d = _defined(backend)
    backend.start(d)
    reporter = RecordingReporter()

    delete_mod.delete(backend, d, force=True, reporter=reporter)

    assert d not in backend.state
    msgs = [c[0] for c in reporter.calls]
    assert msgs[0] == f"force-stopping {d.value} before delete"
    assert f"{d.value} undefined" in msgs


def test_delete_missing_domain_raises_not_found() -> None:
    backend = FakeHypervisorBackend()

    with pytest.raises(DomainNotFound):
        delete_mod.delete(backend, DomainId("nope"))


def test_delete_storage_flag_forwarded_to_undefine() -> None:
    seen: dict[str, object] = {}

    class _Backend:
        def get_domain(self, d: DomainId) -> object:
            from mgf.vm.core.contracts import DomainInfo

            return DomainInfo(domain_id=d, name="vm1", cpu=1, memory_bytes=1, is_running=False)

        def undefine(self, d: DomainId, *, delete_storage: bool = False) -> None:
            seen["delete_storage"] = delete_storage

    delete_mod.delete(_Backend(), DomainId("vm1"), delete_storage=True)
    assert seen["delete_storage"] is True


def test_get_domain_provider_error_translated() -> None:
    class _Backend:
        def get_domain(self, d: DomainId) -> object:
            raise HypervisorTransientError("cannot read")

    with pytest.raises(OperationError) as ei:
        delete_mod.delete(_Backend(), DomainId("vm1"))
    assert "could not read state" in str(ei.value)


def test_undefine_provider_error_translated() -> None:
    class _Backend:
        def get_domain(self, d: DomainId) -> object:
            from mgf.vm.core.contracts import DomainInfo

            return DomainInfo(domain_id=d, name="vm1", cpu=1, memory_bytes=1, is_running=False)

        def undefine(self, d: DomainId, *, delete_storage: bool = False) -> None:
            raise HypervisorTransientError("undefine failed")

    with pytest.raises(OperationError) as ei:
        delete_mod.delete(_Backend(), DomainId("vm1"))
    assert "could not undefine" in str(ei.value)


def test_undefine_domain_not_found_passes_through() -> None:
    class _Backend:
        def get_domain(self, d: DomainId) -> object:
            from mgf.vm.core.contracts import DomainInfo

            return DomainInfo(domain_id=d, name="vm1", cpu=1, memory_bytes=1, is_running=False)

        def undefine(self, d: DomainId, *, delete_storage: bool = False) -> None:
            raise DomainNotFound("vanished mid-delete")

    with pytest.raises(DomainNotFound):
        delete_mod.delete(_Backend(), DomainId("vm1"))
