# Feature 005: Policy Lifecycle Management

## Overview
Implement proper lifecycle management for policies that are deleted in Intune, including soft delete, UI indicators, and orphaned policy handling.

## Problem Statement
Currently, when a policy is deleted in Intune:
- ❌ Policy remains in TenantAtlas database indefinitely
- ❌ No indication that policy no longer exists in Intune
- ❌ Backup Items reference "ghost" policies
- ❌ Users cannot distinguish between active and deleted policies

**Discovered during**: Feature 004 manual testing (user deleted policy in Intune)

## Goals
- **Primary**: Implement soft delete for policies removed from Intune
- **Secondary**: Show clear UI indicators for deleted policies
- **Tertiary**: Maintain referential integrity for Backup Items and Policy Versions

## Scope
- **Policy Sync**: Detect missing policies during `SyncPoliciesJob`
- **Data Model**: Add `deleted_at`, `deleted_by` columns (Laravel Soft Delete pattern)
- **UI**: Badge indicators, filters, restore capability
- **Audit**: Log when policies are soft-deleted and restored

---

## User Stories

### User Story 1 - Automatic Soft Delete on Sync

**As a system administrator**, I want policies deleted in Intune to be automatically marked as deleted in TenantAtlas, so that the inventory reflects the current Intune state.

**Acceptance Criteria:**
1. **Given** a policy exists in TenantAtlas with `external_id` "abc-123",
   **When** the next policy sync runs and "abc-123" is NOT returned by Graph API,
   **Then** the policy is soft-deleted (sets `deleted_at = now()`)

2. **Given** a soft-deleted policy,
   **When** it re-appears in Intune (same `external_id`),
   **Then** the policy is automatically restored (`deleted_at = null`)

3. **Given** multiple policies are deleted in Intune,
   **When** sync runs,
   **Then** all missing policies are soft-deleted in a single transaction

---

### User Story 2 - UI Indicators for Deleted Policies

**As an admin**, I want to see clear indicators when viewing deleted policies, so I understand their status.

**Acceptance Criteria:**
1. **Given** I view a Backup Item referencing a deleted policy,
   **When** I see the policy name,
   **Then** it shows a red "Deleted" badge next to the name

2. **Given** I view the Policies list,
   **When** I enable the "Show Deleted" filter,
   **Then** deleted policies appear with:
   - Red "Deleted" badge
   - Deleted date in "Last Synced" column
   - Grayed-out appearance

3. **Given** a policy was deleted,
   **When** I view the Policy detail page,
   **Then** I see:
   - Warning banner: "This policy was deleted from Intune on {date}"
   - All data remains readable (versions, snapshots, metadata)

---

### User Story 3 - Restore Workflow

**As an admin**, I want to restore a deleted policy from backup, so I can recover accidentally deleted configurations.

**Acceptance Criteria:**
1. **Given** I view a deleted policy's detail page,
   **When** I click the "Restore to Intune" action,
   **Then** the restore wizard opens pre-filled with the latest policy snapshot

2. **Given** a policy is successfully restored to Intune,
   **When** the next sync runs,
   **Then** the policy is automatically undeleted in TenantAtlas (`deleted_at = null`)

---

## Functional Requirements

### Data Model

**FR-005.1**: Policies table MUST use Laravel Soft Delete pattern:
```php
Schema::table('policies', function (Blueprint $table) {
    $table->softDeletes(); // deleted_at
    $table->string('deleted_by')->nullable(); // admin email who triggered deletion
});
```

**FR-005.2**: Policy model MUST use `SoftDeletes` trait:
```php
use Illuminate\Database\Eloquent\SoftDeletes;

class Policy extends Model {
    use SoftDeletes;
}
```

### Policy Sync Behavior

**FR-005.3**: `PolicySyncService::syncPolicies()` MUST detect missing policies:
- Collect all `external_id` values returned by Graph API
- Query existing policies for this tenant: `whereNotIn('external_id', $currentExternalIds)`
- Soft delete missing policies: `each(fn($p) => $p->delete())`

**FR-005.4**: System MUST restore policies that re-appear:
- Check if policy exists with `Policy::withTrashed()->where('external_id', $id)->first()`
- If soft-deleted: call `$policy->restore()`
- Update `last_synced_at` timestamp

**FR-005.5**: System MUST log audit entries:
- `policy.deleted` (when soft-deleted during sync)
- `policy.restored` (when re-appears in Intune)

### UI Display

**FR-005.6**: PolicyResource table MUST:
- Default query: exclude soft-deleted policies
- Add filter "Show Deleted" (includes `withTrashed()` in query)
- Show "Deleted" badge for soft-deleted policies

**FR-005.7**: BackupItemsRelationManager MUST:
- Show "Deleted" badge when `policy->trashed()` returns true
- Allow viewing deleted policy details (read-only)

**FR-005.8**: Policy detail view MUST:
- Show warning banner when policy is soft-deleted
- Display deletion date and reason (if available)
- Disable edit actions (policy no longer exists in Intune)

---

## Non-Functional Requirements

**NFR-005.1**: Soft delete MUST NOT break existing features:
- Backup Items keep valid foreign keys
- Policy Versions remain accessible
- Restore functionality works for deleted policies

**NFR-005.2**: Performance: Sync detection MUST NOT cause N+1 queries:
- Use single `whereNotIn()` query to find missing policies
- Batch soft-delete operation

**NFR-005.3**: Data retention: Soft-deleted policies MUST be retained for audit purposes (no automatic purging)

---

## Implementation Plan

### Phase 1: Data Model (30 min)
1. Create migration for `policies` soft delete columns
2. Add `SoftDeletes` trait to Policy model
3. Run migration on dev environment

### Phase 2: Sync Logic (1 hour)
1. Update `PolicySyncService::syncPolicies()`
   - Track current external IDs from Graph
   - Soft delete missing policies
   - Restore re-appeared policies
2. Add audit logging
3. Test with manual deletion in Intune

### Phase 3: UI Indicators (1.5 hours)
1. Update `PolicyResource`:
   - Add "Show Deleted" filter
   - Add "Deleted" badge column
   - Modify query to exclude deleted by default
2. Update `BackupItemsRelationManager`:
   - Show "Deleted" badge for `policy->trashed()`
3. Update Policy detail view:
   - Warning banner for deleted policies
   - Disable edit actions

### Phase 4: Testing (1 hour)
1. Unit tests:
   - Test soft delete on sync
   - Test restore on re-appearance
2. Feature tests:
   - E2E sync with deleted policies
   - UI filter behavior
3. Manual QA:
   - Delete policy in Intune → sync → verify soft delete
   - Re-create policy → sync → verify restore

**Total Estimated Duration**: ~4-5 hours

---

## Risks & Mitigations

| Risk | Mitigation |
|------|------------|
| Foreign key constraints block soft delete | Laravel soft delete only sets timestamp, constraints remain valid |
| Bulk delete impacts performance | Use chunked queries if tenant has 1000+ policies |
| Deleted policies clutter UI | Default filter hides them, "Show Deleted" is opt-in |

---

## Success Criteria
1. ✅ Policies deleted in Intune are soft-deleted in TenantAtlas within 1 sync cycle
2. ✅ Re-appearing policies are automatically restored
3. ✅ UI clearly indicates deleted status
4. ✅ Backup Items and Versions remain accessible for deleted policies
5. ✅ No breaking changes to existing features

---

## Related Features
- Feature 004: Assignments & Scope Tags (discovered this issue during testing)
- Feature 001: Backup/Restore (must work with deleted policies)

---

**Status**: Planned (Post-Feature 004)  
**Priority**: P2 (Quality of Life improvement)  
**Created**: 2025-12-22  
**Author**: AI + Ahmed  
**Next Steps**: Implement after Feature 004 Phase 3 testing complete
