"""Tests for complyform.core.batch.manifest."""

from __future__ import annotations

from typing import TYPE_CHECKING

import pytest

if TYPE_CHECKING:
    from pathlib import Path
import yaml

from complyform.core.batch.manifest import (
    BatchProject,
    load_batch_manifest,
)
from complyform.core.errors import (
    BatchDuplicateLabelsError,
    BatchManifestInvalidError,
    BatchManifestNotFoundError,
    BatchSizeLimitError,
)


def _write_manifest(path: Path, data: dict[str, object]) -> None:
    path.write_text(yaml.dump(data), encoding="utf-8")


def _valid_manifest(n: int = 3) -> dict[str, object]:
    projects = []
    for i in range(n):
        projects.append(
            {
                "label": f"project-{i}",
                "cloud": "gcp",
                "state": f"./state_{i}.tfstate",
            }
        )
    return {"version": "1", "projects": projects}


class TestLoadBatchManifest:
    def test_valid_manifest(self, tmp_path: Path) -> None:
        path = tmp_path / "manifest.yaml"
        _write_manifest(path, _valid_manifest(3))
        manifest = load_batch_manifest(path)
        assert len(manifest.projects) == 3
        assert manifest.projects[0].label == "project-0"

    def test_missing_file_raises(self, tmp_path: Path) -> None:
        with pytest.raises(BatchManifestNotFoundError):
            load_batch_manifest(tmp_path / "nope.yaml")

    def test_invalid_yaml_raises(self, tmp_path: Path) -> None:
        path = tmp_path / "bad.yaml"
        path.write_text("::invalid yaml{{", encoding="utf-8")
        with pytest.raises(BatchManifestInvalidError):
            load_batch_manifest(path)

    def test_too_many_projects_raises(self, tmp_path: Path) -> None:
        path = tmp_path / "big.yaml"
        _write_manifest(path, _valid_manifest(11))
        with pytest.raises(BatchSizeLimitError) as exc_info:
            load_batch_manifest(path)
        assert exc_info.value.error_code == 15

    def test_duplicate_labels_raises(self, tmp_path: Path) -> None:
        data = {
            "version": "1",
            "projects": [
                {"label": "same", "cloud": "gcp", "state": "./a.tfstate"},
                {"label": "same", "cloud": "aws", "state": "./b.tfstate"},
            ],
        }
        path = tmp_path / "dupes.yaml"
        _write_manifest(path, data)
        with pytest.raises(BatchDuplicateLabelsError):
            load_batch_manifest(path)

    def test_missing_required_fields_raises(self, tmp_path: Path) -> None:
        data = {
            "version": "1",
            "projects": [{"label": "ok", "state": "./a.tfstate"}],
        }
        path = tmp_path / "bad_fields.yaml"
        _write_manifest(path, data)
        with pytest.raises(BatchManifestInvalidError):
            load_batch_manifest(path)

    def test_invalid_cloud_raises(self, tmp_path: Path) -> None:
        data = {
            "version": "1",
            "projects": [
                {"label": "proj", "cloud": "alibaba", "state": "./a.tfstate"},
            ],
        }
        path = tmp_path / "bad_cloud.yaml"
        _write_manifest(path, data)
        with pytest.raises(BatchManifestInvalidError):
            load_batch_manifest(path)

    def test_empty_frameworks_raises(self, tmp_path: Path) -> None:
        data = {
            "version": "1",
            "projects": [
                {
                    "label": "proj",
                    "cloud": "gcp",
                    "state": "./a.tfstate",
                    "frameworks": [],
                },
            ],
        }
        path = tmp_path / "empty_fw.yaml"
        _write_manifest(path, data)
        with pytest.raises(BatchManifestInvalidError):
            load_batch_manifest(path)

    def test_null_frameworks_valid(self, tmp_path: Path) -> None:
        data = {
            "version": "1",
            "projects": [
                {
                    "label": "proj",
                    "cloud": "gcp",
                    "state": "./a.tfstate",
                    "frameworks": None,
                },
            ],
        }
        path = tmp_path / "null_fw.yaml"
        _write_manifest(path, data)
        manifest = load_batch_manifest(path)
        assert manifest.projects[0].frameworks is None

    def test_label_with_space_raises(self, tmp_path: Path) -> None:
        data = {
            "version": "1",
            "projects": [
                {"label": " bad", "cloud": "gcp", "state": "./a.tfstate"},
            ],
        }
        path = tmp_path / "bad_label.yaml"
        _write_manifest(path, data)
        with pytest.raises(BatchManifestInvalidError):
            load_batch_manifest(path)


class TestBatchProject:
    def test_valid_project(self) -> None:
        p = BatchProject(label="my-proj", cloud="gcp", state="./state.tfstate")
        assert p.label == "my-proj"
        assert p.cloud == "gcp"

    def test_label_starts_with_digit(self) -> None:
        p = BatchProject(label="123-ok", cloud="aws", state="./s.tfstate")
        assert p.label == "123-ok"
