"""
CLR Input Types

Pydantic models for CLR API request validation.
Mirrors the CLR v2.0 Zod schemas from the TypeScript SDK.
"""

from __future__ import annotations

from typing import Literal

from pydantic import BaseModel, Field, field_validator

from timeback_common import NonEmptyString  # noqa: TC001

from .base import AssociationType  # noqa: TC001

W3C_CREDENTIALS_CONTEXT = "https://www.w3.org/ns/credentials/v2"


class ClrImageInput(BaseModel):
    """Image reference (IMS Global Image model)."""

    id: NonEmptyString
    type: Literal["Image"] = "Image"
    caption: str | None = None


class ClrProfileInput(BaseModel):
    """Profile (issuer or creator)."""

    id: NonEmptyString
    type: list[str] = Field(min_length=1)
    name: str | None = None
    url: str | None = None
    phone: str | None = None
    description: str | None = None
    image: ClrImageInput | None = None
    email: str | None = None


class ClrProofInput(BaseModel):
    """Cryptographic proof."""

    type: NonEmptyString
    proof_purpose: NonEmptyString = Field(alias="proofPurpose")
    verification_method: NonEmptyString = Field(alias="verificationMethod")
    created: NonEmptyString
    proof_value: NonEmptyString = Field(alias="proofValue")
    cryptosuite: str | None = None

    model_config = {"populate_by_name": True}


class ClrCredentialSchemaInput(BaseModel):
    """Credential schema reference."""

    id: NonEmptyString
    type: Literal["1EdTechJsonSchemaValidator2019"] = "1EdTechJsonSchemaValidator2019"


class AchievementCriteriaInput(BaseModel):
    """Achievement criteria."""

    id: str | None = None
    narrative: str | None = None


class AchievementInput(BaseModel):
    """Achievement."""

    id: NonEmptyString
    type: list[str] = Field(min_length=1)
    name: NonEmptyString
    description: NonEmptyString
    criteria: AchievementCriteriaInput
    image: ClrImageInput | None = None
    achievement_type: str | None = Field(None, alias="achievementType")
    creator: ClrProfileInput | None = None

    model_config = {"populate_by_name": True}


class IdentityObjectInput(BaseModel):
    """Identity object for the credential subject."""

    type: Literal["IdentityObject"] = "IdentityObject"
    identity_hash: NonEmptyString = Field(alias="identityHash")
    identity_type: NonEmptyString = Field(alias="identityType")
    hashed: bool
    salt: str | None = None

    model_config = {"populate_by_name": True}


class AssociationInput(BaseModel):
    """Association between achievements or credentials."""

    type: Literal["Association"] = "Association"
    association_type: AssociationType = Field(alias="associationType")
    source_id: NonEmptyString = Field(alias="sourceId")
    target_id: NonEmptyString = Field(alias="targetId")

    model_config = {"populate_by_name": True}


class NestedCredentialSubjectInput(BaseModel):
    """Credential subject for nested verifiable credentials."""

    id: str | None = None


class VerifiableCredentialInput(BaseModel):
    """Nested verifiable credential within a CLR."""

    context: list[str] = Field(min_length=1, alias="@context")
    id: NonEmptyString
    type: list[str] = Field(min_length=1)
    issuer: ClrProfileInput
    valid_from: NonEmptyString = Field(alias="validFrom")
    valid_until: str | None = Field(None, alias="validUntil")
    credential_subject: NestedCredentialSubjectInput = Field(alias="credentialSubject")
    proof: list[ClrProofInput] | None = None

    model_config = {"populate_by_name": True}


class ClrCredentialSubjectInput(BaseModel):
    """Credential subject containing achievements and nested credentials."""

    id: str | None = None
    type: list[str] = Field(min_length=1)
    identifier: list[IdentityObjectInput] | None = None
    achievement: list[AchievementInput] | None = None
    verifiable_credential: list[VerifiableCredentialInput] = Field(
        min_length=1, alias="verifiableCredential"
    )
    association: list[AssociationInput] | None = None

    model_config = {"populate_by_name": True}


class ClrCredentialInput(BaseModel):
    """
    Input model for upserting a CLR credential.

    Validates the required structure per the CLR v2.0 specification.
    """

    context: list[str] = Field(min_length=3, alias="@context")
    id: NonEmptyString
    type: list[str] = Field(min_length=1)
    issuer: ClrProfileInput
    name: NonEmptyString
    description: str | None = None
    valid_from: NonEmptyString = Field(alias="validFrom")
    valid_until: str | None = Field(None, alias="validUntil")
    credential_subject: ClrCredentialSubjectInput = Field(alias="credentialSubject")
    proof: list[ClrProofInput] | None = None
    credential_schema: list[ClrCredentialSchemaInput] | None = Field(None, alias="credentialSchema")

    model_config = {"populate_by_name": True}

    @field_validator("context", mode="before")
    @classmethod
    def _validate_context(cls, v: list[str]) -> list[str]:
        if not isinstance(v, list):
            return v
        if len(v) < 3:
            msg = "@context must contain at least 3 entries per the CLR v2.0 specification"
            raise ValueError(msg)
        if v[0] != W3C_CREDENTIALS_CONTEXT:
            msg = f'First @context entry must be "{W3C_CREDENTIALS_CONTEXT}"'
            raise ValueError(msg)
        return v
