# Provisioning and tenant lifecycle

## Lifecycle states

`TenantStatus` contains the following states:

| State | Meaning | Allowed next operation |
| --- | --- | --- |
| `provisioning` | Central record exists and database setup is running. | Complete provisioning or retry after failure. |
| `pending_admin` | Database/schema/seeds passed smoke checks. | Create the first store admin. |
| `active` | Store admin exists and store access is enabled. | Suspend or archive (suspend first). |
| `suspended` | Data is retained but store authentication/routing is blocked. | Activate or archive. |
| `failed` | A provisioning step failed; the error is recorded. | Retry provisioning. |
| `archived` | Store is permanently unavailable to users. | Queue database destruction after explicit confirmation. |

## Create and provision flow

`TenantProvisioningService::create()` performs a central transaction:

1. Trims and slugifies the name/slug.
2. Rejects empty values, reserved slugs (`www`, `console`, `admin`, `api`, `mail`, `support`, `status`), duplicate slugs, and unsupported locales.
3. Creates the tenant in `provisioning` state.
4. Creates the primary platform subdomain (`{slug}.{TENANCY_PLATFORM_DOMAIN}`).
5. Dispatches `ProvisionTenantJob` after commit through the `TenantCreated` event.

The job is queueable, retries three times, and uses `WithoutOverlapping` per tenant. `TenantProvisioningService::provision()` claims a run under a central `lockForUpdate()` transaction. A second caller sees the active run and returns without duplicating work.

The provisioning steps are:

1. Create the tenant database if it does not exist.
2. Run `tenants:migrate` against that database.
3. Run `tenants:seed` using `TenantDatabaseSeeder`; the root `db:seed` command
   is reserved for central control-plane data.
4. Initialize all `StoreFeature` rows.
5. Smoke-check `admins`, `roles`, `permissions`, `countries`, and `delivery_times`, plus the local `store_admin` super-admin role.
6. Activate the platform subdomain.
7. Set the tenant to `pending_admin` and complete the provisioning run.

Failures set the tenant and run to `failed` and save a bounded error message. The database is not silently destroyed; cleanup is an explicit protected operation.

## First store administrator

`TenantAdminProvisioningService::createInitial()` locks the central tenant row and accepts only `pending_admin` tenants. It then:

- initializes the tenant connection;
- creates the local `Admin` in a transaction;
- forces `force_password_change=true`;
- assigns the local `super-admin` role;
- marks the tenant `active` and records `initial_store_admin_created_at`.

The same workflow is exposed by the console Create Store Admin action and `tenant:admin:create`. The password is never copied to central storage.

## Lifecycle mutations

`TenantProvisioningService::suspend()`, `activate()`, and `archive()` all lock the central row in a transaction. This prevents concurrent lifecycle actions from overwriting one another.

- Only `active` tenants can be suspended.
- Only `suspended` tenants can be activated.
- Active tenants must be suspended before archiving.
- Archive is intentionally reversible only at the database-record level; routing remains unavailable.

## Commands

All commands resolve a central tenant by its ID unless stated otherwise:

```bash
# Create the central record and queue provisioning.
php artisan tenant:create "Acme Store" acme --locale=ar

# Retry or explicitly run provisioning.
php artisan tenant:provision <tenant-id>

# Run pending migrations or seed one tenant.
php artisan tenant:migrate <tenant-id>
php artisan tenant:migrate <tenant-id> --pretend
php artisan tenant:seed <tenant-id>

# Create the first local operator.
php artisan tenant:admin:create <tenant-id> "Store Owner" owner owner@example.com 'temporary-password'

# Lifecycle actions.
php artisan tenant:suspend <tenant-id>
php artisan tenant:activate <tenant-id>
php artisan tenant:archive <tenant-id> --confirm

# Destruction is queued and requires an archived tenant.
php artisan tenant:database:destroy <tenant-id> --confirm
```

Use the console panel for normal company operations. Use destructive commands only from an audited deployment/operator context.
