from __future__ import annotations

from pathlib import Path

import pytest
from conftest import make_runtime
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey

from scopewrit.crypto import TrustStore, decode_token, encode_token, verify_envelope
from scopewrit.errors import ManifestError, ReplayError, SignatureError
from scopewrit.models import SignedEnvelope


def test_manifest_tamper_is_rejected(tmp_path: Path) -> None:
    runtime = make_runtime(tmp_path / "original")
    raw = runtime.envelope.model_dump(mode="json")
    raw["payload"]["objective"] = "Tampered objective that was never signed."
    unstarted = make_runtime(tmp_path / "other")
    with pytest.raises(ManifestError):
        unstarted.engine.start_run(SignedEnvelope.model_validate(raw))


def test_untrusted_embedded_identity_cannot_replace_trust_root(tmp_path: Path) -> None:
    runtime = make_runtime(tmp_path / "runtime")
    wrong = Ed25519PrivateKey.generate()
    payload = runtime.manifest.model_dump(mode="json")
    from scopewrit.crypto import sign_envelope

    forged = sign_envelope(payload, wrong, "principal-key")
    with pytest.raises(ManifestError):
        runtime.engine.start_run(forged)


def test_run_and_nonce_are_consume_once(tmp_path: Path) -> None:
    runtime = make_runtime(tmp_path / "runtime")
    with pytest.raises(ReplayError):
        runtime.engine.start_run(runtime.envelope)


def test_runtime_token_rejects_wrong_trust_and_tamper() -> None:
    signer = Ed25519PrivateKey.generate()
    token = encode_token({"kind": "test", "value": 7}, signer, "token-key")
    claims = decode_token(
        token,
        TrustStore({"token-key": signer.public_key()}),
        expected_kind="test",
    )
    assert claims["value"] == 7

    wrong = Ed25519PrivateKey.generate()
    with pytest.raises(SignatureError):
        decode_token(
            token,
            TrustStore({"token-key": wrong.public_key()}),
            expected_kind="test",
        )
    parts = token.split(".")
    parts[2] = parts[2][:-1] + ("A" if parts[2][-1] != "A" else "B")
    with pytest.raises(SignatureError):
        decode_token(
            ".".join(parts),
            TrustStore({"token-key": signer.public_key()}),
        )


def test_envelope_round_trip(runtime) -> None:
    payload = verify_envelope(
        runtime.envelope,
        TrustStore({"principal-key": runtime.principal_private.public_key()}),
    )
    assert payload["run_id"] == runtime.manifest.run_id
