"""
CapabilityUseReceipt: Receipt for each use of a capability permit.

Tracks:
- Which capability was used
- Current remaining calls (max_calls decrement)
- Action performed
- Outcome

This implements the max_calls tracking required by Claim 17.
"""
from __future__ import annotations

from datetime import datetime, timezone
from typing import Any, Dict, Literal, Optional

from receipts.compat.pyd import Field, field_validator, model_validator
from receipts.base import BaseReceipt, Domain


class CapabilityUseReceipt(BaseReceipt):
    """
    Receipt documenting a single use of a CapabilityReceipt.

    Every time an agent exercises a capability, this receipt is emitted.
    It tracks the decrement of max_calls and links back to the original
    capability permit.
    """

    receipt_type: str = Field(default="CapabilityUseReceipt")
    domain: str = Field(default=Domain.GOVERNANCE.value)

    # Link to capability
    capability_id: str = Field(
        ...,
        description="ID of the CapabilityReceipt being exercised",
    )
    capability_receipt_id: str = Field(
        ...,
        description="Receipt ID of the original capability permit",
    )

    # Agent and action
    agent_id: str = Field(
        ...,
        description="Agent exercising the capability",
    )
    action: str = Field(
        ...,
        description="Action performed (e.g., 'fs.read', 'shell.exec')",
    )
    action_target: Optional[str] = Field(
        default=None,
        description="Target of the action (e.g., file path, command)",
    )

    # Call budget tracking (Claim 17)
    max_calls_original: Optional[int] = Field(
        default=None,
        description="Original max_calls budget from capability",
    )
    calls_before: int = Field(
        ...,
        ge=0,
        description="Calls used before this use",
    )
    calls_after: int = Field(
        ...,
        ge=0,
        description="Calls used after this use (calls_before + 1)",
    )
    calls_remaining: Optional[int] = Field(
        default=None,
        description="Remaining calls (None if unlimited)",
    )

    # Outcome
    outcome: Literal["success", "failure", "blocked", "expired"] = Field(
        ...,
        description="Result of the capability use",
    )
    outcome_details: Optional[str] = Field(
        default=None,
        description="Additional details about outcome",
    )

    # Guardian check
    guardian_approved: bool = Field(
        default=True,
        description="Whether Guardian approved this use",
    )
    guardian_verdict_id: Optional[str] = Field(
        default=None,
        description="ID of GuardianVerdictReceipt if checked",
    )

    @model_validator(mode="after")
    def _validate_calls(self) -> "CapabilityUseReceipt":
        """Validate call count consistency and budget constraints."""
        # Validate increment
        if self.calls_after != self.calls_before + 1:
            raise ValueError(
                f"calls_after ({self.calls_after}) must be calls_before ({self.calls_before}) + 1"
            )

        # Validate budget constraints when max_calls is set
        if self.max_calls_original is not None:
            # calls_remaining is required when max_calls is set
            if self.calls_remaining is None:
                raise ValueError(
                    "calls_remaining is required when max_calls_original is set"
                )

            # Validate calls_remaining computation
            expected_remaining = self.max_calls_original - self.calls_after
            if self.calls_remaining != expected_remaining:
                raise ValueError(
                    f"calls_remaining ({self.calls_remaining}) doesn't match "
                    f"max_calls_original ({self.max_calls_original}) - calls_after ({self.calls_after}) = {expected_remaining}"
                )

            # Budget overrun check: calls_remaining cannot be negative
            if self.calls_remaining < 0:
                raise ValueError(
                    f"calls_remaining ({self.calls_remaining}) cannot be negative; "
                    f"capability budget exhausted (max_calls={self.max_calls_original})"
                )

        return self


def create_capability_use_receipt(
    capability_id: str,
    capability_receipt_id: str,
    agent_id: str,
    action: str,
    calls_before: int,
    outcome: str = "success",
    max_calls_original: Optional[int] = None,
    **kwargs,
) -> CapabilityUseReceipt:
    """Factory function to create a CapabilityUseReceipt."""
    import uuid
    calls_after = calls_before + 1
    calls_remaining = None
    if max_calls_original is not None:
        calls_remaining = max_calls_original - calls_after

    return CapabilityUseReceipt(
        receipt_id=f"cur_{uuid.uuid4().hex[:16]}",
        capability_id=capability_id,
        capability_receipt_id=capability_receipt_id,
        agent_id=agent_id,
        action=action,
        calls_before=calls_before,
        calls_after=calls_after,
        calls_remaining=calls_remaining,
        max_calls_original=max_calls_original,
        outcome=outcome,
        **kwargs,
    )


__all__ = ["CapabilityUseReceipt", "create_capability_use_receipt"]
