"""
Organism Domain Receipts

Receipt types for the Mac Organism subsystem (self-driving agent loop).
All receipts are Ed25519-signed, JCS-canonical, and stored in the standard
receipt ledger for unified audit trails.

---
feature:
  id: feature.organism_receipts
  name: Organism Domain Receipts
  kind: module
  summary: Receipt models for organism loop events (state, plan, verdict, action, session)
  owners: ["@codex"]
  stage: production
  tags: [receipts, organism, p0]
  depends_on: [feature.receipt_writer]
  inputs: [OrganismLoop events]
  outputs: [Signed receipts in ledger]
  invariants:
    - All receipts are Ed25519-signed
    - Receipt IDs are globally unique
    - Parent chains are maintained for lineage
  risk_axes:
    unsigned_receipt: 0.0  # Enforced by writer
    missing_lineage: 0.02
---
"""

from __future__ import annotations

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

from pydantic import Field

from receipts.base import BaseReceipt, Domain
from receipts.compat.pyd import ConfigDictLike

logger = logging.getLogger(__name__)


# =============================================================================
# Enums
# =============================================================================

class OrganismDecision(str, Enum):
    """Guardian verdict decision for organism actions."""
    ALLOW = "allow"
    DENY = "deny"
    CONFIRM_REQUIRED = "confirm_required"


class OrganismActionType(str, Enum):
    """Types of actions the organism can execute."""
    CLICK = "click"
    TYPE_TEXT = "type_text"
    KEY_COMBO = "key_combo"
    RUN_COMMAND = "run_command"
    OPEN_APP = "open_app"
    SCROLL = "scroll"
    WAIT = "wait"


class OrganismExecutionMode(str, Enum):
    """Execution modes for organism actions."""
    GHOST = "ghost"         # Simulate only (no actual execution)
    CONFIRM = "confirm"     # Require confirmation per action
    LIVE = "live"           # Full autonomous execution


class SnapshotFidelity(str, Enum):
    """Fidelity levels for state snapshots."""
    SEMANTIC = "semantic"   # AX tree only, no screenshot
    LOW_RES = "low_res"     # 480p screenshot + AX tree
    FOVEA = "fovea"         # High-res crop of focus area
    FULL = "full"           # Full resolution screenshot + AX tree


# =============================================================================
# Receipt Models
# =============================================================================

class OrganismStateReceipt(BaseReceipt):
    """
    Receipt emitted when organism captures a state snapshot.
    
    Logged every time Eyes captures Mac state (AX tree, screenshot, health).
    """
    
    receipt_type: str = Field(default="OrganismStateReceipt", alias="type")
    domain: str = Field(default="organism")
    
    # State identifiers
    snapshot_id: str = Field(description="Unique ID for this state snapshot")
    goal_id: Optional[str] = Field(default=None, description="Goal that triggered this capture")
    
    # Capture metadata
    fidelity: SnapshotFidelity = Field(
        default=SnapshotFidelity.SEMANTIC,
        description="Fidelity level of the capture"
    )
    frontmost_app: Optional[str] = Field(
        default=None,
        description="Name of the frontmost application"
    )
    frontmost_window: Optional[str] = Field(
        default=None,
        description="Title of the frontmost window"
    )
    
    # AX tree summary (not full tree - that's too large)
    ax_element_count: int = Field(
        default=0,
        description="Number of AX elements in the captured tree"
    )
    ax_focusable_count: int = Field(
        default=0,
        description="Number of focusable/actionable elements"
    )
    
    # System health at capture time
    cpu_percent: Optional[float] = Field(default=None, description="CPU usage at capture")
    memory_percent: Optional[float] = Field(default=None, description="Memory usage at capture")
    thermal_pressure: Optional[str] = Field(default=None, description="Thermal state (nominal/fair/serious/critical)")
    
    # Screenshot reference (not the actual bytes)
    screenshot_path: Optional[str] = Field(
        default=None,
        description="Path to screenshot file (if captured)"
    )
    screenshot_hash: Optional[str] = Field(
        default=None,
        description="SHA-256 hash of screenshot bytes"
    )
    
    # Timing
    capture_duration_ms: float = Field(
        default=0.0,
        description="Time taken to capture state (ms)"
    )


class OrganismPlanReceipt(BaseReceipt):
    """
    Receipt emitted when Brain generates a plan.
    
    Links goal + state → planned actions with reasoning.
    """
    
    receipt_type: str = Field(default="OrganismPlanReceipt", alias="type")
    domain: str = Field(default="organism")
    
    # Plan identifiers
    plan_id: str = Field(description="Unique ID for this plan")
    goal_id: str = Field(description="Goal this plan addresses")
    snapshot_id: str = Field(description="State snapshot used for planning")
    
    # Planning metadata
    brain_model: str = Field(
        default="stub_v0.1",
        description="Model/backend used for planning (e.g., 'mlx:qwen2.5-7b', 'codex:gpt-5.1')"
    )
    reasoning: str = Field(
        default="",
        description="Human-readable reasoning for the plan"
    )
    confidence: float = Field(
        ge=0.0,
        le=1.0,
        default=0.5,
        description="Model's confidence in the plan (0-1)"
    )
    
    # Actions summary (not full action details - those go in action receipts)
    action_count: int = Field(default=0, description="Number of planned actions")
    action_types: List[str] = Field(
        default_factory=list,
        description="Types of actions in the plan"
    )
    
    # Timing
    planning_duration_ms: float = Field(
        default=0.0,
        description="Time taken to generate plan (ms)"
    )
    
    # Token usage (if LLM-based)
    tokens_input: Optional[int] = Field(default=None, description="Input tokens used")
    tokens_output: Optional[int] = Field(default=None, description="Output tokens generated")

    # Tiered routing metadata (v0.3+)
    actual_provider: Optional[str] = Field(
        default=None,
        description="Which provider actually served this request (e.g., 'local_mlx', 'openai', 'anthropic')"
    )
    goal_metadata_applied: Optional[Dict[str, Any]] = Field(
        default=None,
        description="Copy of goal_metadata used for routing (cost_tier, quality_tier, reasoning_depth)"
    )


class OrganismVerdictReceipt(BaseReceipt):
    """
    Receipt emitted when Guardian evaluates a plan.
    
    Constitutional gate decision with violations and required confirmations.
    """
    
    receipt_type: str = Field(default="OrganismVerdictReceipt", alias="type")
    domain: str = Field(default="organism")
    
    # Verdict identifiers
    verdict_id: str = Field(description="Unique ID for this verdict")
    plan_id: str = Field(description="Plan being evaluated")
    goal_id: str = Field(description="Goal context for the plan")
    
    # Decision
    decision: OrganismDecision = Field(description="Guardian's decision (allow/deny/confirm_required)")
    reasoning: str = Field(default="", description="Human-readable reasoning")
    
    # Violations (if any)
    violation_count: int = Field(default=0, description="Number of policy violations")
    violations: List[Dict[str, Any]] = Field(
        default_factory=list,
        description="List of violation details (rule, severity, action_id, description)"
    )
    
    # Required confirmations (if decision is confirm_required)
    confirmation_count: int = Field(default=0, description="Number of confirmations required")
    required_confirmations: List[Dict[str, Any]] = Field(
        default_factory=list,
        description="Actions requiring user confirmation"
    )
    
    # Constitutional metrics
    dignity_score: float = Field(
        ge=0.0,
        le=1.0,
        default=1.0,
        description="Dignity floor compliance score"
    )
    passes_dignity_floor: bool = Field(
        default=True,
        description="Whether plan passes dignity floor (0.15 minimum)"
    )
    
    # Execution mode granted
    execution_mode: OrganismExecutionMode = Field(
        default=OrganismExecutionMode.GHOST,
        description="Approved execution mode for this plan"
    )
    
    # Guardian backend
    guardian_backend: str = Field(
        default="regex_v0.1",
        description="Guardian backend used (regex_v0.1, opa_policy, gatebook)"
    )
    
    # Timing
    evaluation_duration_ms: float = Field(
        default=0.0,
        description="Time taken to evaluate plan (ms)"
    )


class OrganismActionReceipt(BaseReceipt):
    """
    Receipt emitted for each action executed by Hands.
    
    One receipt per action, with execution status and results.
    """
    
    receipt_type: str = Field(default="OrganismActionReceipt", alias="type")
    domain: str = Field(default="organism")
    
    # Action identifiers
    action_id: str = Field(description="Unique ID for this action")
    plan_id: str = Field(description="Plan this action belongs to")
    goal_id: str = Field(description="Goal context")
    sequence_index: int = Field(default=0, description="Position in plan's action sequence")
    
    # Action details
    action_type: OrganismActionType = Field(description="Type of action")
    parameters: Dict[str, Any] = Field(
        default_factory=dict,
        description="Action parameters (x, y, text, command, etc.)"
    )
    
    # Execution
    execution_mode: OrganismExecutionMode = Field(
        default=OrganismExecutionMode.GHOST,
        description="Mode in which action was executed"
    )
    executed: bool = Field(default=False, description="Whether action was actually executed")
    success: bool = Field(default=False, description="Whether execution succeeded")
    
    # Results
    return_code: Optional[int] = Field(default=None, description="Return code (for run_command)")
    stdout_snippet: Optional[str] = Field(
        default=None,
        description="First 500 chars of stdout (for run_command)"
    )
    stderr_snippet: Optional[str] = Field(
        default=None,
        description="First 500 chars of stderr (for run_command)"
    )
    error_message: Optional[str] = Field(default=None, description="Error message if failed")
    
    # User interaction (for confirm mode)
    user_confirmed: Optional[bool] = Field(
        default=None,
        description="Whether user confirmed this action (for confirm mode)"
    )
    skipped: bool = Field(default=False, description="Whether action was skipped by user")
    skip_reason: Optional[str] = Field(default=None, description="Reason for skipping")
    
    # Timing
    execution_duration_ms: float = Field(
        default=0.0,
        description="Time taken to execute action (ms)"
    )


class OrganismLoopReceipt(BaseReceipt):
    """
    Session-level receipt for a complete organism loop iteration.
    
    Links all sub-receipts (state, plan, verdict, actions) into a coherent session.
    This is the primary audit trail for "what did the organism do?"
    """
    
    receipt_type: str = Field(default="OrganismLoopReceipt", alias="type")
    domain: str = Field(default="organism")
    
    # Session identifiers
    session_id: str = Field(description="Unique ID for this loop session")
    goal_id: str = Field(description="Goal being addressed")
    iteration: int = Field(default=1, description="Loop iteration number (for multi-step goals)")
    
    # Sub-receipt references
    snapshot_id: str = Field(description="State snapshot receipt ID")
    plan_id: str = Field(description="Plan receipt ID")
    verdict_id: str = Field(description="Guardian verdict receipt ID")
    action_receipt_ids: List[str] = Field(
        default_factory=list,
        description="List of action receipt IDs in execution order"
    )
    
    # Goal
    goal_text: str = Field(default="", description="Human-readable goal text")
    goal_kind: str = Field(default="dev_task", description="Goal kind/category")
    
    # Verdict summary
    decision: OrganismDecision = Field(description="Guardian's final decision")
    
    # Execution summary
    execution_mode: OrganismExecutionMode = Field(description="Mode used for execution")
    actions_planned: int = Field(default=0, description="Number of actions in plan")
    actions_executed: int = Field(default=0, description="Number of actions executed")
    actions_failed: int = Field(default=0, description="Number of actions that failed")
    actions_skipped: int = Field(default=0, description="Number of actions skipped by user")
    
    # Overall status
    success: bool = Field(default=False, description="Whether loop completed successfully")
    aborted: bool = Field(default=False, description="Whether loop was aborted")
    abort_reason: Optional[str] = Field(default=None, description="Reason for abort if aborted")
    
    # Timing
    total_duration_ms: float = Field(
        default=0.0,
        description="Total loop duration (ms)"
    )
    
    # Environment context
    hostname: Optional[str] = Field(default=None, description="Machine hostname")
    user: Optional[str] = Field(default=None, description="Current user")
    python_version: Optional[str] = Field(default=None, description="Python version")


# =============================================================================
# Convenience constructors
# =============================================================================

def create_state_receipt(
    snapshot_id: str,
    goal_id: Optional[str] = None,
    fidelity: str = "semantic",
    frontmost_app: Optional[str] = None,
    ax_element_count: int = 0,
    cpu_percent: Optional[float] = None,
    memory_percent: Optional[float] = None,
    thermal_pressure: Optional[str] = None,
    capture_duration_ms: float = 0.0,
    **kwargs
) -> OrganismStateReceipt:
    """Create a state snapshot receipt."""
    return OrganismStateReceipt(
        receipt_id=f"org_state_{snapshot_id}",
        snapshot_id=snapshot_id,
        goal_id=goal_id,
        fidelity=SnapshotFidelity(fidelity),
        frontmost_app=frontmost_app,
        ax_element_count=ax_element_count,
        cpu_percent=cpu_percent,
        memory_percent=memory_percent,
        thermal_pressure=thermal_pressure,
        capture_duration_ms=capture_duration_ms,
        **kwargs
    )


def create_plan_receipt(
    plan_id: str,
    goal_id: str,
    snapshot_id: str,
    action_count: int = 0,
    action_types: Optional[List[str]] = None,
    brain_model: str = "stub_v0.1",
    reasoning: str = "",
    confidence: float = 0.5,
    planning_duration_ms: float = 0.0,
    **kwargs
) -> OrganismPlanReceipt:
    """Create a plan generation receipt."""
    return OrganismPlanReceipt(
        receipt_id=f"org_plan_{plan_id}",
        plan_id=plan_id,
        goal_id=goal_id,
        snapshot_id=snapshot_id,
        action_count=action_count,
        action_types=action_types or [],
        brain_model=brain_model,
        reasoning=reasoning,
        confidence=confidence,
        planning_duration_ms=planning_duration_ms,
        **kwargs
    )


def create_verdict_receipt(
    verdict_id: str,
    plan_id: str,
    decision: str = "allow",
    reasoning: str = "",
    dignity_score: float = 1.0,
    violations: Optional[List[Dict]] = None,
    required_confirmations: Optional[List[Dict]] = None,
    execution_mode: str = "ghost",
    delta_c: Optional[float] = None,
    thermal_concern: bool = False,
    goal_id: str = "",
    guardian_backend: str = "production_v0.2",
    evaluation_duration_ms: float = 0.0,
    **kwargs
) -> OrganismVerdictReceipt:
    """
    Create a guardian verdict receipt.

    Args:
        verdict_id: Unique verdict identifier
        plan_id: Plan being evaluated
        decision: Guardian decision (allow/deny/confirm_required/throttle)
        reasoning: Human-readable explanation
        dignity_score: Multi-facet dignity score (0-1)
        violations: List of policy violations
        required_confirmations: Actions requiring user confirmation
        execution_mode: Granted execution mode (ghost/confirm/live)
        delta_c: Genesis ΔC coherence score (if computed)
        thermal_concern: Whether system is thermally stressed
        goal_id: Goal context
        guardian_backend: Backend used for evaluation
        evaluation_duration_ms: Evaluation time
    """
    violations = violations or []
    confirmations = required_confirmations or []

    # Map decision string to enum
    decision_map = {
        "allow": OrganismDecision.ALLOW,
        "deny": OrganismDecision.DENY,
        "confirm_required": OrganismDecision.CONFIRM_REQUIRED,
        "throttle": OrganismDecision.CONFIRM_REQUIRED,  # Throttle maps to confirm
    }
    decision_enum = decision_map.get(decision, OrganismDecision.DENY)

    # Map execution_mode string to enum
    mode_map = {
        "ghost": OrganismExecutionMode.GHOST,
        "confirm": OrganismExecutionMode.CONFIRM,
        "confirm_per_action": OrganismExecutionMode.CONFIRM,
        "live": OrganismExecutionMode.LIVE,
    }
    mode_enum = mode_map.get(execution_mode, OrganismExecutionMode.GHOST)

    receipt = OrganismVerdictReceipt(
        receipt_id=f"org_verdict_{verdict_id}",
        verdict_id=verdict_id,
        plan_id=plan_id,
        goal_id=goal_id,
        decision=decision_enum,
        reasoning=reasoning,
        violation_count=len(violations),
        violations=violations,
        confirmation_count=len(confirmations),
        required_confirmations=confirmations,
        dignity_score=dignity_score,
        passes_dignity_floor=dignity_score >= 0.15,
        execution_mode=mode_enum,
        guardian_backend=guardian_backend,
        evaluation_duration_ms=evaluation_duration_ms,
        **kwargs
    )

    # Add Genesis ΔC if available (stored in context field)
    if delta_c is not None:
        receipt_dict = receipt.model_dump() if hasattr(receipt, 'model_dump') else receipt.__dict__
        if 'context' not in receipt_dict or receipt_dict['context'] is None:
            object.__setattr__(receipt, 'context', {})
        if hasattr(receipt, 'context') and isinstance(receipt.context, dict):
            receipt.context['delta_c'] = delta_c
            receipt.context['thermal_concern'] = thermal_concern

    return receipt


def create_action_receipt(
    action_id: str,
    plan_id: str,
    goal_id: str,
    action_type: str,
    parameters: Dict[str, Any],
    sequence_index: int = 0,
    execution_mode: str = "ghost",
    executed: bool = False,
    success: bool = False,
    return_code: Optional[int] = None,
    error_message: Optional[str] = None,
    execution_duration_ms: float = 0.0,
    **kwargs
) -> OrganismActionReceipt:
    """Create an action execution receipt."""
    return OrganismActionReceipt(
        receipt_id=f"org_action_{action_id}",
        action_id=action_id,
        plan_id=plan_id,
        goal_id=goal_id,
        sequence_index=sequence_index,
        action_type=OrganismActionType(action_type),
        parameters=parameters,
        execution_mode=OrganismExecutionMode(execution_mode),
        executed=executed,
        success=success,
        return_code=return_code,
        error_message=error_message,
        execution_duration_ms=execution_duration_ms,
        **kwargs
    )


def create_loop_receipt(
    session_id: str,
    goal_id: str,
    snapshot_id: str,
    plan_id: str,
    verdict_id: str,
    action_receipt_ids: List[str],
    goal_text: str = "",
    decision: str = "allow",
    execution_mode: str = "ghost",
    actions_planned: int = 0,
    actions_executed: int = 0,
    actions_failed: int = 0,
    actions_skipped: int = 0,
    success: bool = False,
    total_duration_ms: float = 0.0,
    **kwargs
) -> OrganismLoopReceipt:
    """Create a session-level loop receipt."""
    import os
    import platform
    import sys

    return OrganismLoopReceipt(
        receipt_id=f"org_loop_{session_id}",
        session_id=session_id,
        goal_id=goal_id,
        snapshot_id=snapshot_id,
        plan_id=plan_id,
        verdict_id=verdict_id,
        action_receipt_ids=action_receipt_ids,
        goal_text=goal_text,
        decision=OrganismDecision(decision),
        execution_mode=OrganismExecutionMode(execution_mode),
        actions_planned=actions_planned,
        actions_executed=actions_executed,
        actions_failed=actions_failed,
        actions_skipped=actions_skipped,
        success=success,
        total_duration_ms=total_duration_ms,
        hostname=platform.node(),
        user=os.getenv("USER", "unknown"),
        python_version=f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
        **kwargs
    )


# =============================================================================
# Module Loader Receipts
# =============================================================================

class ModuleAdmissionStageReceipt(BaseReceipt):
    """Receipt for module admission stage transitions."""
    domain: Literal["organism"] = "organism"
    module_id: str = Field(description="ID of the module")
    stage: str = Field(description="Current admission stage (MANIFEST, SCHEMA, ORACLE, SANDBOX, GUARDIAN, WIRE)")
    status: str = Field(description="Module status after stage")
    success: bool = Field(description="Whether stage passed")
    details: Dict[str, Any] = Field(default_factory=dict, description="Stage-specific details")

    model_config = ConfigDictLike(json_schema_extra={
        "example": {
            "module_id": "mod-001",
            "stage": "MANIFEST",
            "status": "schema_pending",
            "success": True,
            "details": {"manifest_hash": "abc123..."}
        }
    })


class ModuleAdmittedReceipt(BaseReceipt):
    """Receipt for module successfully admitted (reaches ACTIVE status)."""
    domain: Literal["organism"] = "organism"
    module_id: str = Field(description="ID of the admitted module")
    module_name: str = Field(description="Name of the module")
    version: str = Field(description="Module version")
    author: str = Field(description="Module author")
    capabilities: List[str] = Field(description="Capabilities the module provides")
    manifest_hash: str = Field(description="SHA256 hash of module manifest")
    guardian_signature: Optional[str] = Field(default=None, description="Guardian's cryptographic signature")
    admitted_at: Optional[datetime] = Field(default=None, description="When module was admitted")
    admission_duration_ms: float = Field(default=0.0, description="Total time through all 6 stages")

    model_config = ConfigDictLike(json_schema_extra={
        "example": {
            "module_id": "mod-001",
            "module_name": "skill_python_refactor",
            "version": "1.0.0",
            "author": "anthropic/engineers",
            "capabilities": ["python_refactor", "code_review"],
            "manifest_hash": "abc123...",
            "guardian_signature": "sig_xyz...",
            "admitted_at": "2025-12-02T12:34:56Z",
            "admission_duration_ms": 245.3
        }
    })


class ModuleQuarantinedReceipt(BaseReceipt):
    """Receipt for module quarantine events."""
    domain: Literal["organism"] = "organism"
    module_id: str = Field(description="ID of quarantined module")
    module_name: str = Field(description="Name of module")
    status_before: str = Field(description="Module status before quarantine")
    quarantine_reason: str = Field(description="Reason for quarantine")
    evidence: List[str] = Field(default_factory=list, description="Evidence/logs leading to quarantine")
    quarantined_by: str = Field(default="system", description="Who triggered quarantine")

    model_config = ConfigDictLike(json_schema_extra={
        "example": {
            "module_id": "mod-001",
            "module_name": "skill_python_refactor",
            "status_before": "active",
            "quarantine_reason": "Security violation detected",
            "evidence": ["Unauthorized file access attempt"],
            "quarantined_by": "guardian"
        }
    })
