"""Tests for the SpecPack compiler."""
import json
from pathlib import Path

import pytest

from abi_spec.compiler import compile_prd_json_to_specpack, validate_specpack

FIXTURES = Path(__file__).resolve().parent.parent / "fixtures"


class TestCompile:
    def test_basic_compile(self, tmp_path):
        result = compile_prd_json_to_specpack(FIXTURES / "prd.json", tmp_path / "out")
        assert "manifest_hash" in result
        assert "prd_hash" in result
        assert len(result["manifest_hash"]) == 64
        assert len(result["prd_hash"]) == 64

    def test_output_files_created(self, tmp_path):
        out = tmp_path / "out"
        compile_prd_json_to_specpack(FIXTURES / "prd.json", out)
        assert (out / "spec_manifest.json").exists()
        assert (out / "hashes.json").exists()

    def test_manifest_schema_valid(self, tmp_path):
        out = tmp_path / "out"
        compile_prd_json_to_specpack(FIXTURES / "prd.json", out)
        manifest = json.loads((out / "spec_manifest.json").read_text(encoding="utf-8"))
        assert manifest["spec_version"] == "1.0"
        assert manifest["spec_id"].startswith("spec-")
        assert len(manifest["prd_hash"]) == 64
        assert len(manifest["requirements"]) == 2

    def test_hashes_json_content(self, tmp_path):
        out = tmp_path / "out"
        result = compile_prd_json_to_specpack(FIXTURES / "prd.json", out)
        hashes = json.loads((out / "hashes.json").read_text(encoding="utf-8"))
        assert hashes["manifest_hash"] == result["manifest_hash"]
        assert hashes["prd_hash"] == result["prd_hash"]

    def test_invalid_prd_no_requirements(self, tmp_path):
        bad_prd = tmp_path / "bad.json"
        bad_prd.write_text('{"not_requirements": []}', encoding="utf-8")
        with pytest.raises(ValueError, match="requirements"):
            compile_prd_json_to_specpack(bad_prd, tmp_path / "out")

    def test_invalid_prd_not_object(self, tmp_path):
        bad_prd = tmp_path / "bad.json"
        bad_prd.write_text("[1, 2, 3]", encoding="utf-8")
        with pytest.raises(ValueError, match="requirements"):
            compile_prd_json_to_specpack(bad_prd, tmp_path / "out")

    def test_creates_output_directory(self, tmp_path):
        out = tmp_path / "deep" / "nested" / "out"
        compile_prd_json_to_specpack(FIXTURES / "prd.json", out)
        assert out.is_dir()


class TestValidate:
    def test_valid_specpack(self, tmp_path):
        out = tmp_path / "out"
        compile_prd_json_to_specpack(FIXTURES / "prd.json", out)
        assert validate_specpack(out) is True

    def test_missing_manifest(self, tmp_path):
        with pytest.raises(ValueError, match="Missing spec_manifest.json"):
            validate_specpack(tmp_path)

    def test_missing_hashes(self, tmp_path):
        (tmp_path / "spec_manifest.json").write_text("{}", encoding="utf-8")
        with pytest.raises(ValueError, match="Missing hashes.json"):
            validate_specpack(tmp_path)

    def test_tampered_manifest(self, tmp_path):
        out = tmp_path / "out"
        compile_prd_json_to_specpack(FIXTURES / "prd.json", out)
        # Tamper with manifest
        manifest_path = out / "spec_manifest.json"
        manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
        manifest["spec_id"] = "spec-tampered"
        manifest_path.write_text(json.dumps(manifest), encoding="utf-8")
        with pytest.raises(ValueError, match="hash mismatch"):
            validate_specpack(out)
