"""
Actuator Domain Receipts

Includes:
- ActionReceipt: For every actuator action
- PromotionReceipt: For policy/model promotions

Part of Stage 1: Actuator Organ Implementation
Integrated with Proof Rails vNext for cryptographic attestation.
"""

import time
import logging
from typing import Dict, Any, Optional, List
from dataclasses import dataclass
from datetime import datetime

from receipts.base import BaseReceipt
from receipts.compat.pyd import Field
from tools.proofkit import ProofKit

# Initialize ProofKit for cryptographic operations
proofkit = ProofKit()
logger = logging.getLogger(__name__)


class ActionReceipt(BaseReceipt):
    """
    Receipt for every Actuator action.
    Cryptographically signed record of what was attempted and what happened.

    Includes Proof Rails vNext attestation for integrity and non-repudiation.
    """

    # Action details
    action_type: str
    action_args: Dict[str, Any]
    target_element: Optional[str] = None

    # Context
    task_id: str
    tier: str
    speaker_id: Optional[str] = None
    screen_state_hash: str = ""

    # Guardian
    guardian_verdict: str = ""  # "ALLOW", "WARN", "VETO"
    guardian_risk_level: str = ""
    guardian_reasons: List[str] = Field(default_factory=list)
    dry_run: bool = False

    # Outcome
    success: bool = False
    latency_ms: int = 0
    error: Optional[str] = None

    # Lineage
    parent_ids: List[str] = Field(default_factory=list)
    policy_variant: str = ""

    # Timestamps (tri-temporal)
    timestamp: datetime = Field(default_factory=datetime.now)
    valid_time: datetime = Field(default_factory=datetime.now)
    transaction_time: datetime = Field(default_factory=datetime.now)

    # Constitutional & privacy
    privacy_safe: bool = True
    consent_obtained: bool = True
    consent_token: Optional[str] = None
    profile: str = "enterprise"
    receipt_mode: str = "signed"  # "signed" | "minimal"
    retention_days: Optional[int] = None
    encryption_required: bool = False

    # Proof Rails vNext attestation
    attestation: Dict[str, Any] = Field(default_factory=dict)

    @property
    def receipt_type(self) -> str:
        return "ActionReceipt"

    def sign(self, key: bytes, algorithm: str = "hmac-sha256") -> None:
        """
        Sign this receipt with Proof Rails vNext attestation.

        Args:
            key: Signing key
            algorithm: Signing algorithm ("hmac-sha256" or "ed25519")
        """
        # Only sign if receipt_mode is "signed"
        if self.receipt_mode != "signed":
            logger.debug(f"Skipping signature for ActionReceipt {self.receipt_id} (receipt_mode: {self.receipt_mode})")
            return

        try:
            # Create signed receipt data
            signed_receipt = proofkit.sign_receipt(
                self.to_dict(),
                key,
                algorithm
            )
            # Extract attestation block
            self.attestation = signed_receipt.get("attestation", {})
            logger.info(f"Signed ActionReceipt: {self.receipt_id}")
        except Exception as e:
            logger.error(f"Failed to sign ActionReceipt {self.receipt_id}: {e}")
            # Continue without attestation rather than failing

    def verify_signature(self, key: bytes, algorithm: str = "hmac-sha256") -> bool:
        """
        Verify the cryptographic signature of this receipt.

        Args:
            key: Verification key
            algorithm: Algorithm used for signing

        Returns:
            True if signature is valid
        """
        if not self.attestation:
            return False

        try:
            return proofkit.verify_receipt_signature(
                self.to_dict(),
                key,
                algorithm
            )
        except Exception as e:
            logger.error(f"Failed to verify ActionReceipt {self.receipt_id}: {e}")
            return False

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        data = super().to_dict()
        # Ensure new fields are present explicitly
        data["profile"] = self.profile
        data["receipt_mode"] = self.receipt_mode
        data["retention_days"] = self.retention_days
        data["encryption_required"] = self.encryption_required
        return data


@dataclass
class PromotionReceipt(BaseReceipt):
    """
    Receipt for promoting a new policy/prompt/tool variant.
    Records the decision to promote and the evidence supporting it.

    Includes Proof Rails vNext attestation for governance integrity.
    """

    # What changed
    change_type: str  # "policy", "prompt", "tool"
    variant_id: str
    previous_variant_id: str

    # Metrics comparison
    metrics_before: Dict[str, float] = None
    metrics_after: Dict[str, float] = None

    # Eval battery results
    eval_battery_results: Dict[str, Any] = None
    success_rate_delta: float = 0.0
    veto_rate_delta: float = 0.0
    latency_delta_ms: float = 0.0

    # Critic verdict
    critic_verdict: str = ""  # "PROMOTE", "REJECT"
    critic_reasons: List[str] = None

    # Lineage
    parent_ids: List[str] = None

    # Governance
    operator: str = ""
    timestamp: datetime = None

    # Proof Rails vNext attestation
    attestation: Dict[str, Any] = None

    def __post_init__(self):
        super().__post_init__()
        if self.metrics_before is None:
            self.metrics_before = {}
        if self.metrics_after is None:
            self.metrics_after = {}
        if self.eval_battery_results is None:
            self.eval_battery_results = {}
        if self.critic_reasons is None:
            self.critic_reasons = []
        if self.parent_ids is None:
            self.parent_ids = []
        if self.timestamp is None:
            self.timestamp = datetime.now()
        if self.attestation is None:
            self.attestation = {}

    @property
    def receipt_type(self) -> str:
        return "PromotionReceipt"

    @property
    def promotion_decision(self) -> str:
        """Determine if this is a promotion or rejection."""
        return "PROMOTE" if self.critic_verdict == "PROMOTE" else "REJECT"

    def sign(self, key: bytes, algorithm: str = "hmac-sha256") -> None:
        """
        Sign this receipt with Proof Rails vNext attestation.

        Args:
            key: Signing key
            algorithm: Signing algorithm ("hmac-sha256" or "ed25519")
        """
        try:
            # Create signed receipt data
            signed_receipt = proofkit.sign_receipt(
                self.to_dict(),
                key,
                algorithm
            )
            # Extract attestation block
            self.attestation = signed_receipt.get("attestation", {})
            logger.info(f"Signed PromotionReceipt: {self.receipt_id}")
        except Exception as e:
            logger.error(f"Failed to sign PromotionReceipt {self.receipt_id}: {e}")
            # Continue without attestation rather than failing

    def verify_signature(self, key: bytes, algorithm: str = "hmac-sha256") -> bool:
        """
        Verify the cryptographic signature of this receipt.

        Args:
            key: Verification key
            algorithm: Algorithm used for signing

        Returns:
            True if signature is valid
        """
        if not self.attestation:
            return False

        try:
            return proofkit.verify_receipt_signature(
                self.to_dict(),
                key,
                algorithm
            )
        except Exception as e:
            logger.error(f"Failed to verify PromotionReceipt {self.receipt_id}: {e}")
            return False

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for serialization."""
        data = super().to_dict()
        # Convert datetime to ISO string
        if self.timestamp:
            data['timestamp'] = self.timestamp.isoformat()
        # Include attestation
        if self.attestation:
            data['attestation'] = self.attestation
        return data


# Convenience functions for creating receipts

def create_action_receipt(
    action_type: str,
    action_args: Dict[str, Any],
    task_id: str,
    tier: str,
    guardian_verdict: str,
    success: bool,
    latency_ms: int,
    sign_key: Optional[bytes] = None,
    sign_algorithm: str = "hmac-sha256",
    profile: str = "enterprise",
    receipt_mode: str = "signed",
    retention_days: Optional[int] = None,
    encryption_required: bool = False,
    consent_token: Optional[str] = None,
    **kwargs
) -> ActionReceipt:
    """
    Create an ActionReceipt for an actuator action.

    Args:
        action_type: Type of action performed
        action_args: Arguments for the action
        task_id: Task identifier
        tier: Curriculum tier (L1, L2, L3)
        guardian_verdict: Guardian decision ("ALLOW", "WARN", "VETO")
        success: Whether the action succeeded
        latency_ms: Action latency in milliseconds
        sign_key: Optional cryptographic key for signing
        sign_algorithm: Signing algorithm ("hmac-sha256" or "ed25519")
        profile: Profile determining signing behavior ("enterprise", "consumer", etc.)
        receipt_mode: Receipt mode ("signed" or "minimal")
        retention_days: Optional retention period in days
        encryption_required: Whether encryption is required
        **kwargs: Additional fields

    Returns:
        ActionReceipt instance, signed if key provided and receipt_mode is "signed"
    """
    import uuid
    
    # Generate ID if not provided
    if "receipt_id" not in kwargs:
        kwargs["receipt_id"] = f"act_{int(time.time())}_{uuid.uuid4().hex[:8]}"
    
    # Set domain if not provided
    if "domain" not in kwargs:
        kwargs["domain"] = "actuator"

    receipt = ActionReceipt(
        action_type=action_type,
        action_args=action_args,
        task_id=task_id,
        tier=tier,
        guardian_verdict=guardian_verdict,
        success=success,
        latency_ms=latency_ms,
        consent_token=consent_token,
        profile=profile,
        receipt_mode=receipt_mode,
        retention_days=retention_days,
        encryption_required=encryption_required,
        **kwargs
    )

    # Sign if key provided and receipt_mode is "signed"
    if sign_key and receipt_mode == "signed":
        receipt.sign(sign_key, sign_algorithm)

    return receipt


def create_promotion_receipt(
    change_type: str,
    variant_id: str,
    previous_variant_id: str,
    metrics_before: Dict[str, float],
    metrics_after: Dict[str, float],
    eval_battery_results: Dict[str, Any],
    critic_verdict: str,
    operator: str,
    sign_key: Optional[bytes] = None,
    sign_algorithm: str = "hmac-sha256",
    **kwargs
) -> PromotionReceipt:
    """
    Create a PromotionReceipt for a policy/model promotion.

    Args:
        change_type: Type of change ("policy", "prompt", "tool")
        variant_id: New variant identifier
        previous_variant_id: Previous variant identifier
        metrics_before: Performance metrics before promotion
        metrics_after: Performance metrics after promotion
        eval_battery_results: Evaluation battery test results
        critic_verdict: Promotion decision ("PROMOTE" or "REJECT")
        operator: Who authorized the promotion
        sign_key: Optional cryptographic key for signing
        sign_algorithm: Signing algorithm ("hmac-sha256" or "ed25519")
        **kwargs: Additional fields

    Returns:
        PromotionReceipt instance, signed if key provided
    """
    # Calculate deltas
    success_rate_delta = metrics_after.get('success_rate', 0) - metrics_before.get('success_rate', 0)
    veto_rate_delta = metrics_after.get('veto_rate', 0) - metrics_before.get('veto_rate', 0)
    latency_delta_ms = metrics_after.get('average_latency_ms', 0) - metrics_before.get('average_latency_ms', 0)

    receipt = PromotionReceipt(
        change_type=change_type,
        variant_id=variant_id,
        previous_variant_id=previous_variant_id,
        metrics_before=metrics_before,
        metrics_after=metrics_after,
        eval_battery_results=eval_battery_results,
        success_rate_delta=success_rate_delta,
        veto_rate_delta=veto_rate_delta,
        latency_delta_ms=latency_delta_ms,
        critic_verdict=critic_verdict,
        operator=operator,
        **kwargs
    )

    # Sign if key provided
    if sign_key:
        receipt.sign(sign_key, sign_algorithm)

    return receipt


# Integration with existing receipt system

# Feature flag for real persistence vs stub mode
import os
_ACTUATOR_PERSIST_RECEIPTS = os.environ.get("LOOM_ACTUATOR_PERSIST_RECEIPTS", "true").lower() == "true"


def emit_action_receipt(receipt: ActionReceipt) -> str:
    """
    Emit an ActionReceipt to the ledger.

    When LOOM_ACTUATOR_PERSIST_RECEIPTS=true (default), writes to the real
    receipt ledger via receipts.writer.write_receipt().

    Returns the receipt ID.
    """
    receipt_id = receipt.receipt_id

    if _ACTUATOR_PERSIST_RECEIPTS:
        try:
            from receipts.writer import write_receipt
            write_receipt(receipt)
            logger.info(f"Persisted ActionReceipt to ledger: {receipt_id}")
        except Exception as e:
            # Don't break the action if persistence fails
            logger.warning(f"Failed to persist ActionReceipt {receipt_id}: {e}")
    else:
        logger.debug(f"ActionReceipt created (persistence disabled): {receipt_id}")

    return receipt_id


def emit_promotion_receipt(receipt: PromotionReceipt) -> str:
    """
    Emit a PromotionReceipt to the ledger.

    When LOOM_ACTUATOR_PERSIST_RECEIPTS=true (default), writes to the real
    receipt ledger via receipts.writer.write_receipt().

    Returns the receipt ID.
    """
    receipt_id = receipt.receipt_id

    if _ACTUATOR_PERSIST_RECEIPTS:
        try:
            from receipts.writer import write_receipt
            write_receipt(receipt)
            logger.info(f"Persisted PromotionReceipt to ledger: {receipt_id}")
        except Exception as e:
            logger.warning(f"Failed to persist PromotionReceipt {receipt_id}: {e}")
    else:
        logger.debug(f"PromotionReceipt created (persistence disabled): {receipt_id}")

    return receipt_id


# Example usage

def example_action_receipt():
    """Example of creating and emitting an ActionReceipt with Proof Rails signing."""
    # Example signing key (in production, use proper key management)
    sign_key = b"example-key-32-bytes-for-demo!!!"

    receipt = create_action_receipt(
        action_type="click",
        action_args={"x": 100, "y": 200, "button": "left"},
        task_id="task_123",
        tier="L1",
        guardian_verdict="ALLOW",
        success=True,
        latency_ms=150,
        target_element="Run Tests button",
        screen_state_hash="abc123",
        policy_variant="v2.1",
        sign_key=sign_key  # Enable cryptographic signing
    )

    # Verify signature works
    is_valid = receipt.verify_signature(sign_key)
    print(f"ActionReceipt signature valid: {is_valid}")

    receipt_id = emit_action_receipt(receipt)
    return receipt_id


def example_promotion_receipt():
    """Example of creating and emitting a PromotionReceipt with Proof Rails signing."""
    # Example signing key
    sign_key = b"example-key-32-bytes-for-demo!!!"

    metrics_before = {
        "success_rate": 0.85,
        "veto_rate": 0.05,
        "average_latency_ms": 200
    }

    metrics_after = {
        "success_rate": 0.90,
        "veto_rate": 0.03,
        "average_latency_ms": 180
    }

    eval_results = {
        "l1_tasks_passed": 8,
        "l1_tasks_total": 10,
        "adversarial_passed": 5,
        "adversarial_total": 5
    }

    receipt = create_promotion_receipt(
        change_type="policy",
        variant_id="click_policy_v2",
        previous_variant_id="click_policy_v1",
        metrics_before=metrics_before,
        metrics_after=metrics_after,
        eval_battery_results=eval_results,
        critic_verdict="PROMOTE",
        operator="auto_critic",
        critic_reasons=["Improved success rate", "Lower veto rate", "Better latency"],
        sign_key=sign_key  # Enable cryptographic signing
    )

    # Verify signature works
    is_valid = receipt.verify_signature(sign_key)
    print(f"PromotionReceipt signature valid: {is_valid}")

    receipt_id = emit_promotion_receipt(receipt)
    return receipt_id


if __name__ == "__main__":
    # Run examples
    action_id = example_action_receipt()
    promotion_id = example_promotion_receipt()

    print(f"Created ActionReceipt: {action_id}")
    print(f"Created PromotionReceipt: {promotion_id}")