# CreateAuditPolicy

## Overview

CreateAuditPolicy creates a new audit policy in DRAFT status. The command accepts a target entity name, optional company scope, a single operation type, and optional field-level rules with sensitivity modes. Policies are created in DRAFT to allow review before activation. The policy's scope must match the target entity's registered scope.

This command requires the `manageAuditPolicies` permission at the appropriate scope.

## Business Rules

- Policy is always created in DRAFT status
- entityName is required and must reference a registered auditable entity
- companyId is optional; if provided, the target entity must be COMPANY_BOUND; if null, the target entity must be GLOBAL
- operationType is required and must be one of CREATE, UPDATE, or DELETE; invalid values are rejected
- Field names in field-level rules must be non-empty strings
- Field-level rules are optional; each rule specifies a fieldName and optional sensitivityMode
- Field names in field-level rules must be a subset of the entity's registered auditableFields
- Duplicate field names within the same policy are rejected
- sensitivityMode defaults to CAPTURE if not specified
- Caller must hold `manageAuditPolicies` permission at the appropriate scope
- A caller with company-scoped `manageAuditPolicies` can only create policies for their assigned companies
- A caller with global-scoped `manageAuditPolicies` can only create global policies (companyId null)

## Process Flow

```mermaid
flowchart TD
    A[Receive create request] --> B{Caller has manageAuditPolicies?}
    B -->|No| C[Return error: INSUFFICIENT_PERMISSION]
    B -->|Yes| B2{Caller scope covers requested policy scope?}
    B2 -->|No| B3[Return error: UNAUTHORIZED]
    B2 -->|Yes| D{entityName registered?}
    D -->|No| E[Return error: ENTITY_TYPE_NOT_REGISTERED]
    D -->|Yes| F{Policy scope matches entity scope?}
    F -->|No| G[Return error: SCOPE_MISMATCH]
    F -->|Yes| G2{operationType valid?}
    G2 -->|No| G3[Return error: INVALID_OPERATION_TYPE]
    G2 -->|Yes| H{Validate field rules}
    H -->|Invalid| I[Return validation error]
    H -->|Valid| J[Create AuditPolicy in DRAFT]
    J --> K[Create PolicyFieldRule records]
    K --> L[Return created policy]
```

## External Dependencies

- None

## Error Scenarios

- **INSUFFICIENT_PERMISSION**: Caller lacks the required command permission (shared error from defineCommand)
- **UNAUTHORIZED**: Caller's permission scope does not cover this policy (e.g., company-scoped caller managing a different company's policy, or global-scoped caller managing a company-scoped policy)
- **ENTITY_TYPE_NOT_REGISTERED**: entityName does not match any registered auditable entity
- **SCOPE_MISMATCH**: companyId provided for a global entity, or companyId null for a company-bound entity
- **INVALID_FIELD_NAME**: A field name in field-level rules is empty or not in the entity's auditableFields
- **DUPLICATE_FIELD_NAME**: The same field name appears more than once in field-level rules
- **INVALID_OPERATION_TYPE**: operationType is missing or not one of CREATE, UPDATE, DELETE
- **INVALID_SENSITIVITY_MODE**: sensitivityMode is not one of CAPTURE, MASK, HASH, EXCLUDE

## Test Cases

- creates policy in DRAFT status
- creates policy with company scope for company-bound entity
- creates policy with global scope for global entity
- creates policy with field-level rules and sensitivity modes
- creates policy without field-level rules (audits all eligible fields)
- rejects when entityName is not registered
- rejects company-scoped policy for global entity
- rejects global policy for company-bound entity
- rejects duplicate field names in field-level rules
- rejects field names not in entity's auditableFields
- rejects invalid sensitivity mode
- rejects invalid operation type
- rejects empty field name in field-level rules
- defaults sensitivityMode to CAPTURE when not specified
- rejects when caller lacks manageAuditPolicies permission
- rejects when company-scoped caller creates policy for a different company
- creates policy with global scope when companyId is explicitly null
- rejects when global-scoped caller creates company-scoped policy
