from __future__ import annotations

import json
import zipfile

import pytest

from backplane_runtime.contracts import CardManifest, Lane
from backplane_runtime.errors import PackageVerificationError
from backplane_runtime.packages import (
    PackageTrustPolicy,
    build_package,
    keygen,
    load_package,
)


def manifest() -> CardManifest:
    return CardManifest(
        id="policy.test",
        name="Test policy",
        version="1.2.3",
        kind="policy",
        reads=(Lane.EVENT,),
        writes=(Lane.VERIFY, Lane.OUTPUT),
        capabilities=("policy",),
    )


def test_signed_package_roundtrip_and_trust(tmp_path):
    private = tmp_path / "private.pem"
    public = tmp_path / "public.pem"
    package_path = tmp_path / "policy.bpcard"
    keygen(private, public)
    built = build_package(
        manifest(),
        {"deny_phrases": ["leak secrets"]},
        package_path,
        private,
    )
    loaded = load_package(
        package_path,
        PackageTrustPolicy(
            trusted_fingerprints=frozenset({built.signer_fingerprint})
        ),
    )
    assert loaded.manifest.id == "policy.test"
    assert loaded.config["deny_phrases"] == ["leak secrets"]
    assert loaded.digest_sha256 == built.digest_sha256


def test_tampering_is_rejected(tmp_path):
    private = tmp_path / "private.pem"
    public = tmp_path / "public.pem"
    original = tmp_path / "original.bpcard"
    tampered = tmp_path / "tampered.bpcard"
    keygen(private, public)
    build_package(manifest(), {"instruction": "safe"}, original, private)
    with zipfile.ZipFile(original) as source, zipfile.ZipFile(tampered, "w") as target:
        for name in source.namelist():
            body = source.read(name)
            if name == "config.json":
                body = json.dumps({"instruction": "unsafe"}).encode()
            target.writestr(name, body)
    with pytest.raises(PackageVerificationError, match="checksum mismatch"):
        load_package(tampered)


def test_executable_archive_member_is_rejected(tmp_path):
    private = tmp_path / "private.pem"
    public = tmp_path / "public.pem"
    original = tmp_path / "original.bpcard"
    malicious = tmp_path / "malicious.bpcard"
    keygen(private, public)
    build_package(manifest(), {}, original, private)
    with zipfile.ZipFile(original) as source, zipfile.ZipFile(malicious, "w") as target:
        for name in source.namelist():
            target.writestr(name, source.read(name))
        target.writestr("driver.py", b"raise SystemExit")
    with pytest.raises(PackageVerificationError, match="invalid package contents"):
        load_package(malicious)


def test_untrusted_signer_is_rejected(tmp_path):
    private = tmp_path / "private.pem"
    public = tmp_path / "public.pem"
    package_path = tmp_path / "policy.bpcard"
    keygen(private, public)
    build_package(manifest(), {}, package_path, private)
    with pytest.raises(PackageVerificationError, match="not trusted"):
        load_package(
            package_path,
            PackageTrustPolicy(trusted_fingerprints=frozenset({"0" * 64})),
        )

