"""Tests for the attestation module (SD-JWT selective disclosure)."""

import json
import time

import pytest
from skytale_sdk.attestation import (
    Attestation,
    create_attestation,
    disclosed_claims,
    serialize_attestation,
    verify_attestation,
)
from skytale_sdk.identity import AgentIdentity


@pytest.fixture
def issuer():
    return AgentIdentity.generate()


@pytest.fixture
def subject():
    return AgentIdentity.generate()


class TestCreateAttestation:
    """create_attestation() produces signed, well-formed attestations."""

    def test_basic_attestation(self, issuer, subject):
        att = create_attestation(
            issuer, subject.did, {"task_score": 0.95},
        )
        assert att.issuer == issuer.did
        assert att.subject == subject.did
        assert att.claims == {"task_score": 0.95}
        assert att.signature is not None
        assert len(att.signature) == 64

    def test_all_claims_disclosed_by_default(self, issuer, subject):
        att = create_attestation(
            issuer, subject.did, {"a": 1, "b": 2},
        )
        assert set(att.disclosed_keys) == {"a", "b"}

    def test_selective_disclosure(self, issuer, subject):
        att = create_attestation(
            issuer, subject.did,
            {"task_score": 0.95, "secret_data": "hidden"},
            disclosed=["task_score"],
        )
        assert att.disclosed_keys == ["task_score"]

    def test_salts_generated_for_all_claims(self, issuer, subject):
        att = create_attestation(
            issuer, subject.did, {"a": 1, "b": 2, "c": 3},
        )
        assert set(att.salt_map.keys()) == {"a", "b", "c"}
        # Each salt is unique
        salts = list(att.salt_map.values())
        assert len(set(salts)) == 3

    def test_ttl_sets_expiry(self, issuer, subject):
        before = time.time()
        att = create_attestation(
            issuer, subject.did, {"x": 1}, ttl=3600,
        )
        assert att.expires_at is not None
        assert att.expires_at >= before + 3600
        assert att.expires_at <= time.time() + 3600


class TestVerifyAttestation:
    """verify_attestation() validates signatures and expiry."""

    def test_valid_attestation_verifies(self, issuer, subject):
        att = create_attestation(issuer, subject.did, {"score": 0.9})
        assert verify_attestation(att, issuer.public_key)

    def test_wrong_public_key_fails(self, issuer, subject):
        other = AgentIdentity.generate()
        att = create_attestation(issuer, subject.did, {"score": 0.9})
        assert not verify_attestation(att, other.public_key)

    def test_tampered_claims_fail(self, issuer, subject):
        att = create_attestation(issuer, subject.did, {"score": 0.9})
        att.claims["score"] = 1.0  # Tamper
        assert not verify_attestation(att, issuer.public_key)

    def test_tampered_subject_fails(self, issuer, subject):
        att = create_attestation(issuer, subject.did, {"score": 0.9})
        att.subject = "did:key:z6MkTampered"
        assert not verify_attestation(att, issuer.public_key)

    def test_no_signature_fails(self, issuer, subject):
        att = create_attestation(issuer, subject.did, {"x": 1})
        att.signature = None
        assert not verify_attestation(att, issuer.public_key)

    def test_expired_attestation_fails(self, issuer, subject):
        att = create_attestation(issuer, subject.did, {"x": 1}, ttl=0)
        # Force expiry into the past
        att.expires_at = time.time() - 1
        assert not verify_attestation(att, issuer.public_key)


class TestDisclosedClaims:
    """disclosed_claims() returns only visible claims."""

    def test_all_disclosed(self, issuer, subject):
        att = create_attestation(
            issuer, subject.did, {"a": 1, "b": "two"},
        )
        result = disclosed_claims(att)
        assert result == {"a": 1, "b": "two"}

    def test_partial_disclosure(self, issuer, subject):
        att = create_attestation(
            issuer, subject.did,
            {"visible": "yes", "hidden": "no"},
            disclosed=["visible"],
        )
        result = disclosed_claims(att)
        assert result["visible"] == "yes"
        assert result["hidden"].startswith("_sd:")

    def test_undisclosed_claims_are_hashed(self, issuer, subject):
        att = create_attestation(
            issuer, subject.did,
            {"public": 1, "private": 2},
            disclosed=["public"],
        )
        result = disclosed_claims(att)
        # Hash should be deterministic for same salt+key+value
        assert len(result["private"]) == 4 + 64  # "_sd:" + sha256 hex


class TestSerializeAttestation:
    """serialize_attestation() produces valid JSON for channel transmission."""

    def test_serialized_is_valid_json(self, issuer, subject):
        att = create_attestation(issuer, subject.did, {"score": 0.9})
        raw = serialize_attestation(att)
        parsed = json.loads(raw)
        assert parsed["type"] == "attestation"
        assert parsed["issuer"] == issuer.did
        assert parsed["subject"] == subject.did

    def test_serialized_contains_only_disclosed(self, issuer, subject):
        att = create_attestation(
            issuer, subject.did,
            {"public": "visible", "secret": "hidden"},
            disclosed=["public"],
        )
        raw = serialize_attestation(att)
        parsed = json.loads(raw)
        assert parsed["claims"]["public"] == "visible"
        assert parsed["claims"]["secret"].startswith("_sd:")

    def test_signature_is_base64_encoded(self, issuer, subject):
        import base64
        att = create_attestation(issuer, subject.did, {"x": 1})
        raw = serialize_attestation(att)
        parsed = json.loads(raw)
        sig_bytes = base64.b64decode(parsed["signature"])
        assert len(sig_bytes) == 64
