# 🏗️ Formato de Schema Horizon - Especificação Completa

## 📋 Resumo Executivo

Este documento define o formato padrão para campos de schema no sistema Horizon. É um formato **minimalista, inteligente e pragmático** projetado para suportar 90+ campos sem redundância.

### Princípios Base
- **Minimalista**: Apenas 3-7 propriedades por campo em média
- **Inferência Inteligente**: Sistema deduz configurações automaticamente
- **Separação Clara**: Validação frontend ≠ Constraints de banco
- **Zero Redundância**: Nunca repetir informação

---

## 🚀 Quick Start

### Campo Básico (apenas 4 propriedades)
```javascript
{
  "key": "valor_venda",
  "type": "Number",
  "label": "Valor de Venda",
  "origin": "horizon-base/property"
}
```

### Campo Completo (seguindo ordem padrão)
```javascript
{
  "key": "dormitorios",
  "label": "Dormitórios",
  "description": "Número de dormitórios do imóvel",
  "type": "Number",
  "format": "count",
  "validation": { "min": 0, "max": 20 },
  "filterable": true,
  "composedLabel": "{{value}} dormitório{{p:s}}",
  "icon": "bedroom",
  "placeholder": "Ex: 3",
  "categories": ["dependencias", "principais"],
  "origin": "horizon-base/property"
}
```

---

## 🏗️ Ordem Padrão de Propriedades

**SEMPRE siga esta ordem ao definir campos:**

### 1. **IDENTIFICAÇÃO BÁSICA**
1. `key` - Identificador único
2. `label` - Nome para UI
3. `description` - Texto de ajuda/helper para formulários
4. `enum` - Valores possíveis 
5. `type` - Tipo de dado

### 2. **FORMATAÇÃO BÁSICA**
6. `format` - Como formatar na exibição
7. `unit` - Unidade de medida

### 3. **VALIDAÇÃO E BANCO (backend)**
8. `validation` - Regras de validação
9. `db` - Configurações específicas do banco

### 4. **COMPORTAMENTO DE BUSCA**
10. `searchable` - Pesquisável por texto
11. `filterable` - Aparece nos filtros
12. `sortable` - Pode ordenar

### 5. **RELAÇÕES E DEPENDÊNCIAS**
13. `parent` - Campo pai hierárquico
14. `conditions` - Condições de visibilidade

### 6. **DISPLAY/UI (camada frontend)**
15. `value` - Valor atual do campo (adicionado na camada de display)
16. `valueLabel` - Valor formatado para exibição (adicionado na camada de display)  
17. `composedLabel` - Template de exibição
18. `icon` - Ícone semântico

### 7. **FORMULÁRIOS (específico para inputs)**
19. `mask` - Máscara/validação específica
20. `placeholder` - Texto de exemplo no input

### 8. **CATEGORIZAÇÃO**
21. `categories` - Categorias múltiplas

### 9. **AUDITORIA**
22. `origin` - Origem do campo
23. `modifiedBy` - Histórico de modificações

---

## 📝 **LISTA COMPLETA DE METADADOS (23 total)**

**TODOS os metadados definidos na especificação:**

1. **`key`** - Identificador único
2. **`label`** - Nome para UI
3. **`description`** - Texto de ajuda/helper para formulários
4. **`enum`** - Valores possíveis 
5. **`type`** - Tipo de dado
6. **`format`** - Como formatar na exibição
7. **`unit`** - Unidade de medida
8. **`validation`** - Regras de validação
9. **`db`** - Configurações específicas do banco
10. **`searchable`** - Pesquisável por texto
11. **`filterable`** - Aparece nos filtros
12. **`sortable`** - Pode ordenar
13. **`parent`** - Campo pai hierárquico
14. **`conditions`** - Condições de visibilidade
15. **`value`** - Valor atual do campo *(adicionado na camada de display)*
16. **`valueLabel`** - Valor formatado para exibição *(adicionado na camada de display)*
17. **`composedLabel`** - Template de exibição
18. **`icon`** - Ícone semântico
19. **`mask`** - Máscara/validação específica
20. **`placeholder`** - Texto de exemplo no input
21. **`categories`** - Categorias múltiplas
22. **`origin`** - Origem do campo
23. **`modifiedBy`** - Histórico de modificações

**Exemplo seguindo a ordem:**
```javascript
{
  "key": "valor_venda",
  "label": "Valor de Venda",
  "description": "Preço de venda do imóvel em Reais",
  "type": "Number",
  "format": "currency",
  "unit": "BRL",
  "validation": { "min": 0, "max": 999999999.99, "precision": 2 },
  "filterable": true,
  "placeholder": "R$ 0,00",
  "categories": ["valores", "principais"],
  "origin": "horizon-base/property"
}
```

---

## 📚 Referência Completa

### 1. CAMPOS OBRIGATÓRIOS

| Campo | Tipo | Descrição | Exemplo |
|-------|------|-----------|---------|
| `key` | String | Identificador único (snake_case) | `"valor_venda"` |
| `label` | String | Nome para UI | `"Valor de Venda"` |
| `type` | String | Tipo Prisma | `"String"`, `"Number"`, `"String[]"` |
| `origin` | String | Origem do campo | `"horizon-base/property"` |

### 2. FORMATAÇÃO E DISPLAY

| Campo | Tipo | Descrição | Quando Usar |
|-------|------|-----------|-------------|
| `format` | String | Como formatar na exibição | Quando muda a forma de mostrar |
| `unit` | String | Unidade de medida | Com format currency/area |
| `mask` | String | Máscara/validação específica | Para CPF, phone, email |
| ~~`icon`~~ | ~~String~~ | ~~Ícone semântico~~ | **REMOVIDO** - Será adicionado na camada de display |
| ~~`composedLabel`~~ | ~~String~~ | ~~Template de exibição~~ | **REMOVIDO** - Será adicionado na camada de display |

#### Formats Disponíveis
```javascript
"currency"  // 1000 → R$ 1.000,00
"date"      // 2024-01-01 → 01/01/2024
"area"      // 100 → 100 m²
"distance"  // 1000 → 1 km
"percent"   // 0.15 → 15%
"count"     // Com pluralização automática
"geo-point" // Campo de geolocalização (coordenadas lat/lng)
```

#### Units Disponíveis
```javascript
// Moedas
"BRL", "USD", "EUR"

// Área
"m2", "km2", "hectare", "ft2"

// Distância  
"m", "km", "mi"
```

#### Masks Disponíveis

**🤖 INFERÊNCIA AUTOMÁTICA:**
Quando `format` + `unit` estão presentes, o `mask` é **automaticamente inferido**:

```javascript
// INFERÊNCIA AUTOMÁTICA (não precisa definir mask)
format: "currency" + unit: "BRL" → mask: "currency-brl"  // R$ 1.000,00
format: "currency" + unit: "USD" → mask: "currency-usd"  // $ 1,000.00
format: "percent"                → mask: "percent"       // 15%
format: "date"                   → mask: "date"          // DD/MM/YYYY
format: "area" + unit: "m2"      → mask: "area"          // 100 m²
format: "distance" + unit: "km"  → mask: "distance"      // 1,5 km
```

**📝 MASKS EXPLÍCITOS:**
Use apenas para casos específicos sem format/unit equivalente:

```javascript
"cpf"     // 000.000.000-00
"cnpj"    // 00.000.000/0000-00
"cep"     // 00000-000
"phone"   // (00) 00000-0000
"email"   // Validação de email
"url"     // Validação de URL
```

### 3. CATEGORIZAÇÃO E UI

| Campo | Tipo | Descrição | Exemplo |
|-------|------|-----------|---------|
| `categories` | String[] | Categorias múltiplas | `["valores", "principais"]` |
| `searchable` | Boolean | Pesquisável por texto | `true` |
| `filterable` | Boolean | Aparece nos filtros | `true` |
| `sortable` | Boolean | Pode ordenar | `true` |

#### Categories Comuns
```javascript
"valores"        // Campos monetários
"localizacao"    // Endereço e geolocalização
"dependencias"   // Cômodos e estrutura
"identificacao"  // Títulos e referências
"comercial"      // Info comerciais
"principais"     // Destaque no frontend
"caracteristicas" // Amenidades
"sistema"        // Campos internos
"seo"            // SEO e marketing
"corretor"       // Info do corretor
"condominio"     // Info do condomínio
```

### 4. ENUMERAÇÃO

| Campo | Tipo | Descrição | Formato |
|-------|------|-----------|---------|
| `enum` | Object | Valores possíveis | `{ "chave": "Label" }` |

```javascript
// Exemplo
"enum": {
  "venda": "Venda",
  "aluguel": "Aluguel",
  "temporada": "Temporada"
}
```

### 5. RELAÇÕES E DEPENDÊNCIAS

| Campo | Tipo | Descrição | Uso |
|-------|------|-----------|-----|
| `parent` | String | Campo pai hierárquico | `"tipo"` |
| `conditions` | String[] | Condições de visibilidade | `["operacao:venda"]` |

#### Conditions - Operadores Disponíveis

| Categoria | Operador | Descrição | Exemplo |
|-----------|----------|-----------|---------|
| **Comparação** | `equals` | Igual a | `"tipo.equals:apartamento"` |
|  | `not` | Diferente de | `"tipo.not:rural"` |
|  | `in` | Está na lista | `"finalidade.in:residencial,comercial"` |
|  | `notIn` | Não está na lista | `"status.notIn:vendido,alugado"` |
| **Strings** | `contains` | Contém (padrão) | `"operacao:venda"` |
|  | `startsWith` | Começa com | `"titulo.startsWith:Casa"` |
|  | `endsWith` | Termina com | `"titulo.endsWith:Centro"` |
| **Números** | `gt` | Maior que | `"valor.gt:100000"` |
|  | `gte` | Maior ou igual | `"valor.gte:100000"` |
|  | `lt` | Menor que | `"valor.lt:500000"` |
|  | `lte` | Menor ou igual | `"valor.lte:500000"` |
| **Existência** | `exists` | Campo preenchido/vazio | `"condominio.exists:true"` |

```javascript
// Exemplos práticos
"conditions": [
  "operacao:venda",              // operacao contém "venda" (padrão)
  "tipo.not:rural",              // tipo diferente de "rural"
  "valor.gte:100000",            // valor >= 100000
  "condominio.exists:true",      // condominio preenchido
  "finalidade.in:residencial,comercial"  // finalidade é residencial OU comercial
]
```

### 6. VALIDAÇÃO (Frontend/Zod)

| Campo | Tipo | Descrição | Exemplo |
|-------|------|-----------|---------|
| `validation` | Object | Regras de validação | Ver abaixo |

```javascript
"validation": {
  "required": true,      // Obrigatório
  "min": 0,             // Valor mínimo (números)
  "max": 999999999.99,  // Valor máximo (números)
  "minLength": 3,       // Tamanho mínimo (strings)
  "maxLength": 255,     // Tamanho máximo (strings)
  "precision": 2        // Casas decimais (infere decimal no DB)
}
```

**Importante**: NÃO incluir validações já cobertas por `mask` (cpf, email, etc)

### 7. BANCO DE DADOS

| Campo | Tipo | Descrição | Quando Usar |
|-------|------|-----------|-------------|
| `db` | Object | Configurações do banco | Quando diferente do padrão |

```javascript
"db": {
  "type": "decimal(12,2)",    // Tipo específico
  "unique": true,             // Constraint única
  "index": true,              // Criar índice
  "default": "now()",         // Valor padrão
  "primary": true,            // Chave primária
  "autoincrement": true,      // Auto incremento
  
  // CONFIGURAÇÃO PARA GEOLOCALIZAÇÃO
  "geoSource": ["latitude", "longitude"]  // Campos fonte para coordenadas
}
```

#### Tipos de DB Especiais
```javascript
"text"              // Texto longo
"decimal(12,2)"     // Decimal com precisão
"timestamp"         // Data/hora
"uuid"              // UUID
"jsonb"             // JSON binário (Postgres)
```

#### Tipos de Index
```javascript
true         // Índice simples (btree)
false        // Sem índice
"fulltext"   // Busca de texto (para descriptions)
"hash"       // Hash index
"gin"        // GIN index (Postgres)
"gist"       // GiST index (para dados geográficos PostGIS)
```

### 8. AUDITORIA

| Campo | Tipo | Descrição | Exemplo |
|-------|------|-----------|---------|
| `origin` | String | Onde foi criado | `"horizon-base/property"` |
| `modifiedBy` | String[] | Quem modificou | `["jetimob", "crm-sync"]` |

---

## 📋 Campos Removidos da Especificação Base

### `icon` e `composedLabel` - Camada de Display

**Decisão**: Os campos `icon` e `composedLabel` foram **removidos do schema base** e serão adicionados **manualmente na camada de display** apenas quando necessário.

**Justificativa**:
- Apenas poucos campos principais precisam de ícones (dormitórios, banheiros, vagas, área)
- `composedLabel` é específico da UI e pode variar por contexto
- Mantém o schema base focado nos dados essenciais
- Camada de display terá mais flexibilidade para customizações

**Implementação**:
```javascript
// Schema base (limpo)
{
  "key": "dormitorios",
  "type": "Number", 
  "label": "Dormitórios",
  "format": "count"
}

// Camada de display (quando necessário)
const displayConfig = {
  "dormitorios": {
    "icon": "bedroom",
    "composedLabel": "{{value}} dormitório{{p:s}}"
  }
}
```

---

## 🧠 Sistema de Inferência Inteligente

O sistema deduz automaticamente para reduzir redundância:

### Inferência de Tipo de Banco
```javascript
// String + maxLength → varchar(N)
{ "validation": { "maxLength": 100 } }  // Gera: db.type = "varchar(100)"

// String sem limite → varchar(255)
{ "type": "String" }  // Gera: db.type = "varchar(255)"

// String longo + searchable → text + fulltext
{ "type": "String", "searchable": true }  // Gera: db.type = "text", db.index = "fulltext"

// Number + precision → decimal
{ "validation": { "precision": 2, "max": 999999.99 } }  // Gera: db.type = "decimal(8,2)"

// Format + padrões → decimal automático
{ "format": "currency" }  // Gera: db.type = "decimal(12,2)"
{ "format": "area" }      // Gera: db.type = "decimal(8,2)"
{ "format": "percent" }   // Gera: db.type = "decimal(5,4)"
```

### Inferência de Índices
```javascript
// POR PADRÃO: TODOS OS CAMPOS SÃO INDEXADOS (db.index = true)
// Apenas explicitamos exceções:

{ "db": { "index": false } }     // Campo SEM índice
{ "db": { "index": "gin" } }      // Índice GIN (arrays/JSONB)
{ "db": { "index": "fulltext" } } // Índice de texto completo
{ "db": { "unique": true } }       // Unique constraint (já inclui index)

// Casos especiais automáticos:
{ "type": "String[]", "searchable": true }  // Gera: db.index = "gin"
{ "type": "Json", "searchable": true }      // Gera: db.index = "gin"
```

### Inferência de Mask Automática
```javascript
// Format + Unit → Mask automático (frontend)
{ "format": "currency", "unit": "BRL" }  // Gera: mask = "currency-brl"
{ "format": "percent" }                  // Gera: mask = "percent"
{ "format": "date" }                     // Gera: mask = "date"
{ "format": "area", "unit": "m2" }       // Gera: mask = "area"

// Override manual quando necessário
{ "format": "currency", "unit": "BRL", "mask": false }  // Desabilita mask
{ "format": "currency", "unit": "BRL", "mask": "custom" }  // Override
```

### Inferência de Validação
```javascript
// Mask explícito (apenas casos específicos)
{ "mask": "cpf" }     // Frontend: formatação + validação visual
{ "mask": "email" }   // Frontend: validação de formato
{ "mask": "cep" }     // Frontend: formatação CEP

// Precision para Zod (backend)
{ "validation": { "precision": 2 } }  // Gera: z.number().multipleOf(0.01)
{ "validation": { "precision": 3 } }  // Gera: z.number().multipleOf(0.001)
```

### Inferência para Geolocalização
```javascript
// Format geo-point + geoSource → configurações espaciais automáticas
{ "format": "geo-point", "db": { "geoSource": ["lat", "lng"] } }  

// Sistema infere baseado no ORM/banco:
// - PostgreSQL/PostGIS: geography(Point, 4326) + índice gist
// - MySQL: POINT + índice spatial
// - MongoDB: 2dsphere index
// - Coluna gerada ou trigger conforme suporte do banco
```

### Cálculo Automático de Decimal
```javascript
// Sistema calcula precisão automaticamente:
validation.max = 999999.99 + precision = 2 → decimal(8,2)
validation.max = 999999999.99 + precision = 2 → decimal(12,2)

// Exemplos práticos:
{ "validation": { "max": 999999.99, "precision": 2 } }     // decimal(8,2)
{ "validation": { "max": 999999999.99, "precision": 2 } }   // decimal(12,2)
{ "validation": { "max": 180.0, "precision": 8 } }          // decimal(11,8) - lat/lng
```

---

## 📖 Biblioteca de Exemplos

### Campos Simples

#### Campo Básico
```javascript
{
  "key": "reference",
  "type": "String", 
  "label": "Referência",
  "origin": "horizon-base/property"
}
```

#### Campo com Validação
```javascript
{
  "key": "email",
  "type": "String",
  "label": "E-mail",
  "mask": "email",
  "validation": {
    "required": true,
    "maxLength": 255
  },
  "origin": "horizon-base/property"
}
```

#### Campo com Formatação e Precision
```javascript
{
  "key": "valor_venda",
  "type": "Number",
  "label": "Valor de Venda",
  "format": "currency",
  "unit": "BRL",
  "categories": ["valores"],
  "validation": {
    "required": true,
    "min": 0,
    "max": 999999999.99,
    "precision": 2
  },
  "origin": "horizon-base/property"
  // Sistema infere automaticamente: 
  // - db.type = "decimal(12,2)", db.index = true
  // - mask = "currency-brl" (frontend)
}
```

### Campos com Enum

```javascript
{
  "key": "operacao",
  "type": "String[]",
  "label": "Operação",
  "enum": {
    "venda": "Venda",
    "aluguel": "Aluguel",
    "temporada": "Temporada"
  },
  "categories": ["comercial"],
  "filterable": true,
  "origin": "horizon-base/property"
}
```

### Campos com Hierarquia

```javascript
{
  "key": "subtipo",
  "type": "String",
  "label": "Subtipo",
  "parent": "tipo",
  "categories": ["estrutura"],
  "filterable": true,
  "origin": "horizon-base/property"
}
```

### Campos com Condições

#### Condição Simples
```javascript
{
  "key": "valor_aluguel",
  "type": "Number",
  "label": "Valor do Aluguel",
  "format": "currency",
  "unit": "BRL",
  "conditions": ["operacao:aluguel"],
  "categories": ["valores"],
  "filterable": true,
  "origin": "horizon-base/property"
}
```

#### Condições Múltiplas
```javascript
{
  "key": "andar",
  "type": "Number",
  "label": "Andar",
  "conditions": [
    "tipo.equals:apartamento",
    "operacao:venda",
    "valor.gte:200000"
  ],
  "categories": ["estrutura"],
  "validation": {
    "min": 1,
    "max": 50
  },
  "origin": "horizon-base/property"
}
```

### Campos com Banco Customizado

#### Campo Único
```javascript
{
  "key": "cpf",
  "type": "String",
  "label": "CPF",
  "mask": "cpf",
  "db": {
    "unique": true
  },
  "validation": {
    "required": true,
    "maxLength": 14
  },
  "origin": "horizon-base/property"
}
```

#### Campo com Fulltext
```javascript
{
  "key": "description",
  "type": "String",
  "label": "Descrição",
  "db": {
    "type": "text",
    "index": "fulltext"
  },
  "categories": ["identificacao"],
  "searchable": true,
  "validation": {
    "required": true
  },
  "origin": "horizon-base/property"
}
```

### Campos Complexos

#### Campo Principal Completo
```javascript
{
  "key": "dormitorios",
  "type": "Number",
  "label": "Dormitórios",
  "icon": "bedroom",
  "categories": ["dependencias", "principais"],
  "format": "count",
  "composedLabel": "{{value}} dormitório{{p:s}}",
  "filterable": true,
  "sortable": true,
  "validation": {
    "min": 0,
    "max": 20
  },
  "origin": "horizon-base/property"
  // Sistema infere: mask = "count" (frontend)
}
```

#### Campo com Index Especial
```javascript
{
  "key": "description",
  "type": "String",
  "label": "Descrição",
  "categories": ["identificacao"],
  "searchable": true,
  "validation": {
    "required": true
  },
  "origin": "horizon-base/property"
  // Sistema infere: db.type = "text", db.index = "fulltext"
}
```

#### Campo Sem Index
```javascript
{
  "key": "endereco_numero",
  "type": "String",
  "label": "Número",
  "categories": ["localizacao"],
  "db": {"index": false},  // Explicitamente SEM índice
  "origin": "horizon-base/property"
}
```

#### Campo com Precision
```javascript
{
  "key": "latitude",
  "type": "Number",
  "label": "Latitude",
  "categories": ["localizacao"],
  "validation": {
    "min": -90.0,
    "max": 90.0,
    "precision": 8
  },
  "origin": "horizon-base/property"
  // Sistema infere: db.type = "decimal(10,8)"
}
```

#### Array com Index GIN
```javascript
{
  "key": "caracteristicas",
  "type": "String[]",
  "label": "Características",
  "categories": ["caracteristicas"],
  "searchable": true,
  "origin": "horizon-base/property"
  // Sistema infere: db.type = "jsonb", db.index = "gin"
}
```

#### Campo de Geolocalização
```javascript
{
  "key": "location",
  "type": "String",
  "label": "Localização",
  "format": "geo-point",
  "db": {
    "geoSource": ["latitude", "longitude"]
  },
  "filterable": true,
  "origin": "horizon-base/property"
  // Sistema infere todo o resto baseado no ORM/banco:
  // - Tipo apropriado (geography, geometry, point, etc)
  // - Índice espacial apropriado
  // - Coluna gerada ou trigger conforme o banco
}
```

---

## ⚠️ Anti-Patterns

### ❌ Redundância
```javascript
// ERRADO - Repetindo informação
{
  "db": { "type": "varchar(255)" },
  "validation": { "maxLength": 255 }
}

// CERTO - Sistema infere
{
  "validation": { "maxLength": 255 }
}
```

### ❌ Format Desnecessário
```javascript
// ERRADO - Type já diz tudo
{
  "type": "String",
  "format": "text"
}

// CERTO - Sem format
{
  "type": "String"
}
```

### ❌ Validação Duplicada
```javascript
// ERRADO - Mask já valida
{
  "mask": "cpf",
  "validation": { "cpf": true }
}

// CERTO - Mask cuida da validação
{
  "mask": "cpf"
}
```

---

## 📏 Guia de Decisão Rápido

### Quando usar cada campo?

| Se você quer... | Use... | Exemplo |
|------------------|---------|----------|
| Formatar exibição | `format` + `unit` | `"format": "currency", "unit": "BRL"` |
| Validar/mascarar | `mask` | `"mask": "cpf"` |
| Múltiplas categorias | `categories` | `"categories": ["valores", "principais"]` |
| Hierarquia pai-filho | `parent` | `"parent": "tipo"` |
| Condições de visibilidade | `conditions` | `"conditions": ["operacao:venda"]` |
| Constraints de banco | `db` | `"db": { "unique": true }` |
| Validação frontend | `validation` | `"validation": { "required": true }` |
| Rastrear origem | `origin` + `modifiedBy` | `"origin": "jetimob/import"` |

### Quantas propriedades usar?
- **Mínimo**: 4 (key, type, label, origin)
- **Média ideal**: 5-7 propriedades
- **Máximo aceitável**: 10-12 (casos muito complexos)

---

## 🎯 Regras de Ouro

1. **Comece simples**: key, type, label, origin
2. **Adicione só quando necessário**: Cada propriedade deve ter propósito claro
3. **Deixe o sistema inferir**: Não especifique o que pode ser automatizado
4. **Separe responsabilidades**: Frontend ≠ Banco
5. **Zero redundância**: Nunca repetir informação
6. **Documentes mudanças**: Use `modifiedBy` para auditoria
7. **🤖 Mask automático**: Quando `format` + `unit` existem, `mask` é inferido automaticamente
8. **📝 Mask explícito**: Use apenas para casos específicos (CPF, CEP, telefone, email, URL)

---

Este formato é a **base definitiva** para todos os schemas do sistema Horizon. Deve ser seguido consistentemente para garantir interoperabilidade, manutenibilidade e escalabilidade do sistema.