"""Opt-in LIVE Infomaniak integration test — creates + destroys one nano VM.

This talks to the real Infomaniak Public Cloud and **costs money**, so it is
double-gated and skipped by default:

* ``MGF_CLOUD_LIVE=1`` must be set (an explicit "yes, hit the cloud"), and
* openstacksdk + resolvable credentials must be present.

Run it deliberately::

    export MGF_CLOUD_LIVE=1
    export MGF_CLOUD_CLOUDS_YAML=$PWD/my_stuff/infomaniak/PCU-HOESFJV-clouds.yaml
    .venv/bin/python -m pytest tests/integration -m integration \
        -o addopts="" -p no:cacheprovider

It allocates the smallest flavour, waits for ACTIVE, then tears everything down
in a ``finally`` so a failure never leaks a billable VM. The created resources
are tagged ``mgf:owner=mgf-cloud-itest`` so a stray one is easy to spot.
"""

from __future__ import annotations

import contextlib
import os
import time

import pytest

pytest.importorskip("openstack")

from mgf.cloud import (
    config,
    defaults,
)
from mgf.cloud.exceptions import CredentialsError, ResourceNotFoundError
from mgf.cloud.models import ServerSpec, owned_metadata
from mgf.cloud.providers.infomaniak import InfomaniakProvider

_LIVE = os.environ.get("MGF_CLOUD_LIVE") == "1"
_ITEST_OWNER = "mgf-cloud-itest"

pytestmark = [
    pytest.mark.integration,
    pytest.mark.skipif(not _LIVE, reason="set MGF_CLOUD_LIVE=1 to run the live nano-VM test"),
    # openstacksdk emits its own deprecation warnings just on Connection
    # construction (RemovedInSDK60Warning derives from PendingDeprecationWarning,
    # the others from OpenStackWarning); the suite's filterwarnings=error policy
    # would turn those third-party warnings into spurious failures. Nothing we can
    # act on, so ignore the SDK's warning families for the live tests only.
    pytest.mark.filterwarnings("ignore::openstack.warnings.OpenStackWarning"),
    pytest.mark.filterwarnings("ignore::openstack.warnings.RemovedInSDK50Warning"),
    pytest.mark.filterwarnings("ignore::openstack.warnings.RemovedInSDK60Warning"),
]


def _provider() -> InfomaniakProvider:
    # Honour the same cloud-name caveat the recipe documents: the downloaded file
    # names its cloud PCP-<id>-<region>, not 'openstack'. Allow an override.
    cloud = os.environ.get("MGF_CLOUD_NAME", "openstack")
    try:
        creds = config.load(cloud=cloud)
    except CredentialsError as exc:  # pragma: no cover - depends on the operator's env
        pytest.skip(f"no resolvable Infomaniak credentials: {exc}")
    return InfomaniakProvider(creds)


def test_live_catalogue_is_reachable() -> None:
    """Smoke: the project answers read-only catalogue queries."""
    provider = _provider()
    assert provider.list_flavors()
    assert provider.list_images()
    assert provider.list_networks()


@pytest.mark.timeout(600)  # a real boot takes minutes; well above the adapter's 300s wait
def test_live_nano_vm_create_and_destroy() -> None:
    """Full create→ACTIVE→destroy round-trip on the smallest flavour."""
    provider = _provider()
    name = "mgf-cloud-itest-vm"
    spec = ServerSpec(
        name=name,
        flavor=defaults.DEFAULT_FLAVOR,
        image=defaults.DEFAULT_IMAGE,
        network=defaults.DEFAULT_NETWORK,
        metadata=owned_metadata(_ITEST_OWNER),
    )
    try:
        server = provider.create_server(spec)
        assert server.status == "ACTIVE"
        assert provider.get_server(name).id == server.id
    finally:
        # Leak-safe teardown: delete by NAME unconditionally — if create_server
        # raised after the VM came up (e.g. a boot-wait timeout) it never returned,
        # so we must not depend on a bound `server` to clean up a billable VM.
        with contextlib.suppress(ResourceNotFoundError):
            provider.delete_server(name)
    # delete is asynchronous (the VM passes through DELETING) — poll until it
    # leaves the project listing rather than checking instantly.
    for _ in range(40):
        if all(s.name != name for s in provider.list_servers()):
            break
        time.sleep(3)
    else:
        pytest.fail(f"{name} still present ~120s after delete")
