# Data Model: Email Migration (013)

**Storage:** SQLite (VM122) — extensão do schema existente.

---

## Tabelas

### `migration_jobs`

| Coluna | Tipo | Descrição |
|--------|------|-----------|
| `id` | INTEGER PK | |
| `tenant_id` | INTEGER FK | Tenant destino (VM112…) |
| `ticket_id` | INTEGER FK NULL | Ticket Desk associado |
| `domain` | TEXT | Domínio migrado |
| `phase` | TEXT | discovered, preflight, initial_sync, delta_sync, cutover_ready, dns_cutover, final_sync, verified, closed, failed |
| `migration_gate` | TEXT | blocked, warning, ready_for_dns |
| `source_server_label` | TEXT | Ex.: "cPanel HostGator", "Exchange O365" |
| `dest_imap_host` | TEXT | mail.dominio.com |
| `notes` | TEXT | |
| `approved_by` | TEXT NULL | user_id ops_lead |
| `approved_at` | TEXT NULL | ISO8601 |
| `dns_cutover_at` | TEXT NULL | |
| `created_at` | TEXT | |
| `updated_at` | TEXT | |

### `migration_mailboxes`

| Coluna | Tipo | Descrição |
|--------|------|-----------|
| `id` | INTEGER PK | |
| `job_id` | INTEGER FK | |
| `email` | TEXT | user@domain.com |
| `source_type` | TEXT | imap, imap_oauth, pst, mbox, eml, tgz, zimbra, dovecot |
| `source_host` | TEXT NULL | |
| `source_user` | TEXT NULL | |
| `credentials_ref` | TEXT NULL | ID encriptado (não password plain) |
| `pst_path` | TEXT NULL | caminho upload |
| `folder_map_json` | TEXT NULL | `{"Sent Items":"Sent"}` |
| `messages_source` | INTEGER DEFAULT 0 | última contagem origem |
| `messages_dest` | INTEGER DEFAULT 0 | última contagem destino |
| `bytes_source` | INTEGER DEFAULT 0 | |
| `bytes_dest` | INTEGER DEFAULT 0 | |
| `sync_percent` | REAL DEFAULT 0 | |
| `last_error` | TEXT NULL | |
| `status` | TEXT | pending, syncing, ok, error, quarantine |
| `created_at` | TEXT | |
| `updated_at` | TEXT | |

### `migration_runs`

| Coluna | Tipo | Descrição |
|--------|------|-----------|
| `id` | INTEGER PK | |
| `job_id` | INTEGER FK | |
| `mailbox_id` | INTEGER FK NULL | NULL = job-level |
| `run_type` | TEXT | preflight, initial, delta, final, verify, pst_extract, pst_upload, tgz_export, tgz_import |
| `tool` | TEXT | imapsync, readpst, imap-upload, zmmailbox, doveadm, verify |
| `status` | TEXT | queued, running, success, partial, failed |
| `exit_code` | INTEGER NULL | |
| `log_path` | TEXT | |
| `stats_json` | TEXT | `{"transferred":1200,"errors":2,"folders":14}` |
| `started_at` | TEXT | |
| `finished_at` | TEXT NULL | |
| `triggered_by` | TEXT | user_id ou `worker` |

### `migration_gate_checks`

| Coluna | Tipo | Descrição |
|--------|------|-----------|
| `id` | INTEGER PK | |
| `job_id` | INTEGER FK | |
| `check_id` | TEXT | count_ratio, inbox_complete, sent_complete, pst_quarantine, delta_pending, manual_approval |
| `status` | TEXT | pass, warn, fail |
| `message` | TEXT | |
| `checked_at` | TEXT | |

### `migration_credentials` (vault)

| Coluna | Tipo | Descrição |
|--------|------|-----------|
| `id` | TEXT PK | uuid |
| `mailbox_id` | INTEGER FK | |
| `secret_blob` | BLOB | Fernet encrypted JSON `{password, oauth_token}` |
| `created_at` | TEXT | |
| `expires_at` | TEXT NULL | OAuth |

---

## Estados — `phase`

```
discovered → preflight → initial_sync → delta_sync → cutover_ready
    → dns_cutover → final_sync → verified → closed

Qualquer fase → failed (retry manual)
```

## Estados — `migration_gate`

| Valor | Significado | DNS |
|-------|-------------|-----|
| `blocked` | Checks críticos falham | ❌ Bloqueado |
| `warning` | Quase pronto; revisão humana | ❌ Bloqueado |
| `ready_for_dns` | Aprovado ops_lead + checks OK | ✅ Permitido |

---

## Payload ticket (extensão)

```json
{
  "migration_job_id": 42,
  "migration_phase": "delta_sync",
  "migration_gate": "warning",
  "migration_sync_percent": 87.5
}
```

---

## Índices

```sql
CREATE INDEX idx_migration_jobs_domain ON migration_jobs(domain);
CREATE INDEX idx_migration_jobs_phase ON migration_jobs(phase);
CREATE INDEX idx_migration_mailboxes_job ON migration_mailboxes(job_id);
CREATE INDEX idx_migration_runs_job ON migration_runs(job_id);
```
