"""Pure provisioning-input types consumed by the renderers.

The three renderers (cloud-init / preseed / kickstart) need only two
things from a VM definition: the VM ``name`` and a
:class:`ProvisioningSpec` describing first-boot configuration. This
module defines that minimal, pure, dependency-free shape.

Why not reuse :class:`mgf.vm.core.contracts.VmSpec`? That type is the
engine-neutral hypervisor contract (``name`` / ``cpu`` /
``memory_bytes`` / ``metadata``) — it deliberately carries no
first-boot provisioning data. The provisioning layer is a different
concern that sits above the hypervisor contract, so its input shape
lives here under its own name rather than shadowing the core
``VmSpec``. Adapters that already hold a richer spec (the legacy
``mgf.vm.creator.spec.VMSpec``) can pass it straight in — it is
structurally compatible (it exposes ``.name`` and ``.provisioning``).
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Protocol, runtime_checkable


@dataclass
class ProvisioningSpec:
    """Cloud-init / preseed / kickstart configuration for first-boot.

    A field-for-field pure port of the legacy
    ``mgf.vm.creator.spec.ProvisioningSpec``. Every field has a typed
    default so a bare ``ProvisioningSpec()`` renders a valid (if
    minimal) artefact.

    ``hostname``, ``username``, and ``password`` are ``None`` when the
    spec does not set them; the renderers fall back to the VM name /
    ``"vmanager"`` defaults documented per backend.
    """

    backend: str = "none"
    hostname: str | None = None
    username: str | None = None
    password: str | None = None
    ssh_authorized_keys: list[str] = field(default_factory=list)
    packages: list[str] = field(default_factory=list)
    run_commands: list[str] = field(default_factory=list)


@runtime_checkable
class ProvisioningInput(Protocol):
    """Structural shape the renderers require from a VM definition.

    Any object exposing a ``name`` string and a ``provisioning``
    :class:`ProvisioningSpec` satisfies this Protocol — including the
    legacy ``mgf.vm.creator.spec.VMSpec`` and the lightweight
    :class:`SimpleProvisioningInput` below. Declaring the input as a
    Protocol keeps the pure core decoupled from any one spec class.
    """

    @property
    def name(self) -> str: ...

    @property
    def provisioning(self) -> ProvisioningSpec: ...


@dataclass
class SimpleProvisioningInput:
    """Concrete :class:`ProvisioningInput` for callers/tests with no
    full ``VMSpec`` on hand."""

    name: str
    provisioning: ProvisioningSpec = field(default_factory=ProvisioningSpec)


__all__ = ["ProvisioningInput", "ProvisioningSpec", "SimpleProvisioningInput"]
