# State Machine Metadata Specification

## Overview

This document defines the metadata specification for RichChoice objects when used in state machine contexts. The metadata drives all state machine behavior including valid transitions, permissions, and state properties.

## Metadata Structure

Metadata is stored in the `metadata` dictionary field of a RichChoice object:

```python
RichChoice(
    value="state_value",
    label="State Label",
    metadata={
        # Metadata fields go here
    }
)
```

## Required Fields

### `can_transition_to`

**Type**: `List[str]`  
**Required**: Yes  
**Description**: List of valid target state values this state can transition to.

**Example**:
```python
metadata={
    "can_transition_to": ["approved", "rejected", "escalated"]
}
```

**Validation Rules**:
- Must be present in every state's metadata (use empty list `[]` for terminal states)
- All referenced state values must exist in the same choice group
- Terminal states (marked with `is_final: True`) should have empty list

**Common Patterns**:
```python
# Initial state with multiple transitions
metadata={"can_transition_to": ["in_review", "rejected"]}

# Intermediate state
metadata={"can_transition_to": ["approved", "needs_revision"]}

# Terminal state
metadata={"can_transition_to": [], "is_final": True}
```

## Optional Fields

### `is_final`

**Type**: `bool`  
**Default**: `False`  
**Description**: Marks a state as terminal/final with no outgoing transitions.

**Example**:
```python
metadata={
    "is_final": True,
    "can_transition_to": []
}
```

**Validation Rules**:
- If `is_final: True`, `can_transition_to` must be empty
- Terminal states cannot have outgoing transitions

### `is_actionable`

**Type**: `bool`  
**Default**: `False`  
**Description**: Indicates whether actions can be taken in this state.

**Example**:
```python
metadata={
    "is_actionable": True,
    "can_transition_to": ["approved", "rejected"]
}
```

**Use Cases**:
- Marking states where user input is required
- Identifying states in moderation queues
- Filtering for states needing attention

### `requires_moderator`

**Type**: `bool`  
**Default**: `False`  
**Description**: Transition to/from this state requires moderator permissions.

**Example**:
```python
metadata={
    "requires_moderator": True,
    "can_transition_to": ["approved"]
}
```

**Permission Check**:
- User must have `is_staff=True`, OR
- User must have `moderation.can_moderate` permission, OR
- User must be in "moderators", "admins", or "staff" group

### `requires_admin_approval`

**Type**: `bool`  
**Default**: `False`  
**Description**: Transition requires admin-level permissions.

**Example**:
```python
metadata={
    "requires_admin_approval": True,
    "can_transition_to": ["published"]
}
```

**Permission Check**:
- User must have `is_superuser=True`, OR
- User must have `moderation.can_admin` permission, OR
- User must be in "admins" group

**Note**: Admin approval implies moderator permission. Don't set both flags.

## Extended Metadata Fields

### `transition_callbacks`

**Type**: `Dict[str, str]`  
**Optional**: Yes  
**Description**: Callback function names to execute during transitions.

**Example**:
```python
metadata={
    "transition_callbacks": {
        "on_enter": "handle_approval",
        "on_exit": "cleanup_pending",
    }
}
```

### `estimated_duration`

**Type**: `int` (seconds)  
**Optional**: Yes  
**Description**: Expected duration for remaining in this state.

**Example**:
```python
metadata={
    "estimated_duration": 86400,  # 24 hours
    "can_transition_to": ["approved"]
}
```

### `notification_triggers`

**Type**: `List[str]`  
**Optional**: Yes  
**Description**: Notification types to trigger on entering this state.

**Example**:
```python
metadata={
    "notification_triggers": ["moderator_assigned", "user_notified"],
    "can_transition_to": ["approved"]
}
```

## Complete Examples

### Example 1: Basic Moderation Workflow

```python
from backend.apps.core.choices.base import RichChoice

moderation_states = [
    # Initial state
    RichChoice(
        value="pending",
        label="Pending Review",
        description="Awaiting moderator assignment",
        metadata={
            "can_transition_to": ["in_review", "rejected"],
            "is_actionable": True,
        }
    ),
    
    # Processing state
    RichChoice(
        value="in_review",
        label="Under Review",
        description="Being reviewed by moderator",
        metadata={
            "can_transition_to": ["approved", "rejected", "escalated"],
            "requires_moderator": True,
            "is_actionable": True,
        }
    ),
    
    # Escalation state
    RichChoice(
        value="escalated",
        label="Escalated to Admin",
        description="Requires admin decision",
        metadata={
            "can_transition_to": ["approved", "rejected"],
            "requires_admin_approval": True,
            "is_actionable": True,
        }
    ),
    
    # Terminal states
    RichChoice(
        value="approved",
        label="Approved",
        description="Approved and published",
        metadata={
            "can_transition_to": [],
            "is_final": True,
            "requires_moderator": True,
        }
    ),
    
    RichChoice(
        value="rejected",
        label="Rejected",
        description="Rejected and archived",
        metadata={
            "can_transition_to": [],
            "is_final": True,
            "requires_moderator": True,
        }
    ),
]
```

### Example 2: Content Publishing Pipeline

```python
publishing_states = [
    RichChoice(
        value="draft",
        label="Draft",
        metadata={
            "can_transition_to": ["submitted", "archived"],
            "is_actionable": True,
        }
    ),
    
    RichChoice(
        value="submitted",
        label="Submitted for Review",
        metadata={
            "can_transition_to": ["draft", "approved", "rejected"],
            "requires_moderator": True,
        }
    ),
    
    RichChoice(
        value="approved",
        label="Approved",
        metadata={
            "can_transition_to": ["published", "draft"],
            "requires_moderator": True,
        }
    ),
    
    RichChoice(
        value="published",
        label="Published",
        metadata={
            "can_transition_to": ["archived"],
            "requires_admin_approval": True,
        }
    ),
    
    RichChoice(
        value="archived",
        label="Archived",
        metadata={
            "can_transition_to": [],
            "is_final": True,
        }
    ),
    
    RichChoice(
        value="rejected",
        label="Rejected",
        metadata={
            "can_transition_to": ["draft"],
            "requires_moderator": True,
        }
    ),
]
```

## Validation Rules

### Rule 1: Transition Reference Validity
All states in `can_transition_to` must exist in the same choice group.

**Invalid**:
```python
RichChoice("pending", "Pending", metadata={
    "can_transition_to": ["nonexistent_state"]  # ❌ State doesn't exist
})
```

### Rule 2: Terminal State Consistency
States marked `is_final: True` must have empty `can_transition_to`.

**Invalid**:
```python
RichChoice("approved", "Approved", metadata={
    "is_final": True,
    "can_transition_to": ["published"]  # ❌ Final state has transitions
})
```

### Rule 3: Permission Hierarchy
`requires_admin_approval: True` implies moderator permissions.

**Redundant** (but not invalid):
```python
metadata={
    "requires_admin_approval": True,
    "requires_moderator": True,  # ⚠️ Redundant
}
```

**Correct**:
```python
metadata={
    "requires_admin_approval": True,  # ✅ Admin implies moderator
}
```

### Rule 4: Cycle Detection
State machines should generally avoid cycles (except for revision flows).

**Warning** (may be valid for revision workflows):
```python
# State A -> State B -> State A creates a cycle
RichChoice("draft", "Draft", metadata={"can_transition_to": ["review"]}),
RichChoice("review", "Review", metadata={"can_transition_to": ["draft"]}),
```

### Rule 5: Reachability
All states should be reachable from initial states.

**Invalid**:
```python
# "orphan" state is unreachable
RichChoice("pending", "Pending", metadata={"can_transition_to": ["approved"]}),
RichChoice("approved", "Approved", metadata={"is_final": True}),
RichChoice("orphan", "Orphan", metadata={"can_transition_to": []}),  # ❌
```

## Testing Metadata

Use `MetadataValidator` to test your metadata:

```python
from backend.apps.core.state_machine import MetadataValidator

validator = MetadataValidator("your_choice_group", "your_domain")
result = validator.validate_choice_group()

if not result.is_valid:
    print(validator.generate_validation_report())
```

## Anti-Patterns

### ❌ Missing Transitions
```python
# Don't leave can_transition_to undefined
RichChoice("pending", "Pending", metadata={})  # Missing!
```

### ❌ Overly Complex Graphs
```python
# Avoid states with too many outgoing transitions
metadata={
    "can_transition_to": [
        "state1", "state2", "state3", "state4", 
        "state5", "state6", "state7", "state8"
    ]  # Too many options!
}
```

### ❌ Inconsistent Permission Requirements
```python
# Don't require admin without requiring moderator first
metadata={
    "requires_admin_approval": True,
    "requires_moderator": False,  # Inconsistent!
}
```

## Best Practices

1. ✅ Always define `can_transition_to` (use `[]` for terminal states)
2. ✅ Use `is_final: True` for all terminal states
3. ✅ Mark actionable states with `is_actionable: True`
4. ✅ Apply permission flags at the appropriate level
5. ✅ Keep state graphs simple and linear when possible
6. ✅ Document complex transition logic in descriptions
7. ✅ Run validation during development
8. ✅ Test all transition paths

## Version History

- **v1.0** (2025-12-20): Initial specification
