# `Sigra.Account.Deletion`
[🔗](https://github.com/sztheory/sigra/blob/v1.20.0/lib/sigra/account/deletion.ex#L1)

Account deletion lifecycle: schedule, cancel, execute.

Implements configurable account deletion with three strategies:
- `:soft_delete` - Mark as deleted, preserve data (default)
- `:hard_delete` - Cascade delete all Sigra tables, remove user row
- `:anonymize` - Replace PII with anonymous values, preserve row

## Security Properties

- Grace period (D-14): configurable delay before finalization (default 14 days)
- Immediate deactivation (D-13): all sessions/tokens revoked on schedule
- Pending email change auto-cancelled (D-24)
- MFA data cleared at finalization only (D-13)
- Cooldown rate limiting (D-22): 24h after cancellation
- Telemetry events for audit trail (D-26)

# `cancel`
*since 0.8.0* 

```elixir
@spec cancel(module(), map(), keyword()) :: {:ok, map()} | {:error, :not_scheduled}
```

Cancel scheduled deletion and reactivate account.

Clears deletion timestamps and original_email. The user must
re-authenticate to log in (all sessions were revoked on scheduling).

## Options

- `:changeset_fn` - `(user, attrs -> Ecto.Changeset.t())` for user updates

## Returns

- `{:ok, user}` on success
- `{:error, :not_scheduled}` if no deletion is scheduled

# `execute`
*since 0.8.0* 

```elixir
@spec execute(module(), map(), keyword()) :: {:ok, atom()} | {:error, :not_scheduled}
```

Execute deletion based on configured strategy.

Called by the Oban worker when the grace period expires, or
immediately for zero-grace-period configurations.

## Strategies

- `:soft_delete` - No additional action (deleted_at already set)
- `:hard_delete` - Cascade delete Sigra tables, delete user row
- `:anonymize` - Replace email with `deleted_{id}@deleted.invalid`,
  clear hashed_password, null optional PII fields

All strategies clear MFA data (TOTP secrets, backup codes) per D-13.

## Options

- `:changeset_fn` - `(user, attrs -> Ecto.Changeset.t())` for user updates
- `:token_query_fn` - `(user, contexts -> Ecto.Queryable.t())` for token cleanup
- `:config` - Config with `:deletion` section

## Returns

- `{:ok, strategy}` on success
- `{:error, :not_scheduled}` if user is not scheduled for deletion

# `schedule`
*since 0.8.0* 

```elixir
@spec schedule(module(), map(), keyword()) ::
  {:ok, map(), DateTime.t()} | {:error, :already_scheduled}
```

Schedule account deletion with grace period.

Immediately deactivates the account (revokes sessions/tokens), sets
deletion timestamps, and preserves the original email for potential
restoration.

## Options

- `:changeset_fn` - `(user, attrs -> Ecto.Changeset.t())` for user updates
- `:session_store` - SessionStore for session revocation
- `:token_query_fn` - `(user, contexts -> Ecto.Queryable.t())` for token cleanup
- `:config` - Config with `:deletion` section (strategy, grace_period_days, etc.)

## Returns

- `{:ok, user, scheduled_deletion_at}` on success
- `{:error, :already_scheduled}` if deletion already scheduled

# `scheduled?`
*since 0.8.0* 

```elixir
@spec scheduled?(map()) :: boolean()
```

Check if deletion is scheduled.

Returns `true` when both `deleted_at` and `scheduled_deletion_at`
are set (account is in grace period).

# `status`
*since 0.8.0* 

```elixir
@spec status(map()) :: {:scheduled, non_neg_integer()} | :not_scheduled | :deleted
```

Get deletion status.

Returns:
- `{:scheduled, days_remaining}` when in grace period
- `:not_scheduled` when no deletion is pending
- `:deleted` when finalized (deleted_at set but no scheduled_deletion_at)

# `within_cooldown?`
*since 0.8.0* 

```elixir
@spec within_cooldown?(DateTime.t(), non_neg_integer()) :: boolean()
```

Check if a cancellation is within the cooldown period.

Used to enforce the 24h cooldown after cancelling deletion (D-22).
Prevents abuse of the request/cancel cycle.

## Parameters

- `cancelled_at` - The DateTime when deletion was cancelled
- `cooldown_hours` - Number of hours in the cooldown window

---

*Consult [api-reference.md](api-reference.md) for complete listing*
