"""plan / apply / destroy against mgf-cloud's in-memory FakeProvider."""

from __future__ import annotations

import pytest
from mgf.cloud.models import OWNER_TAG
from mgf.cloud.providers import get_provider

from mgf.cloud_provision import ServiceSpec, apply, destroy, plan, to_server_spec
from mgf.cloud_provision.exceptions import ProvisionError
from mgf.cloud_provision.models import Action


def _spec(**over: object) -> ServiceSpec:
    base: dict[str, object] = {
        "name": "svc-1",
        "flavor": "a2-ram4-disk40",
        "image": "Ubuntu 24.04 LTS",
    }
    base.update(over)
    return ServiceSpec(**base)  # type: ignore[arg-type]


def test_plan_create_on_empty_cloud() -> None:
    decision = plan(_spec(), get_provider("fake"))
    assert decision.action is Action.CREATE
    assert decision.current_id is None


def test_apply_creates_then_is_idempotent() -> None:
    provider = get_provider("fake")
    result = apply(_spec(), provider)
    assert result.action is Action.CREATE
    assert result.server_id
    assert result.ip_addresses  # fake assigns a private (ext-net1 ⇒ public) IP

    assert plan(_spec(), provider).action is Action.NOOP
    again = apply(_spec(), provider)
    assert again.action is Action.NOOP
    assert again.server_id == result.server_id


def test_drift_is_replace_and_gated_by_allow_replace() -> None:
    provider = get_provider("fake")
    apply(_spec(flavor="a2-ram2-disk20"), provider)
    drifted = _spec(flavor="a2-ram4-disk40")  # different flavour ⇒ drift

    assert plan(drifted, provider).action is Action.REPLACE
    with pytest.raises(ProvisionError):
        apply(drifted, provider)  # destructive — refused without allow_replace

    recreated = apply(drifted, provider, allow_replace=True)
    assert recreated.action is Action.REPLACE
    assert recreated.server_id


def test_image_drift_detected() -> None:
    provider = get_provider("fake")
    apply(_spec(image="Debian 12"), provider)
    assert plan(_spec(image="Ubuntu 24.04 LTS"), provider).action is Action.REPLACE


def test_destroy_absent_then_present() -> None:
    provider = get_provider("fake")
    assert destroy("svc-1", provider) is False
    apply(_spec(), provider)
    assert destroy("svc-1", provider) is True
    assert plan(_spec(), provider).action is Action.CREATE


def test_to_server_spec_tags_owner_and_carries_fields() -> None:
    server_spec = to_server_spec(_spec(owner="mgf-cloud-provision", assign_floating_ip=True))
    assert server_spec.metadata[OWNER_TAG] == "mgf-cloud-provision"
    assert server_spec.assign_floating_ip is True
    assert server_spec.name == "svc-1"
