"""
CloudEvents-Native Receipt Schemas

First-class CloudEvents 1.0 support for Loom receipts with:
- MetaReceiptBundle: TSA + Sigstore + Rekor anchoring
- External webhook normalization (Intuit, Qlik, GitHub, Stripe, Slack)
- Court-grade verification metadata

CloudEvents Alignment:
    id       → receipt_id (ULID)
    source   → urn:loom:{organ}:{component}
    type     → loom.receipt.{domain}.{action}.v{version}
    time     → RFC 3339 timestamp (ts field)
    
Loom Extensions (loom-* prefix, spec-compliant):
    loomseq        → Sequence number (monotonic per edge)
    loomvc         → Vector clock (JSON-encoded)
    loomedgeid     → Edge/string identifier
    loomhistorykey → Tenant identifier
    loomattestlevel → Proof level (L1-L5)
    loommerkleroot → Merkle tree root hash
    loomtsatoken   → TSA timestamp token (base64)
    loomrekorindex → Rekor transparency log index

Reference: CloudEvents v1.0 Specification
https://github.com/cloudevents/spec/blob/v1.0.2/spec.md
"""
from __future__ import annotations

import hashlib
import secrets
from datetime import datetime, timezone
from enum import Enum
from typing import Any, Dict, List, Literal, Optional

from pydantic import field_validator, model_validator

from receipts.compat.pyd import BaseModelLike as BaseModel, ConfigDictLike, FieldLike as Field


# =============================================================================
# CloudEvents Type Registry
# =============================================================================

class LoomEventType(str, Enum):
    """CloudEvents type values for Loom receipts."""
    
    # Core receipt types
    PROPHECY = "loom.receipt.prophecy.v1"
    REFUSAL = "loom.receipt.refusal.v1"
    CALIBRATION = "loom.receipt.calibration.v1"
    DIP = "loom.receipt.dip.v1"
    MERGE = "loom.receipt.merge.v1"
    
    # Governance
    DIGNITY_GATE = "loom.governance.dignity.v1"
    GUARDIAN_VERDICT = "loom.governance.guardian.v1"
    FREEZE_DECISION = "loom.governance.freeze.v1"
    
    # Memory/Graph
    HYPHA_PUBLISHED = "loom.mycelium.publish.v1"
    HYPHA_BLOOMED = "loom.mycelium.bloom.v1"
    HYPHA_COMPOSTED = "loom.mycelium.compost.v1"
    MEMORYGRAPH_NODE = "loom.memorygraph.node.v1"
    
    # External events (normalized from webhooks)
    EXTERNAL_GENERIC = "loom.external.generic.v1"
    EXTERNAL_PAYMENT = "loom.external.payment.v1"
    EXTERNAL_AUTH = "loom.external.auth.v1"
    EXTERNAL_CI = "loom.external.ci.v1"


class AttestationLevel(str, Enum):
    """
    Proof attestation levels (L1-L5).
    
    Each level adds cryptographic assurance:
    - L1: JCS canonical hash only
    - L2: L1 + Ed25519 signature
    - L3: L2 + RFC 3161 TSA timestamp
    - L4: L3 + countersignature (witness)
    - L5: L4 + Rekor transparency log
    """
    L1_HASH = "L1"
    L2_SIGNED = "L2"
    L3_TSA = "L3"
    L4_COUNTERSIGNED = "L4"
    L5_REKOR = "L5"


# =============================================================================
# CloudEvents Envelope
# =============================================================================

class CloudEventsEnvelope(BaseModel):
    """
    CloudEvents 1.0 envelope for Loom receipts.
    
    This is the canonical transport format for receipts across:
    - HTTP webhooks
    - Kafka topics
    - NATS subjects
    - WebSocket streams
    - SSE channels
    """
    model_config = ConfigDictLike(extra="allow")  # Allow CE extensions
    
    # Required CloudEvents attributes
    specversion: Literal["1.0"] = Field(
        default="1.0",
        description="CloudEvents specification version (always 1.0)",
    )
    id: str = Field(
        description="Unique event ID (maps to receipt_id)",
    )
    source: str = Field(
        description="Event source URI (urn:loom:{organ}:{component})",
    )
    type: str = Field(
        description="Event type (loom.receipt.{domain}.{action}.v{version})",
    )
    
    # Optional CloudEvents attributes
    time: Optional[datetime] = Field(
        default=None,
        description="RFC 3339 timestamp",
    )
    datacontenttype: str = Field(
        default="application/json",
        description="MIME type of data payload",
    )
    dataschema: Optional[str] = Field(
        default=None,
        description="Schema URI for data validation",
    )
    subject: Optional[str] = Field(
        default=None,
        description="Event subject (e.g., tenant:domain:asset)",
    )
    
    # Loom extensions (loom-* prefix)
    loomseq: Optional[int] = Field(
        default=None,
        description="Sequence number (monotonic per edge)",
    )
    loomvc: Optional[Dict[str, int]] = Field(
        default=None,
        description="Vector clock (node_id → clock value)",
    )
    loomedgeid: Optional[str] = Field(
        default=None,
        description="Edge/string identifier",
    )
    loomhistorykey: Optional[str] = Field(
        default=None,
        description="Tenant identifier (cross-tenant isolation)",
    )
    loomattestlevel: Optional[str] = Field(
        default=None,
        description="Attestation level (L1-L5)",
    )
    loommerkleroot: Optional[str] = Field(
        default=None,
        description="Merkle tree root hash (sha256:...)",
    )
    loomtsatoken: Optional[str] = Field(
        default=None,
        description="RFC 3161 TSA timestamp token (base64)",
    )
    loomrekorindex: Optional[int] = Field(
        default=None,
        description="Rekor transparency log index",
    )
    loomrekorlogid: Optional[str] = Field(
        default=None,
        description="Rekor log ID (shard identifier)",
    )
    
    # W3C Trace Context extensions
    traceparent: Optional[str] = Field(
        default=None,
        description="W3C Trace Context traceparent (00-trace_id-span_id-flags)",
    )
    tracestate: Optional[str] = Field(
        default=None,
        description="W3C Trace Context tracestate (vendor context)",
    )
    
    # Data payload
    data: Dict[str, Any] = Field(
        default_factory=dict,
        description="Receipt payload",
    )
    
    @field_validator("time", mode="before")
    @classmethod
    def _parse_time(cls, v: Any) -> Optional[datetime]:
        if v is None:
            return None
        if isinstance(v, datetime):
            return v if v.tzinfo else v.replace(tzinfo=timezone.utc)
        if isinstance(v, str):
            # Parse RFC 3339
            try:
                dt = datetime.fromisoformat(v.replace("Z", "+00:00"))
                return dt
            except ValueError:
                return None
        return None
    
    @field_validator("traceparent")
    @classmethod
    def _validate_traceparent(cls, v: Optional[str]) -> Optional[str]:
        if v is None:
            return None
        # W3C Trace Context: 00-{trace_id}-{parent_id}-{flags}
        import re
        pattern = re.compile(r"^00-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$", re.IGNORECASE)
        if not pattern.match(v):
            raise ValueError("traceparent must match W3C Trace Context format")
        return v


# =============================================================================
# TSA Timestamp Token
# =============================================================================

class TSATimestampToken(BaseModel):
    """RFC 3161 timestamp token metadata."""
    
    model_config = ConfigDictLike(frozen=True)
    
    tsa_url: str = Field(
        description="TSA service URL",
    )
    policy_oid: Optional[str] = Field(
        default=None,
        description="TSA policy OID (e.g., 2.16.840.1.101.3.4.2.1)",
    )
    timestamp: datetime = Field(
        description="Timestamp from TSA response",
    )
    token_b64: str = Field(
        description="Base64-encoded timestamp token (DER)",
    )
    cert_chain_b64: Optional[str] = Field(
        default=None,
        description="Base64-encoded certificate chain (for offline verification)",
    )
    serial_number: Optional[str] = Field(
        default=None,
        description="TSA certificate serial number",
    )
    accuracy_ms: Optional[int] = Field(
        default=None,
        description="Timestamp accuracy in milliseconds",
    )


# =============================================================================
# Sigstore Bundle
# =============================================================================

class SigstoreBundle(BaseModel):
    """Sigstore signing bundle metadata."""
    
    model_config = ConfigDictLike(frozen=True)
    
    signature_b64: str = Field(
        description="Base64-encoded signature",
    )
    certificate_b64: str = Field(
        description="Base64-encoded Fulcio certificate",
    )
    rekor_log_index: int = Field(
        description="Rekor transparency log index",
    )
    rekor_log_id: str = Field(
        description="Rekor log ID (shard identifier)",
    )
    integrated_time: datetime = Field(
        description="Time of Rekor integration",
    )
    inclusion_proof: Optional[Dict[str, Any]] = Field(
        default=None,
        description="Merkle inclusion proof from Rekor",
    )
    signed_entry_timestamp: Optional[str] = Field(
        default=None,
        description="SET from Rekor (base64)",
    )


# =============================================================================
# MetaReceiptBundle - Court-Grade Anchoring
# =============================================================================

class MetaReceiptBundle(BaseModel):
    """
    Court-grade receipt bundle combining all trust anchors.
    
    This is the "gold standard" for Loom receipts:
    - Original receipt (JCS canonical hash)
    - Ed25519 signature (with key rotation)
    - RFC 3161 TSA timestamp (legal time)
    - Sigstore bundle (Fulcio + Rekor)
    - Merkle chain position
    - ΔC coherence metrics
    
    Use this for:
    - Legal/compliance evidence
    - Long-term archival
    - Cross-organization trust
    - Audit trail requirements
    """
    
    model_config = ConfigDictLike(frozen=True)
    
    # Identity
    bundle_id: str = Field(
        description="Unique bundle ID (ULID)",
    )
    receipt_id: str = Field(
        description="Original receipt ID",
    )
    receipt_hash: str = Field(
        description="JCS canonical hash of receipt (sha256:...)",
    )
    
    # CloudEvents envelope
    cloudevents_envelope: CloudEventsEnvelope = Field(
        description="CloudEvents 1.0 envelope wrapping the receipt",
    )
    
    # Cryptographic attestations
    attestation_level: AttestationLevel = Field(
        description="Highest attestation level achieved",
    )
    
    # Ed25519 signature
    signature: Optional[str] = Field(
        default=None,
        description="Base64-encoded Ed25519 signature",
    )
    signing_key_id: Optional[str] = Field(
        default=None,
        description="Key ID used for signing (kid)",
    )
    
    # TSA timestamp
    tsa_token: Optional[TSATimestampToken] = Field(
        default=None,
        description="RFC 3161 timestamp token",
    )
    
    # Sigstore bundle
    sigstore_bundle: Optional[SigstoreBundle] = Field(
        default=None,
        description="Sigstore signing bundle (Fulcio + Rekor)",
    )
    
    # Merkle chain
    merkle_root: Optional[str] = Field(
        default=None,
        description="Merkle tree root at time of anchoring",
    )
    chain_position: Optional[int] = Field(
        default=None,
        description="Position in the Merkle chain",
    )
    parent_hash: Optional[str] = Field(
        default=None,
        description="Previous receipt's hash in chain",
    )
    
    # Coherence metrics (ΔC)
    delta_c: Optional[float] = Field(
        default=None,
        description="Coherence delta at time of crystallization",
    )
    chi: Optional[float] = Field(
        default=None,
        description="Curvature score (χ-routing)",
    )
    dignity_floor: Optional[float] = Field(
        default=None,
        description="Dignity floor enforced (0.15 minimum)",
    )
    
    # Timestamps
    created_at: datetime = Field(
        default_factory=lambda: datetime.now(timezone.utc),
        description="Bundle creation time",
    )
    tsa_timestamp: Optional[datetime] = Field(
        default=None,
        description="TSA timestamp (legal time)",
    )
    rekor_timestamp: Optional[datetime] = Field(
        default=None,
        description="Rekor integration time",
    )
    
    @model_validator(mode="after")
    def _set_attestation_level(self) -> "MetaReceiptBundle":
        """Auto-compute attestation level based on available proofs."""
        # Determine level based on what's present
        if self.sigstore_bundle and self.sigstore_bundle.rekor_log_index:
            level = AttestationLevel.L5_REKOR
        elif self.tsa_token:
            level = AttestationLevel.L3_TSA
        elif self.signature:
            level = AttestationLevel.L2_SIGNED
        else:
            level = AttestationLevel.L1_HASH
        
        # Use object.__setattr__ since model is frozen
        object.__setattr__(self, "attestation_level", level)
        return self


# =============================================================================
# Factory Functions
# =============================================================================

def generate_ulid() -> str:
    """Generate ULID (time-sortable unique ID)."""
    timestamp_ms = int(datetime.now(timezone.utc).timestamp() * 1000)
    random_part = secrets.token_hex(8)
    return f"{timestamp_ms:010x}{random_part}"


def wrap_receipt_as_cloudevents(
    receipt: Dict[str, Any],
    source: str = "urn:loom:garden:crystallization",
    event_type: Optional[str] = None,
    subject: Optional[str] = None,
    seq: Optional[int] = None,
    history_key: Optional[str] = None,
    attestation_level: AttestationLevel = AttestationLevel.L1_HASH,
    traceparent: Optional[str] = None,
) -> CloudEventsEnvelope:
    """
    Wrap a Loom receipt in CloudEvents 1.0 envelope.
    
    Args:
        receipt: Raw receipt dict
        source: Event source URI
        event_type: CloudEvents type (auto-detected if None)
        subject: Event subject (e.g., tenant:domain:asset)
        seq: Sequence number
        history_key: Tenant identifier
        attestation_level: Proof level achieved
        traceparent: W3C Trace Context
    
    Returns:
        CloudEventsEnvelope ready for transport
    """
    receipt_id = receipt.get("receipt_id", generate_ulid())
    receipt_type = receipt.get("receipt_type", "GenericReceipt")
    
    # Auto-detect event type
    if event_type is None:
        type_map = {
            "ProphecyStone": LoomEventType.PROPHECY.value,
            "RefusalStone": LoomEventType.REFUSAL.value,
            "CalibrationReceipt": LoomEventType.CALIBRATION.value,
            "DIPReceipt": LoomEventType.DIP.value,
            "MergeReceipt": LoomEventType.MERGE.value,
            "HyphaPublishedReceipt": LoomEventType.HYPHA_PUBLISHED.value,
            "HyphaBloomedReceipt": LoomEventType.HYPHA_BLOOMED.value,
        }
        event_type = type_map.get(receipt_type, "loom.receipt.generic.v1")
    
    # Extract timestamp
    ts = receipt.get("ts") or receipt.get("valid_time")
    if isinstance(ts, str):
        try:
            ts = datetime.fromisoformat(ts.replace("Z", "+00:00"))
        except ValueError:
            ts = datetime.now(timezone.utc)
    elif not isinstance(ts, datetime):
        ts = datetime.now(timezone.utc)
    
    # Compute canonical hash
    import json
    canonical = json.dumps(receipt, sort_keys=True, separators=(",", ":"))
    receipt_hash = hashlib.sha256(canonical.encode()).hexdigest()
    
    return CloudEventsEnvelope(
        specversion="1.0",
        id=receipt_id,
        source=source,
        type=event_type,
        time=ts,
        datacontenttype="application/json",
        subject=subject,
        loomseq=seq,
        loomhistorykey=history_key,
        loomattestlevel=attestation_level.value,
        loommerkleroot=f"sha256:{receipt_hash}",
        traceparent=traceparent,
        data=receipt,
    )


def create_meta_bundle(
    receipt: Dict[str, Any],
    source: str = "urn:loom:garden:crystallization",
    signature: Optional[str] = None,
    signing_key_id: Optional[str] = None,
    tsa_token: Optional[TSATimestampToken] = None,
    sigstore_bundle: Optional[SigstoreBundle] = None,
    merkle_root: Optional[str] = None,
    chain_position: Optional[int] = None,
    parent_hash: Optional[str] = None,
    delta_c: Optional[float] = None,
    chi: Optional[float] = None,
) -> MetaReceiptBundle:
    """
    Create a court-grade MetaReceiptBundle.
    
    This combines all trust anchors into a single verifiable bundle.
    """
    envelope = wrap_receipt_as_cloudevents(receipt, source=source)
    
    # Compute receipt hash
    import json
    canonical = json.dumps(receipt, sort_keys=True, separators=(",", ":"))
    receipt_hash = f"sha256:{hashlib.sha256(canonical.encode()).hexdigest()}"
    
    # Determine attestation level
    if sigstore_bundle:
        level = AttestationLevel.L5_REKOR
    elif tsa_token:
        level = AttestationLevel.L3_TSA
    elif signature:
        level = AttestationLevel.L2_SIGNED
    else:
        level = AttestationLevel.L1_HASH
    
    return MetaReceiptBundle(
        bundle_id=generate_ulid(),
        receipt_id=envelope.id,
        receipt_hash=receipt_hash,
        cloudevents_envelope=envelope,
        attestation_level=level,
        signature=signature,
        signing_key_id=signing_key_id,
        tsa_token=tsa_token,
        sigstore_bundle=sigstore_bundle,
        merkle_root=merkle_root or envelope.loommerkleroot,
        chain_position=chain_position,
        parent_hash=parent_hash,
        delta_c=delta_c,
        chi=chi,
        dignity_floor=0.15,  # Constitutional minimum
        tsa_timestamp=tsa_token.timestamp if tsa_token else None,
        rekor_timestamp=sigstore_bundle.integrated_time if sigstore_bundle else None,
    )


__all__ = [
    "LoomEventType",
    "AttestationLevel",
    "CloudEventsEnvelope",
    "TSATimestampToken",
    "SigstoreBundle",
    "MetaReceiptBundle",
    "generate_ulid",
    "wrap_receipt_as_cloudevents",
    "create_meta_bundle",
]
