"""Tests for CLR type validation."""

import pytest
from pydantic import ValidationError

from timeback_clr.types.inputs import (
    ClrCredentialInput,
)
from timeback_clr.types.responses import ClrCredential


def _minimal_credential_dict() -> dict:
    """Build a minimal valid CLR credential dict."""
    return {
        "@context": [
            "https://www.w3.org/ns/credentials/v2",
            "https://purl.imsglobal.org/spec/clr/v2p0/context-2.0.1",
            "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.jsonld",
        ],
        "id": "urn:uuid:test-credential",
        "type": ["VerifiableCredential", "ClrCredential"],
        "issuer": {
            "id": "https://example.edu",
            "type": ["Profile"],
            "name": "Test University",
        },
        "name": "Test Credential",
        "validFrom": "2024-01-01T00:00:00Z",
        "credentialSubject": {
            "type": ["ClrSubject"],
            "verifiableCredential": [
                {
                    "@context": [
                        "https://www.w3.org/ns/credentials/v2",
                        "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json",
                    ],
                    "id": "urn:uuid:nested-credential",
                    "type": ["VerifiableCredential", "AchievementCredential"],
                    "issuer": {
                        "id": "https://example.edu",
                        "type": ["Profile"],
                    },
                    "validFrom": "2024-01-01T00:00:00Z",
                    "credentialSubject": {},
                },
            ],
        },
    }


class TestClrCredentialInput:
    """Tests for ClrCredentialInput validation."""

    def test_valid_minimal_credential(self):
        """Should accept a valid minimal credential."""
        data = _minimal_credential_dict()
        model = ClrCredentialInput.model_validate(data)
        assert model.id == "urn:uuid:test-credential"
        assert model.name == "Test Credential"

    def test_valid_with_optional_fields(self):
        """Should accept a credential with optional fields."""
        data = _minimal_credential_dict()
        data["description"] = "A test credential"
        data["validUntil"] = "2030-12-31T23:59:59Z"
        model = ClrCredentialInput.model_validate(data)
        assert model.description == "A test credential"
        assert model.valid_until == "2030-12-31T23:59:59Z"

    def test_reject_empty_id(self):
        """Should reject credential with empty id."""
        data = _minimal_credential_dict()
        data["id"] = ""
        with pytest.raises(ValidationError):
            ClrCredentialInput.model_validate(data)

    def test_reject_empty_name(self):
        """Should reject credential with empty name."""
        data = _minimal_credential_dict()
        data["name"] = ""
        with pytest.raises(ValidationError):
            ClrCredentialInput.model_validate(data)

    def test_reject_empty_type_array(self):
        """Should reject credential with empty type array."""
        data = _minimal_credential_dict()
        data["type"] = []
        with pytest.raises(ValidationError):
            ClrCredentialInput.model_validate(data)

    def test_reject_empty_context(self):
        """Should reject credential with empty @context."""
        data = _minimal_credential_dict()
        data["@context"] = []
        with pytest.raises(ValidationError):
            ClrCredentialInput.model_validate(data)

    def test_reject_empty_valid_from(self):
        """Should reject credential with empty validFrom."""
        data = _minimal_credential_dict()
        data["validFrom"] = ""
        with pytest.raises(ValidationError):
            ClrCredentialInput.model_validate(data)

    def test_reject_missing_issuer_id(self):
        """Should reject credential with empty issuer id."""
        data = _minimal_credential_dict()
        data["issuer"]["id"] = ""
        with pytest.raises(ValidationError):
            ClrCredentialInput.model_validate(data)

    def test_reject_empty_issuer_type(self):
        """Should reject credential with empty issuer type array."""
        data = _minimal_credential_dict()
        data["issuer"]["type"] = []
        with pytest.raises(ValidationError):
            ClrCredentialInput.model_validate(data)

    def test_reject_empty_verifiable_credential_array(self):
        """Should reject credential subject with empty verifiableCredential."""
        data = _minimal_credential_dict()
        data["credentialSubject"]["verifiableCredential"] = []
        with pytest.raises(ValidationError):
            ClrCredentialInput.model_validate(data)

    def test_reject_empty_subject_type(self):
        """Should reject credential subject with empty type array."""
        data = _minimal_credential_dict()
        data["credentialSubject"]["type"] = []
        with pytest.raises(ValidationError):
            ClrCredentialInput.model_validate(data)

    def test_reject_nested_empty_context(self):
        """Should reject nested credential with empty @context."""
        data = _minimal_credential_dict()
        data["credentialSubject"]["verifiableCredential"][0]["@context"] = []
        with pytest.raises(ValidationError):
            ClrCredentialInput.model_validate(data)

    def test_reject_nested_empty_id(self):
        """Should reject nested credential with empty id."""
        data = _minimal_credential_dict()
        data["credentialSubject"]["verifiableCredential"][0]["id"] = ""
        with pytest.raises(ValidationError):
            ClrCredentialInput.model_validate(data)

    def test_serialization_round_trip(self):
        """Should serialize and deserialize correctly."""
        data = _minimal_credential_dict()
        model = ClrCredentialInput.model_validate(data)
        dumped = model.model_dump(mode="json", by_alias=True, exclude_none=True)
        assert dumped["@context"] == data["@context"]
        assert dumped["id"] == data["id"]
        assert dumped["name"] == data["name"]
        assert dumped["issuer"]["id"] == data["issuer"]["id"]
        assert (
            dumped["credentialSubject"]["verifiableCredential"][0]["id"]
            == (data["credentialSubject"]["verifiableCredential"][0]["id"])
        )


class TestClrCredentialResponse:
    """Tests for ClrCredential response model."""

    def test_parse_response(self):
        """Should parse a full credential response."""
        data = _minimal_credential_dict()
        model = ClrCredential.model_validate(data)
        assert model.id == "urn:uuid:test-credential"
        assert model.issuer.name == "Test University"
        assert len(model.credential_subject.verifiable_credential) == 1

    def test_parse_with_proof(self):
        """Should parse response with proof field."""
        data = _minimal_credential_dict()
        data["proof"] = [
            {
                "type": "DataIntegrityProof",
                "proofPurpose": "assertionMethod",
                "verificationMethod": "did:example:123#key-1",
                "created": "2024-01-01T00:00:00Z",
                "proofValue": "z123abc...",
            }
        ]
        model = ClrCredential.model_validate(data)
        assert model.proof is not None
        assert len(model.proof) == 1
        assert model.proof[0].type == "DataIntegrityProof"
