"""Tests for the unattended spec loader (:mod:`mgf.vm.core.creator.unattended`)."""

from __future__ import annotations

from typing import TYPE_CHECKING

import pytest

from mgf.vm.core.creator import unattended
from mgf.vm.core.exceptions import SpecValidationError

if TYPE_CHECKING:
    from pathlib import Path

    from tests.conftest import FakeProvider


class TestLoadAndValidate:
    def test_valid_spec_file_parses_and_validates(self, tmp_path: Path) -> None:
        spec_path = tmp_path / "web.yaml"
        spec_path.write_text(
            """
name: web-01
vcpus: 4
ram_mb: 4096
disk_gb: 30
source:
  type: cloud_image
  url: https://example/u.img
nics:
  - network: default
    model: virtio
""".lstrip()
        )

        spec = unattended.load_and_validate(spec_path)
        assert spec.name == "web-01"
        assert spec.vcpus == 4
        assert spec.ram_mb == 4096

    def test_validation_errors_collected(self, tmp_path: Path) -> None:
        spec_path = tmp_path / "bad.yaml"
        spec_path.write_text(
            """
name: bad name
vcpus: 0
ram_mb: 64
disk_gb: 0
source:
  type: cloud_image
""".lstrip()
        )
        with pytest.raises(SpecValidationError) as exc_info:
            unattended.load_and_validate(spec_path)
        errors = exc_info.value.errors
        assert any("name" in e for e in errors)
        assert any("vcpus" in e for e in errors)
        assert any("ram_mb" in e for e in errors)
        assert any("disk_gb" in e for e in errors)

    def test_missing_file_raises_spec_validation_error(self, tmp_path: Path) -> None:
        with pytest.raises(SpecValidationError, match="could not read"):
            unattended.load_and_validate(tmp_path / "nope.yaml")

    def test_provider_uniqueness_check(
        self, tmp_path: Path, fake_provider_cls: type[FakeProvider]
    ) -> None:
        spec_path = tmp_path / "web.yaml"
        spec_path.write_text(
            """
name: web
vcpus: 2
ram_mb: 2048
disk_gb: 20
source:
  type: cloud_image
  url: https://example/u.img
""".lstrip()
        )
        provider = fake_provider_cls()
        provider.seed_domain("web")

        with pytest.raises(SpecValidationError, match="already exists"):
            unattended.load_and_validate(spec_path, provider)

    def test_lenient_source_skips_path_check(self, tmp_path: Path) -> None:
        spec_path = tmp_path / "iso.yaml"
        spec_path.write_text(
            f"""
name: ghost-iso
vcpus: 2
ram_mb: 2048
disk_gb: 20
source:
  type: iso
  path: {tmp_path / "does-not-exist.iso"}
""".lstrip()
        )
        # lenient_source=True accepts a non-existent path.
        spec = unattended.load_and_validate(spec_path, lenient_source=True)
        assert spec.name == "ghost-iso"


class TestLintNoRaise:
    def test_clean_spec_returns_empty_list(self, tmp_path: Path) -> None:
        spec_path = tmp_path / "good.yaml"
        spec_path.write_text(
            """
name: good
vcpus: 2
ram_mb: 2048
disk_gb: 20
source:
  type: cloud_image
  url: https://example/u.img
""".lstrip()
        )
        assert unattended.lint(spec_path) == []

    def test_bad_spec_returns_error_list(self, tmp_path: Path) -> None:
        spec_path = tmp_path / "bad.yaml"
        spec_path.write_text("name: bad name\nram_mb: 64\n")
        errors = unattended.lint(spec_path)
        assert errors
        assert any("name" in e for e in errors)


class TestLoad:
    def test_load_without_validation(self, tmp_path: Path) -> None:
        spec_path = tmp_path / "web.yaml"
        # Intentionally invalid RAM — `load` does not validate.
        spec_path.write_text("name: web\nram_mb: 64\n")
        spec = unattended.load(spec_path)
        assert spec.ram_mb == 64
