# ReplaceAuditPolicy

## Overview

ReplaceAuditPolicy atomically deactivates the current ACTIVE policy and activates a replacement DRAFT policy for the same (entityName, companyId, operationType) combination in a single transaction. This ensures no gap in audit coverage during policy updates. The replacement policy must be in DRAFT status and target the same entity, operation, and scope as the existing ACTIVE policy.

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

## Business Rules

- The replacement policy must be in DRAFT status
- An existing ACTIVE policy must exist for the same entityName; if none exists, the operation is rejected (use activateAuditPolicy instead)
- The existing ACTIVE policy must match the replacement policy's companyId and operationType; a mismatch is rejected as TARGET_MISMATCH
- The operation is atomic — either both the deactivation and activation succeed, or neither takes effect
- After completion, the previously ACTIVE policy is INACTIVE and the replacement is ACTIVE
- Caller must hold `manageAuditPolicies` permission at the appropriate scope
- A caller with company-scoped `manageAuditPolicies` can only replace policies for their assigned companies
- A caller with global-scoped `manageAuditPolicies` can only replace global policies

## Process Flow

```mermaid
flowchart TD
    A[Receive replace request] --> B{Caller has manageAuditPolicies?}
    B -->|No| C[Return error: INSUFFICIENT_PERMISSION]
    B -->|Yes| D{Replacement policy exists?}
    D -->|No| E[Return error: POLICY_NOT_FOUND]
    D -->|Yes| D2{Caller scope covers this policy?}
    D2 -->|No| D3[Return error: UNAUTHORIZED]
    D2 -->|Yes| F{Replacement in DRAFT status?}
    F -->|No| G[Return error: INVALID_STATE]
    F -->|Yes| H{Any ACTIVE policy exists for this entity?}
    H -->|No| I[Return error: NO_ACTIVE_POLICY_TO_REPLACE]
    H -->|Yes| H2{ACTIVE policy matches replacement's operation + scope?}
    H2 -->|No| H3[Return error: TARGET_MISMATCH]
    H2 -->|Yes| J[Begin transaction]
    J --> K[Set existing ACTIVE policy to INACTIVE]
    K --> L[Set replacement DRAFT policy to ACTIVE]
    L --> M[Commit transaction]
    M --> N[Return both updated policies]
```

## 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)
- **POLICY_NOT_FOUND**: Specified replacement policy ID does not exist
- **INVALID_STATE**: Replacement policy is not in DRAFT status
- **NO_ACTIVE_POLICY_TO_REPLACE**: No ACTIVE policy exists for the same entityName; use activateAuditPolicy instead
- **TARGET_MISMATCH**: The existing ACTIVE policy does not match the replacement's companyId or operationType

## Test Cases

- atomically deactivates existing ACTIVE policy and activates DRAFT replacement
- existing policy is INACTIVE after replacement
- replacement policy is ACTIVE after replacement
- no gap in audit coverage during replacement
- rejects when replacement policy is not in DRAFT status
- rejects when replacement policy is ACTIVE
- rejects when replacement policy is INACTIVE
- rejects when no existing ACTIVE policy for the same entity
- rejects when replacement targets different operationType than existing ACTIVE policy
- rejects when replacement targets different companyId than existing ACTIVE policy
- selects correct ACTIVE policy when multiple ACTIVE policies exist for the same entity
- rejects when replacement policy does not exist
- rejects when caller lacks manageAuditPolicies permission
- rejects when company-scoped caller replaces policy for a different company
- rejects when global-scoped caller replaces company-scoped policy
