# 🔍 Search Schema Specification

## Visão Geral

O **Search Schema** é a especificação que define a estrutura completa de uma interface de busca, incluindo campos, comportamentos, regras condicionais e metadata de UI. Diferente do [Domain Schema](/__domain-data-schema-specification.md) que representa a estrutura do banco de dados, o Search Schema é focado na experiência de busca do usuário.

## 📋 Estrutura Principal

```typescript
interface SearchSchema {
  // Campos principais com metadata completo
  fields: Record<string, FieldMetadata>
  
  // Campos especiais na raiz (não em "root")
  fts?: FtsMetadata           // Full-text search
  geom?: GeomMetadata         // Geometria/localização
  page?: PaginationMetadata   // Paginação
  limit?: LimitMetadata       // Limite de resultados
  sort?: SortMetadata         // Ordenação
  layout?: LayoutMetadata     // Layout da página
  zoom?: ZoomMetadata         // Zoom do mapa
  bbox?: BboxMetadata         // Bounding box
  center?: CenterMetadata     // Centro do mapa
  card?: CardMetadata         // Estilo dos cards
}
```

## 🎯 Field Metadata

Cada campo em `fields` possui metadata completo para UI e comportamento:

```typescript
interface FieldMetadata {
  // === Identificação ===
  key: string                    // Chave do campo no estado
  
  // === UI/Renderização ===
  label: string                  // Label exibido
  type: FieldType               // Tipo do componente
  placeholder?: string           // Placeholder
  icon?: string                  // Ícone do campo
  helperText?: string           // Texto de ajuda
  
  // === Comportamento ===
  operator?: OperatorType        // Operador para arrays/ranges
  multiple?: boolean             // Permite múltiplos valores
  default?: any                  // Valor padrão
  required?: boolean             // Campo obrigatório
  minSelected?: number           // Mínimo de seleções
  maxSelected?: number           // Máximo de seleções
  
  // === Relacionamentos ===
  parent?: string                // Campo pai (hierarquia)
  
  // === Regras Condicionais ===
  when?: string                  // Condição inline para visibilidade
  disabled?: string              // Condição para disabled
  required?: string              // Condição para required
  
  // === Options ===
  options?: FieldOption[]        // Opções estáticas
  optionsSource?: 'static' | 'faceted' | 'api'  // Fonte das options
  optionsProvider?: (state: any) => FieldOption[]  // Provider dinâmico
  
  // === Eventos ===
  onChange?: 'submit' | 'default'  // Comportamento ao mudar
  debounce?: number                 // Delay antes de processar
  
  // === Validação ===
  validation?: ValidationRule[]     // Regras de validação
  min?: number                      // Valor mínimo (ranges)
  max?: number                      // Valor máximo (ranges)
  minLength?: number                // Comprimento mínimo
  maxLength?: number                // Comprimento máximo
  pattern?: string                  // Regex de validação
}
```

### Field Types

```typescript
type FieldType = 
  | 'select'        // Dropdown simples
  | 'multiselect'   // Seleção múltipla
  | 'range'         // Range de valores
  | 'search'        // Campo de texto/busca
  | 'boolean'       // Checkbox/toggle
  | 'date'          // Seletor de data
  | 'daterange'     // Range de datas
  | 'radio'         // Radio buttons
  | 'slider'        // Slider numérico
  | 'tags'          // Input de tags
  | 'autocomplete'  // Autocomplete
```

### Operator Types

```typescript
type OperatorType = 
  | 'and'          // Todos os valores (arrays)
  | 'or'           // Qualquer valor (arrays)
  | 'equals'       // Igualdade exata
  | 'contains'     // Contém texto
  | 'between'      // Entre min e max
  | 'gte'          // Maior ou igual
  | 'lte'          // Menor ou igual
  | 'gt'           // Maior que
  | 'lt'           // Menor que
  | 'in'           // Está em lista
  | 'not'          // Negação
```

## 🔀 Field Options

Options podem ter condições próprias:

```typescript
interface FieldOption {
  value: any                    // Valor da option
  label: string                 // Label exibido
  icon?: string                 // Ícone opcional
  description?: string          // Descrição adicional
  when?: string                 // Condição inline para exibir
  disabled?: string             // Condição para disabled
  group?: string                // Grupo da option
  metadata?: any                // Metadata adicional
}
```

## 📝 Conditions (Regras Inline)

Conditions usam expressões inline simples e legíveis:

### Sintaxe Básica

```javascript
// Igualdade
"operacao === 'venda'"
"tipo !== 'comercial'"

// Comparações
"quartos > 2"
"valor_venda >= 500000"

// Arrays/Contains
"operacao.includes('venda')"
"caracteristicas.length > 0"

// Lógica
"operacao === 'venda' && tipo === 'residencial'"
"quartos > 2 || area_total > 100"

// Existência
"valor_venda"  // verifica se existe/tem valor
"!valor_locacao"  // verifica se NÃO existe

// Múltiplas condições
"(operacao === 'venda' || operacao === 'permuta') && tipo === 'residencial'"
```

### Exemplos de Uso

```typescript
{
  // Campo só aparece para venda
  valor_venda: {
    type: 'range',
    when: "operacao === 'venda'"
  },
  
  // Option só aparece se tem filtro de área
  sort: {
    options: [
      {
        value: 'area_desc',
        label: 'Maior área',
        when: 'area_total > 0'
      }
    ]
  },
  
  // Campo desabilitado condicionalmente
  subtipo: {
    type: 'select',
    disabled: "!tipo"  // disabled se tipo não selecionado
  }
}
```

## 🌐 Root Fields (Campos Especiais)

### FTS (Full-Text Search)

```typescript
interface FtsMetadata {
  placeholder?: string
  operator?: 'websearch' | 'plainto' | 'phrase' | 'boolean'
  debounce?: number
  minLength?: number
  onChange?: 'submit' | 'default'
}
```

### Geom (Geometria/Localização)

```typescript
interface GeomMetadata {
  operation?: 'within' | 'intersects' | 'near'
  defaultZoom?: number
  defaultCenter?: { lat: number, lng: number }
  enableDrawing?: boolean
  enableClustering?: boolean
}
```

### Sort (Ordenação)

```typescript
interface SortMetadata {
  default?: string
  options?: SortOption[]
  onChange?: 'submit' | 'default'
}

interface SortOption {
  value: string
  label: string
  when?: string  // Condição para exibir
}
```

### Page & Limit

```typescript
interface PaginationMetadata {
  default?: number
  min?: number
  max?: number
  onChange?: 'submit' | 'default'
}

interface LimitMetadata {
  default?: number
  options?: number[] | LimitOption[]
  onChange?: 'submit' | 'default'
}
```

### Layout & Card

```typescript
interface LayoutMetadata {
  default?: 'list' | 'map' | 'map-list'
  options?: LayoutOption[]
}

interface CardMetadata {
  default?: 'horizontal' | 'vertical' | 'compact'
  options?: CardOption[]
}
```

## 🎯 Exemplo Completo: Busca de Imóveis

```typescript
const propertySearchSchema: SearchSchema = {
  // === Fields ===
  fields: {
    operacao: {
      key: 'operacao',
      label: 'Operação',
      type: 'select',
      multiple: true,
      operator: 'or',
      default: ['venda'],
      minSelected: 1,
      options: [
        { value: 'venda', label: 'Venda' },
        { value: 'locacao', label: 'Locação' },
        { value: 'permuta', label: 'Permuta' }
      ],
      onChange: 'submit'
    },
    
    tipo: {
      key: 'tipo',
      label: 'Tipo de Imóvel',
      type: 'select',
      placeholder: 'Selecione o tipo',
      options: [
        { value: 'residencial', label: 'Residencial' },
        { 
          value: 'comercial', 
          label: 'Comercial',
          when: "operacao.includes('venda')"  // Comercial só para venda
        }
      ]
    },
    
    subtipo: {
      key: 'subtipo',
      label: 'Subtipo',
      type: 'multiselect',
      parent: 'tipo',  // Relacionado ao tipo
      operator: 'and',
      disabled: "!tipo",  // Disabled até selecionar tipo
      options: [
        { 
          value: 'apartamento', 
          label: 'Apartamento',
          when: "tipo === 'residencial'"
        },
        { 
          value: 'casa', 
          label: 'Casa',
          when: "tipo === 'residencial'"
        },
        { 
          value: 'loja', 
          label: 'Loja',
          when: "tipo === 'comercial'"
        },
        { 
          value: 'sala', 
          label: 'Sala Comercial',
          when: "tipo === 'comercial'"
        }
      ]
    },
    
    endereco_cidade: {
      key: 'endereco_cidade',
      label: 'Cidade',
      type: 'autocomplete',
      placeholder: 'Digite a cidade...',
      optionsSource: 'api',
      onChange: 'submit'
    },
    
    endereco_bairro: {
      key: 'endereco_bairro',
      label: 'Bairros',
      type: 'multiselect',
      parent: 'endereco_cidade',
      operator: 'and',
      disabled: "!endereco_cidade",
      optionsSource: 'faceted',
      placeholder: 'Selecione os bairros'
    },
    
    valor_venda: {
      key: 'valor_venda',
      label: 'Valor de Venda',
      type: 'range',
      operator: 'between',
      when: "operacao.includes('venda')",  // Só aparece para venda
      min: 50000,
      max: 10000000,
      placeholder: {
        min: 'Valor mínimo',
        max: 'Valor máximo'
      }
    },
    
    valor_locacao: {
      key: 'valor_locacao',
      label: 'Valor de Locação',
      type: 'range',
      operator: 'between',
      when: "operacao.includes('locacao')",  // Só aparece para locação
      min: 500,
      max: 50000
    },
    
    quartos: {
      key: 'quartos',
      label: 'Quartos',
      type: 'range',
      operator: 'gte',  // Maior ou igual
      min: 1,
      max: 10,
      default: { min: 2 }
    },
    
    area_total: {
      key: 'area_total',
      label: 'Área Total (m²)',
      type: 'range',
      operator: 'between',
      min: 20,
      max: 1000
    },
    
    caracteristicas: {
      key: 'caracteristicas',
      label: 'Características',
      type: 'multiselect',
      operator: 'and',
      optionsSource: 'faceted',
      options: [
        { value: 'piscina', label: 'Piscina' },
        { value: 'churrasqueira', label: 'Churrasqueira' },
        { value: 'academia', label: 'Academia' },
        { value: 'playground', label: 'Playground' },
        { value: 'salao_festas', label: 'Salão de Festas' },
        { 
          value: 'ar_condicionado', 
          label: 'Ar Condicionado',
          when: "tipo === 'residencial'"
        }
      ]
    }
  },
  
  // === Root Fields ===
  fts: {
    placeholder: 'Buscar por endereço, código ou descrição...',
    operator: 'websearch',
    debounce: 500,
    minLength: 3,
    onChange: 'submit'
  },
  
  geom: {
    operation: 'within',
    enableDrawing: true,
    enableClustering: true,
    defaultZoom: 12
  },
  
  sort: {
    default: 'updated_at_desc',
    onChange: 'submit',
    options: [
      { value: 'updated_at_desc', label: 'Mais recentes' },
      { value: 'updated_at_asc', label: 'Mais antigos' },
      { 
        value: 'valor_venda_asc', 
        label: 'Menor preço',
        when: "valor_venda && operacao.includes('venda')"
      },
      { 
        value: 'valor_venda_desc', 
        label: 'Maior preço',
        when: "valor_venda && operacao.includes('venda')"
      },
      { 
        value: 'valor_locacao_asc', 
        label: 'Menor aluguel',
        when: "valor_locacao && operacao.includes('locacao')"
      },
      { 
        value: 'area_total_desc', 
        label: 'Maior área',
        when: "area_total > 0"
      },
      { 
        value: 'quartos_desc', 
        label: 'Mais quartos',
        when: "quartos >= 1"
      }
    ]
  },
  
  page: {
    default: 1,
    min: 1,
    onChange: 'submit'
  },
  
  limit: {
    default: 20,
    onChange: 'submit',
    options: [
      { value: 10, label: '10 por página' },
      { value: 20, label: '20 por página' },
      { value: 50, label: '50 por página' },
      { value: 100, label: '100 por página' }
    ]
  },
  
  layout: {
    default: 'list',
    options: [
      { value: 'list', label: 'Lista', icon: 'list' },
      { value: 'map', label: 'Mapa', icon: 'map' },
      { value: 'map-list', label: 'Mapa + Lista', icon: 'layout' }
    ]
  },
  
  card: {
    default: 'horizontal',
    options: [
      { value: 'horizontal', label: 'Horizontal' },
      { value: 'vertical', label: 'Vertical' },
      { value: 'compact', label: 'Compacto' }
    ]
  }
}
```

## 🔄 Integração com Search State Enricher

O Search Schema é usado pelo `SearchStateEnricher` para enriquecer o estado da URL com operadores e metadata:

```typescript
// Estado simples da URL
const urlState = {
  filters: {
    operacao: ['venda', 'locacao'],
    tipo: 'apartamento',
    quartos: { min: 2 }
  },
  fts: 'centro',
  page: 2
}

// Enriquecido com base no schema
const enrichedState = {
  filters: {
    operacao: { or: ['venda', 'locacao'] },  // operator: 'or' do schema
    tipo: 'apartamento',
    quartos: { gte: 2 }  // operator: 'gte' do schema
  },
  fts: {
    value: 'centro',
    operator: 'websearch'  // do schema.fts.operator
  },
  page: 2
}
```

## 🎮 Integração com Schema Controller

O `SearchStateSchemaController` usa o schema para:

1. **Avaliar visibilidade** - `when` conditions
2. **Filtrar options** - Options condicionais
3. **Determinar comportamento** - onChange submit/default
4. **Validar campos** - Validation rules
5. **Renderizar componentes** - Baseado no type

```typescript
// Controller processa campo com schema + estado
const controller = new SearchFieldController()
const output = controller.process({
  fieldMetadata: schema.fields.valor_venda,
  searchState: currentState,
  globalConfig: { onChange: 'submit' }
})

// Output
{
  isVisible: true,  // operacao inclui 'venda'
  isDisabled: false,
  availableOptions: [],
  shouldSubmitOnChange: true
}
```

## 📚 Diferenças do Domain Schema

| Aspecto | Domain Schema | Search Schema |
|---------|--------------|---------------|
| **Foco** | Estrutura do banco | Interface de busca |
| **Campos** | Todos do modelo | Seleção para busca |
| **Metadata** | Tipos, validações DB | UI, comportamento, conditions |
| **Uso** | Geração de código, migrations | Renderização de UI, enrichment |
| **Operadores** | SQL/Prisma | Busca/filtros |
| **Conditions** | Constraints DB | Regras de UI |

## 🔧 Configuração Global

O schema pode ser usado com configurações globais:

```typescript
const enricher = new SearchStateEnricher({
  schema: propertySearchSchema,
  options: {
    defaults: {
      arrays: { operator: 'and' },      // Default para todos os arrays
      ranges: { operator: 'between' },  // Default para ranges
      fts: { operator: 'websearch' }    // Default para FTS
    },
    globalConfig: {
      onChange: 'submit',    // Todos os campos submetem ao mudar
      debounce: 300,         // Delay global
      validateOnChange: true // Validar ao mudar
    }
  }
})
```

## 🎯 Best Practices

1. **Use conditions inline** - Mais legível que objetos
2. **Mantenha options simples** - Use facets para listas grandes
3. **Defina defaults úteis** - Melhora UX inicial
4. **Use parent relationships** - Para campos dependentes
5. **Configure onChange wisely** - Balance entre responsividade e performance
6. **Valide no schema** - Não no componente
7. **Use operators corretos** - AND para características, OR para operações
8. **Documente conditions** - Para manutenção futura

## 🚀 Próximos Passos

1. Implementar `SearchStateSchemaController`
2. Criar componentes genéricos por type
3. Integrar com `SearchContext`
4. Adicionar suporte a facets dinâmicos
5. Implementar validação em tempo real
6. Criar builders visuais de schema