# #region Test.CleanRelease.CandidateService [C:3] [TYPE Module] [SEMANTICS test,clean-release,candidate,service]
# @BRIEF Unit tests for candidate_service — _validate_artifacts, register_candidate.
# @RELATION BINDS_TO -> [candidate_service]
# @TEST_EDGE: empty_artifacts -> ValueError
# @TEST_EDGE: missing_id_field -> ValueError
# @TEST_EDGE: empty_id -> ValueError
# @TEST_EDGE: non_positive_size -> ValueError
# @TEST_EDGE: duplicate_candidate -> ValueError

from pathlib import Path
import sys

sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))

from unittest.mock import MagicMock
import pytest


# ── _validate_artifacts ──

class TestValidateArtifacts:
    def test_valid_artifacts(self):
        from src.services.clean_release.candidate_service import _validate_artifacts
        artifacts = [
            {"id": "art-1", "path": "/tmp/a.tar.gz", "sha256": "abc123", "size": 1024},
        ]
        result = _validate_artifacts(artifacts)
        assert len(result) == 1
        assert result[0]["id"] == "art-1"

    def test_empty_artifacts(self):
        from src.services.clean_release.candidate_service import _validate_artifacts
        with pytest.raises(ValueError, match="artifacts must not be empty"):
            _validate_artifacts([])

    def test_non_dict_artifact(self):
        from src.services.clean_release.candidate_service import _validate_artifacts
        with pytest.raises(ValueError, match="artifact\\[0\\] must be an object"):
            _validate_artifacts(["string"])

    def test_missing_required_field(self):
        from src.services.clean_release.candidate_service import _validate_artifacts
        with pytest.raises(ValueError, match="missing required field 'path'"):
            _validate_artifacts([{"id": "a1", "sha256": "s", "size": 1}])

    def test_empty_id(self):
        from src.services.clean_release.candidate_service import _validate_artifacts
        with pytest.raises(ValueError, match="field 'id' must be non-empty"):
            _validate_artifacts([{"id": "", "path": "/p", "sha256": "s", "size": 1}])

    def test_empty_path(self):
        from src.services.clean_release.candidate_service import _validate_artifacts
        with pytest.raises(ValueError, match="field 'path' must be non-empty"):
            _validate_artifacts([{"id": "a1", "path": "", "sha256": "s", "size": 1}])

    def test_empty_sha256(self):
        from src.services.clean_release.candidate_service import _validate_artifacts
        with pytest.raises(ValueError, match="field 'sha256' must be non-empty"):
            _validate_artifacts([{"id": "a1", "path": "/p", "sha256": "", "size": 1}])

    def test_non_positive_size(self):
        from src.services.clean_release.candidate_service import _validate_artifacts
        with pytest.raises(ValueError, match="field 'size' must be a positive integer"):
            _validate_artifacts([{"id": "a1", "path": "/p", "sha256": "s", "size": 0}])

    def test_size_not_int(self):
        from src.services.clean_release.candidate_service import _validate_artifacts
        with pytest.raises(ValueError, match="field 'size' must be a positive integer"):
            _validate_artifacts([{"id": "a1", "path": "/p", "sha256": "s", "size": "not-int"}])


# ── register_candidate ──

class TestRegisterCandidateService:
    def test_register_success(self):
        repo = MagicMock()
        repo.get_candidate.return_value = None

        from src.services.clean_release.candidate_service import register_candidate
        result = register_candidate(
            repository=repo,
            candidate_id="cand-1",
            version="1.0.0",
            source_snapshot_ref="main",
            created_by="admin",
            artifacts=[{"id": "a1", "path": "/p", "sha256": "s", "size": 1}],
        )
        assert result is not None
        assert result.id == "cand-1"
        assert repo.save_candidate.call_count == 2  # DRAFT then PREPARED
        assert repo.save_artifact.call_count == 1

    def test_register_empty_candidate_id(self):
        repo = MagicMock()
        from src.services.clean_release.candidate_service import register_candidate
        with pytest.raises(ValueError, match="candidate_id must be non-empty"):
            register_candidate(repo, "", "1.0", "main", "admin", [])

    def test_register_empty_version(self):
        repo = MagicMock()
        from src.services.clean_release.candidate_service import register_candidate
        with pytest.raises(ValueError, match="version must be non-empty"):
            register_candidate(repo, "cand-1", "", "main", "admin", [])

    def test_register_empty_source_ref(self):
        repo = MagicMock()
        from src.services.clean_release.candidate_service import register_candidate
        with pytest.raises(ValueError, match="source_snapshot_ref must be non-empty"):
            register_candidate(repo, "cand-1", "1.0", "", "admin", [])

    def test_register_empty_created_by(self):
        repo = MagicMock()
        from src.services.clean_release.candidate_service import register_candidate
        with pytest.raises(ValueError, match="created_by must be non-empty"):
            register_candidate(repo, "cand-1", "1.0", "main", "", [])

    def test_register_duplicate(self):
        repo = MagicMock()
        repo.get_candidate.return_value = MagicMock(id="cand-1")

        from src.services.clean_release.candidate_service import register_candidate
        with pytest.raises(ValueError, match="already exists"):
            register_candidate(repo, "cand-1", "1.0", "main", "admin", [{"id": "a1", "path": "/p", "sha256": "s", "size": 1}])
# #endregion Test.CleanRelease.CandidateService
