"""Shared-claim consistency between ACT and ECT refimpls.

Covers the claims both specs declare (jti, wid, iat, exp, aud,
exec_act, pred, inp_hash, out_hash): verifies that identical wire
values are accepted and round-trip cleanly.
"""

from __future__ import annotations

import base64
import hashlib
import uuid

import pytest

from act.crypto import b64url_sha256
from act.token import ACTRecord
from ect.types import Payload
from ect.validate import validate_hash_format


# --- pred array semantics ----------------------------------------------------


class TestPredArray:
    @pytest.mark.parametrize(
        "pred",
        [
            [],
            [str(uuid.uuid4())],
            [str(uuid.uuid4()), str(uuid.uuid4())],
        ],
    )
    def test_pred_roundtrips_on_both(self, act_record_builder, ect_payload_builder, pred):
        """Same pred array shape is serialised identically by both refimpls."""
        act_compact, act_rec = act_record_builder(pred=list(pred))
        ect_compact, ect_pl = ect_payload_builder(pred=list(pred))

        assert act_rec.pred == pred
        assert ect_pl.pred == pred

        # Decode and compare the on-wire pred arrays.
        from act.token import decode_jws

        _, act_claims, _, _ = decode_jws(act_compact)
        assert act_claims["pred"] == pred

        import jwt

        ect_claims = jwt.decode(
            ect_compact, options={"verify_signature": False, "verify_exp": False}
        )
        assert ect_claims["pred"] == pred


# --- jti / wid scoping -------------------------------------------------------


class TestJtiWidScoping:
    def test_same_uuid_accepted_independently(self, act_record_builder, ect_payload_builder):
        """The same jti UUID is accepted by both refimpls independently.

        jti uniqueness is scoped per-store in each refimpl; there is no
        shared namespace. This test documents that reusing a jti across
        token types is not inherently rejected.
        """
        shared_jti = str(uuid.uuid4())
        act_compact, _ = act_record_builder(jti=shared_jti)
        ect_compact, _ = ect_payload_builder(jti=shared_jti)
        assert act_compact and ect_compact

    def test_same_wid_accepted_by_both(self, act_record_builder, ect_payload_builder):
        """Identical wid value is preserved on both sides."""
        shared_wid = str(uuid.uuid4())
        _, act_rec = act_record_builder(wid=shared_wid)
        _, ect_pl = ect_payload_builder(wid=shared_wid)
        assert act_rec.wid == shared_wid == ect_pl.wid


# --- inp_hash / out_hash format (both now plain base64url) -------------------


class TestHashFormat:
    def test_act_b64url_sha256_is_valid_for_ect(self):
        """ACT's b64url_sha256() output MUST pass ECT's validate_hash_format.

        The ECT validator was aligned to plain base64url to match ACT —
        this test would break if either side ever drifts.
        """
        h = b64url_sha256(b"interop-payload")
        # Should not raise.
        validate_hash_format(h)

    def test_ect_rejects_prefixed_hash(self):
        """Prefixed form (sha-256:...) is explicitly rejected by ECT now."""
        digest = b64url_sha256(b"interop-payload")
        with pytest.raises(ValueError, match="plain base64url"):
            validate_hash_format(f"sha-256:{digest}")

    def test_act_and_ect_agree_on_hash_shape(self):
        """Identical raw bytes produce the same base64url hash on both sides."""
        raw = b"same bytes hashed on both sides"
        act_hash = b64url_sha256(raw)
        # Recompute independently.
        expected = (
            base64.urlsafe_b64encode(hashlib.sha256(raw).digest())
            .rstrip(b"=")
            .decode("ascii")
        )
        assert act_hash == expected
        validate_hash_format(act_hash)  # ECT accepts.


# --- exec_act ----------------------------------------------------------------


class TestExecAct:
    @pytest.mark.parametrize("value", ["read", "read.data", "write.result"])
    def test_exec_act_string_shared(self, act_record_builder, ect_payload_builder, value):
        """ACT-grammar-legal exec_act values are accepted unchanged by ECT."""
        _, act_rec = act_record_builder(exec_act=value, cap_actions=[value])
        _, ect_pl = ect_payload_builder(exec_act=value)
        assert act_rec.exec_act == value == ect_pl.exec_act
